Spring访问数据库异常的处理方法
创始人
2024-07-20 14:01:31
0

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

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

  1.  public void insert(final Vehicle vehicle) {      
  2. String sql = "insert into vehicle  
  3. (ID,PLATE,CHASSIS,COLOR,WHEEL,SEAT) values  
  4. (:id,:plate,:chassis,:color,:wheel,:seat)";      
  5.     SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(      
  6.             vehicle);      
  7.     getSimpleJdbcTemplate().update(sql, parameterSource);      
  8. }     
  9.     public void insert(final Vehicle vehicle) {  
  10.         String sql = "insert into vehicle(ID,PLATE,CHASSIS,COLOR,WHEEL,SEAT) 
  11. values(:id,:plate,:chassis,:color,:wheel,:seat)";  
  12.         SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(  
  13.                 vehicle);  
  14.         getSimpleJdbcTemplate().update(sql, parameterSource);  
  15.     } 

  1.      public static void main(String[] args) {      
  2.  
  3.         ApplicationContext ctx = new ClassPathXmlApplicationContext(       
  4.  "classpath:org/ourpioneer/vehicle/spring/applicationContext.xml");      
  5.         VehicleDAO vehicleDAO = (VehicleDAO) ctx.getBean("vehicleDAO");      
  6.         Vehicle vehicle = new Vehicle("辽B-000000", "1A00000001", "RED", 4, 4);      
  7.         vehicle.setId(1);      
  8.             vehicleDAO.insert(vehicle);      
  9. }     
  10.     public static void main(String[] args) {  
  11.         ApplicationContext ctx = new ClassPathXmlApplicationContext(       
  12.  "classpath:org/ourpioneer/vehicle/spring/applicationContext.xml");  
  13.         VehicleDAO vehicleDAO = (VehicleDAO) ctx.getBean("vehicleDAO");  
  14.         Vehicle vehicle = new Vehicle("辽B-000000", "1A00000001", "RED", 4, 4);  
  15.         vehicle.setId(1);  
  16.             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.     
  2.          
  3.         HSQL Database Engine     
  4.          
  5.          
  6.         -22,-28     
  7.          
  8.          
  9.         -104     
  10.          
  11.          
  12.         -9     
  13.          
  14.          
  15.         -80     
  16.          
  17.      
  18.      
  19.          
  20.             HSQL Database Engine 
  21.          
  22.          
  23.             -22,-28 
  24.          
  25.          
  26.             -104 
  27.          
  28.          
  29.             -9 
  30.          
  31.          
  32.             -80 
  33.          
  34.      

其余数据库的错误码内容也可以从这个文件之中获得。下面我们来看看如何自定义异常处理。上面我们已经知道在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.      
  2.              
  3.              
  4.              
  5.                  
  6.                      
  7.                  
  8.              
  9.          
  10.     
  11.     class="org.springframework.jdbc.support.CustomSQLErrorCodesTranslation">     
  12.              
  13.         
  14.     value="org.ourpioneer.vehicle.exception.VehicleDuplicateKeyException" />     
  15.          
  16.  
  17.          
  18.          
  19.          
  20.              
  21.                  
  22.              
  23.          
  24.      
  25.     
  26.     class="org.springframework.jdbc.support.CustomSQLErrorCodesTranslation"> 
  27.          
  28.         
  29.     value="org.ourpioneer.vehicle.exception.VehicleDuplicateKeyException" /> 
  30.      

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

 

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

  1. package org.ourpioneer.vehicle.exception;      
  2. import java.sql.SQLException;      
  3. import org.springframework.dao.DataAccessException;      
  4. import org.springframework.jdbc.UncategorizedSQLException;      
  5. import org.springframework.jdbc.support.SQLExceptionTranslator;      
  6. public class VehicleDuplicateKeyTranslator implements SQLExceptionTranslator {      
  7.     public DataAccessException translate(String task, String sql,      
  8.             SQLException ex) {      
  9.         if (task == null) {      
  10.             task = "";      
  11.         }      
  12.         if (sql == null) {      
  13.         }      
  14.         if (ex.getErrorCode() == -104) {      
  15.             return new VehicleDuplicateKeyException(buildMessage(task, sql, ex));      
  16.         } else {      
  17.             return new UncategorizedSQLException(task, sql, ex);      
  18.         }      
  19.     }      
  20.     private String buildMessage(String task, String sql, SQLException ex) {      
  21.         return "数据库操作异常:" + task + "; SQL [" + sql + "]; " + ex.getMessage();      
  22.     }      
  23. }     
  24. package org.ourpioneer.vehicle.exception;  
  25. import java.sql.SQLException;  
  26. import org.springframework.dao.DataAccessException;  
  27. import org.springframework.jdbc.UncategorizedSQLException;  
  28. import org.springframework.jdbc.support.SQLExceptionTranslator;  
  29. public class VehicleDuplicateKeyTranslator implements SQLExceptionTranslator {  
  30.     public DataAccessException translate(String task, String sql,  
  31.             SQLException ex) {  
  32.         if (task == null) {  
  33.             task = "";  
  34.         }  
  35.         if (sql == null) {  
  36.         }  
  37.         if (ex.getErrorCode() == -104) {  
  38.             return new VehicleDuplicateKeyException(buildMessage(task, sql, ex));  
  39.         } else {  
  40.             return new UncategorizedSQLException(task, sql, ex);  
  41.         }  
  42.     }  
  43.     private String buildMessage(String task, String sql, SQLException ex) {  
  44.         return "数据库操作异常:" + task + "; SQL [" + sql + "]; " + ex.getMessage();  
  45.     }  

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

  1. class="org.ourpioneer.vehicle.exception.VehicleDuplicateKeyTranslator">     
  2.      
  3.          
  4.          
  5.      
  6.      
  7.          
  8.      
  9.     
  10.     class="org.ourpioneer.vehicle.exception.VehicleDuplicateKeyTranslator"> 
  11.      
  12.          
  13.          
  14.      
  15.      
  16.          
  17.      

调整DAO实现类的代码: 

  1. public class VehicleDAOImpl extends SimpleJdbcDaoSupport implements VehicleDAO {      
  2.     …   …      
  3.     public void insert(final Vehicle vehicle) {      
  4.         String sql = "insert into vehicle(ID,PLATE,CHASSIS,COLOR,WHEEL,SEAT) values(?,?,?,?,?,?)";      
  5.         getJdbcTemplate().update(sql, vehicle.getId(),vehicle.getPlate(),vehicle.getChassis(),vehicle.getColor(),vehicle.getWheel(),vehicle.getSeat());      
  6.     }      
  7. …   …      
  8. }     
  9. public class VehicleDAOImpl extends SimpleJdbcDaoSupport implements VehicleDAO {  
  10.     …   …  
  11.     public void insert(final Vehicle vehicle) {  
  12.         String sql = "insert into vehicle(ID,PLATE,CHASSIS,COLOR,WHEEL,SEAT) values(?,?,?,?,?,?)";  
  13.         getJdbcTemplate().update(sql, vehicle.getId(),vehicle.getPlate(),vehicle.getChassis(),vehicle.getColor(),vehicle.getWheel(),vehicle.getSeat());  
  14.     }  
  15. …   …  

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

 

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

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

【编辑推荐】

  1. Java持久化框架 DataNucleus 2.1发布
  2. 浅谈Spring框架中的JDBC应用
  3. Spring框架的7个模块
  4. 详细介绍Spring框架
  5. 将Flex与Spring框架集成

 

相关内容

热门资讯

如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
施耐德电气数据中心整体解决方案... 近日,全球能效管理专家施耐德电气正式启动大型体验活动“能效中国行——2012卡车巡展”,作为该活动的...
20个非常棒的扁平设计免费资源 Apple设备的平面图标PSD免费平板UI 平板UI套件24平图标Freen平板UI套件PSD径向平...
德国电信门户网站可实时显示全球... 德国电信周三推出一个门户网站,直观地实时提供其安装在全球各地的传感器网络检测到的网络攻击状况。该网站...
为啥国人偏爱 Mybatis,... 关于 SQL 和 ORM 的争论,永远都不会终止,我也一直在思考这个问题。昨天又跟群里的小伙伴进行...
《非诚勿扰》红人闫凤娇被曝厕所... 【51CTO.com 综合消息360安全专家提醒说,“闫凤娇”、“非诚勿扰”已经被黑客盯上成为了“木...
2012年第四季度互联网状况报... [[71653]]  北京时间4月25日消息,据国外媒体报道,全球知名的云平台公司Akamai Te...