Spring访问数据库异常的处理方法    
使用JDBC API时,很多操作都要声明抛出java.sql.SQLException异常,通常情况下是要制定异常处理策略。而Spring的JDBC模块为我们提供了一套异常处理机制,这套异常系统的基类是DataAccessException,它是RuntimeException的一种类型,那么就不用强制去捕捉异常了,Spring的异常体系如下:
 
目前为止我们还没有明确地处理Spring中JDBC模块的异常。要理解它的异常处理机制,我们来做几个测试。看下面的测试代码:
  1. 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); 
        }
  2. 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语句,不使用自增主键的特性,并在这里设置重复的主键,那么运行程序,就会报出字段重复的异常。下面来捕捉这个异常: 

  1. try {      
  2.     vehicleDAO.insert(vehicle);      
  3. catch (DataAccessException e) {      
  4.     SQLException sqle = (SQLException) e.getCause();      
  5.     System.out.println("Error code: " + sqle.getErrorCode());      
  6.     System.out.println("SQL state: " + sqle.getSQLState());      
  7. }     
  8.         try {  
  9.             vehicleDAO.insert(vehicle);  
  10.         } catch (DataAccessException e) {  
  11.             SQLException sqle = (SQLException) e.getCause();  
  12.             System.out.println("Error code: " + sqle.getErrorCode());  
  13.             System.out.println("SQL state: " + sqle.getSQLState());  
  14.         } 

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

 


 
 

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

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

  1. <bean id="HSQL" class="org.springframework.jdbc.support.SQLErrorCodes">    
  2.     <property name="databaseProductName">     
  3.         <value>HSQL Database Engine</value>     
  4.     </property>     
  5.     <property name="badSqlGrammarCodes">     
  6.         <value>-22,-28</value>     
  7.     </property>     
  8.     <property name="duplicateKeyCodes">     
  9.         <value>-104</value>     
  10.     </property>     
  11.     <property name="dataIntegrityViolationCodes">     
  12.         <value>-9</value>     
  13.     </property>     
  14.     <property name="dataAccessResourceFailureCodes">     
  15.         <value>-80</value>     
  16.     </property>     
  17. </bean>     
  18.     <bean id="HSQL" class="org.springframework.jdbc.support.SQLErrorCodes"> 
  19.         <property name="databaseProductName"> 
  20.             <value>HSQL Database Engine</value> 
  21.         </property> 
  22.         <property name="badSqlGrammarCodes"> 
  23.             <value>-22,-28</value> 
  24.         </property> 
  25.         <property name="duplicateKeyCodes"> 
  26.             <value>-104</value> 
  27.         </property> 
  28.         <property name="dataIntegrityViolationCodes"> 
  29.             <value>-9</value> 
  30.         </property> 
  31.         <property name="dataAccessResourceFailureCodes"> 
  32.             <value>-80</value> 
  33.         </property> 
  34.     </bean> 

其余数据库的错误码内容也可以从这个

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

 

  1. package org.ourpioneer.vehicle.exception;      
  2. import org.springframework.dao.DataIntegrityViolationException;      
  3. public class VehicleDuplicateKeyException extends     
  4.         DataIntegrityViolationException {      
  5.     public VehicleDuplicateKeyException(String msg) {      
  6.         super(msg);      
  7.     }      
  8.     public VehicleDuplicateKeyException(String msg, Throwable cause) {      
  9.         super(msg, cause);      
  10.     }      
  11. }     
  12. package org.ourpioneer.vehicle.exception;  
  13. import org.springframework.dao.DataIntegrityViolationException;  
  14. public class VehicleDuplicateKeyException extends  
  15.         DataIntegrityViolationException {  
  16.     public VehicleDuplicateKeyException(String msg) {  
  17.         super(msg);  
  18.     }  
  19.     public VehicleDuplicateKeyException(String msg, Throwable cause) {  
  20.         super(msg, cause);  
  21.     }  

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

  1. <bean id="HSQL" class="org.springframework.jdbc.support.SQLErrorCodes">     
  2.         <property name="databaseProductName" value="HSQL Database Engine" />     
  3.         <property name="useSqlStateForTranslation" value="false" />     
  4.         <property name="customTranslations">     
  5.             <list>     
  6.                 <ref local="vehicleDuplicateKeyTranslation" />     
  7.             </list>     
  8.         </property>     
  9.     </bean>     
  10.     <bean id="vehicleDuplicateKeyTranslation"     
  11.     class="org.springframework.jdbc.support.CustomSQLErrorCodesTranslation">     
  12.         <property name="errorCodes" value="-104" />     
  13.         <property name="exceptionClass"     
  14.     value="org.ourpioneer.vehicle.exception.VehicleDuplicateKeyException" />     
  15.     </bean>     
  16. <bean id="HSQL" class="org.springframework.jdbc.support.SQLErrorCodes"> 
  17.         <property name="databaseProductName" value="HSQL Database Engine" /> 
  18.         <property name="useSqlStateForTranslation" value="false" /> 
  19.         <property name="customTranslations"> 
  20.             <list> 
  21.                 <ref local="vehicleDuplicateKeyTranslation" /> 
  22.             </list> 
  23.         </property> 
  24.     </bean> 
  25.     <bean id="vehicleDuplicateKeyTranslation" 
  26.     class="org.springframework.jdbc.support.CustomSQLErrorCodesTranslation"> 
  27.         <property name="errorCodes" value="-104" /> 
  28.         <property name="exceptionClass" 
  29.     value="org.ourpioneer.vehicle.exception.VehicleDuplicateKeyException" /> 
  30.     </bean> 

Spring的JDBC模块在自定义异常处理上也非常灵活,可以选择自己喜欢的方式来实现。

关联文档