zoukankan      html  css  js  c++  java
  • Spring访问数据库异常的处理方法(转) 东师理想

    原文链接:http://sarin.javaeye.com/blog/888458

    今天我们将谈谈Spring访问数据库异常的处理方法,使用JDBC
    API时,很多操作都要声明抛出java.sql.SQLException异常,通常情况下是要制定异常处理策略。

    使用JDBC API时,很多操作都要声明抛出java.sql.SQLException异常,通常情况下是要制定异常处理策略。而Spring的JDBC模块为我们提供了一套异常处理机制,这套异常系统的基类是DataAccessException,它是RuntimeException的一种类型,那么就不用强制去捕捉异常了,Spring的异常体系如下:

    目前为止我们还没有明确地处理Spring中JDBC模块的异常。要理解它的异常处理机制,我们来做几个测试。看下面的测试代码:

    public void insert(final Vehicle vehicle) {      
      String sql = "insert into vehicle  (ID,PLATE,CHASSIS,COLOR,WHEEL,SEAT) values  (:id,:plate,:chassis,:color,:wheel,:seat)";
      SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(vehicle);
      getSimpleJdbcTemplate().update(sql, parameterSource); }
    public void insert(final Vehicle vehicle) { String sql = "insert into vehicle(ID,PLATE,CHASSIS,COLOR,WHEEL,SEAT) values(:id,:plate,:chassis,:color,:wheel,:seat)"; SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(vehicle); getSimpleJdbcTemplate().update(sql, parameterSource); }
    public static void main(String[] args) {               
            ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:org/ourpioneer/vehicle/spring/applicationContext.xml");
            VehicleDAO vehicleDAO = (VehicleDAO) ctx.getBean("vehicleDAO");
            Vehicle vehicle = new Vehicle("辽B-000000", "1A00000001", "RED", 4, 4);
            vehicle.setId(1);
            vehicleDAO.insert(vehicle);
            }         
        public static void main(String[] args) {          
            ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:org/ourpioneer/vehicle/spring/applicationContext.xml");
            VehicleDAO vehicleDAO = (VehicleDAO) ctx.getBean("vehicleDAO");
            Vehicle vehicle = new Vehicle("辽B-000000", "1A00000001", "RED", 4, 4);
            vehicle.setId(1);
            vehicleDAO.insert(vehicle);
            } 

    修改SQL语句,不使用自增主键的特性,并在这里设置重复的主键,那么运行程序,就会报出字段重复的异常。下面来捕捉这个异常:

    try {
            vehicleDAO.insert(vehicle);
            } catch (DataAccessException e) {
                SQLException sqle = (SQLException) e.getCause();
                System.out.println("Error code: " + sqle.getErrorCode());
                System.out.println("SQL state: " + sqle.getSQLState());
                }             
        try {              
            vehicleDAO.insert(vehicle);          
            } catch (DataAccessException e) {              
                SQLException sqle = (SQLException) e.getCause();              
                System.out.println("Error code: " + sqle.getErrorCode());              
                System.out.println("SQL state: " + sqle.getSQLState());          
            } 

    此时,我们就可以获得错误码和SQL状态(不同的数据库系统会有不同):

    关于HSQL数据库的错误码可以到org.hsqldb.Trace类中查看,只要注意运行结果会有一个负号,而类中定义的是没有负号的。这样就知道了这个错误的具体含义,比如104:唯一约束验证失败。这就是我们故意设置的重复主键问题。

    Spring的JDBC模块为我们预定义了一些错误代码,它存储在org.springframework.jdbc.support包下的sql-error-codes.xml文件中,其中描述HSQL的内容为:

    <bean id="HSQL" class="org.springframework.jdbc.support.SQLErrorCodes">        
            <property name="databaseProductName">             
                <value>HSQL Database Engine</value>         
            </property>         
            <property name="badSqlGrammarCodes">             
                <value>-22,-28</value>         
            </property>         
            <property name="duplicateKeyCodes">             
                <value>-104</value>         
            </property>         
            <property name="dataIntegrityViolationCodes">             
                <value>-9</value>         
            </property>         
            <property name="dataAccessResourceFailureCodes">             
                <value>-80</value>         
            </property>     
            </bean>         
            <bean id="HSQL" class="org.springframework.jdbc.support.SQLErrorCodes">         
            <property name="databaseProductName">             
                <value>HSQL Database Engine</value>         
            </property>         
            <property name="badSqlGrammarCodes">             
                <value>-22,-28</value>         
            </property>         
            <property name="duplicateKeyCodes">             
                <value>-104</value>         
            </property>         
            <property name="dataIntegrityViolationCodes">             
                <value>-9</value>         
            </property>         
            <property name="dataAccessResourceFailureCodes">             
                <value>-80</value>         
            </property>     
        </bean> 

    其余数据库的错误码内容也可以从这个文件之中获得。下面我们来看看如何自定义异常处理。上面我们已经知道在org.springframework.jdbc.support包下有sql-error-codes.xml文件,在Spring启动时会自动读取这个文件中的错误码,它为我们预分类了一些错误码,而我们可以加强它,来使用我们自定义的异常。首先,定义一个异常类,我们就来自定义一下前面的-104错误,就是HSQL的重复键的问题:

    package org.ourpioneer.vehicle.exception;      
        import org.springframework.dao.DataIntegrityViolationException;      
        public class VehicleDuplicateKeyException extends DataIntegrityViolationException {          
            public VehicleDuplicateKeyException(String msg) {              
                super(msg);          
            }          
            public VehicleDuplicateKeyException(String msg, Throwable cause) {              
                super(msg, cause);          
            }      
        }     
        package org.ourpioneer.vehicle.exception;  
        import org.springframework.dao.DataIntegrityViolationException;  
        public class VehicleDuplicateKeyException extends DataIntegrityViolationException {      
            public VehicleDuplicateKeyException(String msg) {          
                super(msg);      
            }      
            public VehicleDuplicateKeyException(String msg, Throwable cause) {          
                super(msg, cause);      
            }  
        } 


    之后我们重新新建一个sql-error-codes.xml代码,并将它放到类路径的根目录下,这样Spring会发现它并使用我们自定义的文件,在配置中定义如下:

    <bean id="HSQL" class="org.springframework.jdbc.support.SQLErrorCodes">             
            <property name="databaseProductName" value="HSQL Database Engine" />             
            <property name="useSqlStateForTranslation" value="false" />             
            <property name="customTranslations">                 
            <list>                     
                <ref local="vehicleDuplicateKeyTranslation" />                 
            </list>             
            </property>         
        </bean>         
        
        <bean id="vehicleDuplicateKeyTranslation" class="org.springframework.jdbc.support.CustomSQLErrorCodesTranslation">             
            <property name="errorCodes" value="-104" />             
            <property name="exceptionClass" value="org.ourpioneer.vehicle.exception.VehicleDuplicateKeyException" />         
        </bean>     
        
        <bean id="HSQL" class="org.springframework.jdbc.support.SQLErrorCodes">         
            <property name="databaseProductName" value="HSQL Database Engine" />         
            <property name="useSqlStateForTranslation" value="false" />         
            <property name="customTranslations">             
            <list>                 
                <ref local="vehicleDuplicateKeyTranslation" />             
            </list>         
            </property>     
        </bean>     
        
        <bean id="vehicleDuplicateKeyTranslation" class="org.springframework.jdbc.support.CustomSQLErrorCodesTranslation">         
            <property name="errorCodes" value="-104" />         
            <property name="exceptionClass" value="org.ourpioneer.vehicle.exception.VehicleDuplicateKeyException" />     
        </bean> 

    HSQL的bean的名称不要改,并将useSqlStateForTranslation置为false,就可以使用我们自己定义的异常类了。在主函数中移除try/catch块,启动程序,我们就可以看到如下内容:

    从启动信息中可以发现Spring发现了我们自定义的sql-error-codes.xml,并替换其中的HSQL数据库处理部分,使用了我们定义的异常,模拟出主键重复的异常后,VehicleDuplicateKeyException就抛出了。除此之外,还可以实现SQLExceptionTranslator接口,并在JDBC模板中注入其实例来实现异常控制,我们来看一下,首先创建一个Translator类:

    package org.ourpioneer.vehicle.exception;      
        import java.sql.SQLException;      
        import org.springframework.dao.DataAccessException;      
        import org.springframework.jdbc.UncategorizedSQLException;      
        import org.springframework.jdbc.support.SQLExceptionTranslator;      
        public class VehicleDuplicateKeyTranslator implements SQLExceptionTranslator {          
            public DataAccessException translate(String task, String sql, SQLException ex) {              
                if (task == null) {                  
                    task = "";
                }              
                if (sql == null) {              
                    
                }              
                if (ex.getErrorCode() == -104) {                  
                    return new VehicleDuplicateKeyException(buildMessage(task, sql, ex));              
                } else {                  
                    return new UncategorizedSQLException(task, sql, ex);              
                    }          
            }          
            private String buildMessage(String task, String sql, SQLException ex) {              
                return "数据库操作异常:" + task + "; SQL [" + sql + "]; " + ex.getMessage();          
            }      
        }     
    package org.ourpioneer.vehicle.exception;  
        import java.sql.SQLException;  
        import org.springframework.dao.DataAccessException;  
        import org.springframework.jdbc.UncategorizedSQLException;  
        import org.springframework.jdbc.support.SQLExceptionTranslator;  
        public class VehicleDuplicateKeyTranslator implements SQLExceptionTranslator {      
            public DataAccessException translate(String task, String sql, SQLException ex) {          
                if (task == null) {              
                    task = "";          
                }          
                if (sql == null) {          
                    
                }          
                if (ex.getErrorCode() == -104) {              
                    return new VehicleDuplicateKeyException(buildMessage(task, sql, ex));          
                } else {              
                    return new UncategorizedSQLException(task, sql, ex);          
                    }      
            }      
            
            private String buildMessage(String task, String sql, SQLException ex) {          
                return "数据库操作异常:" + task + "; SQL [" + sql + "]; " + ex.getMessage();      
            }  
        } 


    其中,要覆盖translate方法,方法有三个参数,task表示当前操作要进行的任务是什么,sql就是执行的sql语句,ex表示SQLException,我们可以从中获取异常信息,其处理代码仅仅捕捉了错误码为-104(HSQL数据库)的错误,其余的配置信息可以根据需要来自行添加。之后要在Spring中重新配置它们:

    <bean id="vehicleDuplicateKeyTranslator" class="org.ourpioneer.vehicle.exception.VehicleDuplicateKeyTranslator">
        </bean> 
        
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">         
            <property name="exceptionTranslator" ref="vehicleDuplicateKeyTranslator" />         
            <property name="dataSource" ref="dataSource" />     
        </bean>   
        
        <bean id="vehicleDAO" class="org.ourpioneer.vehicle.dao.VehicleDAOImpl">         
            <property name="jdbcTemplate" ref="jdbcTemplate" />     
        </bean>  
        
        <bean id="vehicleDuplicateKeyTranslator" class="org.ourpioneer.vehicle.exception.VehicleDuplicateKeyTranslator">
        </bean>   
        
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">         
            <property name="exceptionTranslator" ref="vehicleDuplicateKeyTranslator" />         
            <property name="dataSource" ref="dataSource" />     
        </bean>     
        
        <bean id="vehicleDAO" class="org.ourpioneer.vehicle.dao.VehicleDAOImpl">         
            <property name="jdbcTemplate" ref="jdbcTemplate" />     
        </bean> 

    调整DAO实现类的代码:

    public class VehicleDAOImpl extends SimpleJdbcDaoSupport implements VehicleDAO {          
            …   …          
            public void insert(final Vehicle vehicle) {              
                String sql = "insert into vehicle(ID,PLATE,CHASSIS,COLOR,WHEEL,SEAT) values(?,?,?,?,?,?)";              
                getJdbcTemplate().update(sql, vehicle.getId(),vehicle.getPlate(),vehicle.getChassis(),vehicle.getColor(),vehicle.getWheel(),vehicle.getSeat());          
            }      
            …   …      
        }     
        
        public class VehicleDAOImpl extends SimpleJdbcDaoSupport implements VehicleDAO {      
            …   …      
            public void insert(final Vehicle vehicle) {          
                String sql = "insert into vehicle(ID,PLATE,CHASSIS,COLOR,WHEEL,SEAT) values(?,?,?,?,?,?)";          
                getJdbcTemplate().update(sql, vehicle.getId(),vehicle.getPlate(),vehicle.getChassis(),vehicle.getColor(),vehicle.getWheel(),vehicle.getSeat());      
            }  
            …   …  
        } 

    为了进行测试,其它代码可不用修改,这样继续运行测试程序,同时将sql-error-codes.xml文件从类路径的根路径下去除,就可以得到如下结果:

    Spring的JDBC模块在自定义异常处理上也非常灵活,可以选择自己喜欢的方式来实现。希望对使用者有用,欢迎交流,下一部分开始介绍Spring的ORM。

  • 相关阅读:
    Thymeleaf+Spring使用自己的工具类
    bootstrap 响应式布局
    bootstrap 流布局
    bootstrap 布局
    bootstrap 新建网页
    quick 定时更新函数
    acm hdoj 1157
    acm hdoj 今年暑假不ac
    quick removeTileMaptile
    quick schedule 的添加和移除
  • 原文地址:https://www.cnblogs.com/cczhoufeng/p/2918584.html
Copyright © 2011-2022 走看看