Spring访问数据库异常的处理方法

  使用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 . }
   
   
   
   
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 . }
22 . }

  之后我们重新新建一个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

  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 . }
46 . }

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

   
   
   
   
1 . bean id = " vehicleDuplicateKeyTranslator "
2 . class = " org.ourpioneer.vehicle.exception.VehicleDuplicateKeyTranslator " / bean
3 . bean id = " jdbcTemplate " class = " org.springframework.jdbc.core.JdbcTemplate "
4 . property name = " exceptionTranslator " ref = " vehicleDuplicateKeyTranslator " /
5 . property name = " dataSource " ref = " dataSource " /
6 . / bean
7 . bean id = " vehicleDAO " class = " org.ourpioneer.vehicle.dao.VehicleDAOImpl "
8 . property name = " jdbcTemplate " ref = " jdbcTemplate " /
9 . / bean
10 . bean id = " vehicleDuplicateKeyTranslator "
11 . class = " org.ourpioneer.vehicle.exception.VehicleDuplicateKeyTranslator " / bean
12 . bean id = " jdbcTemplate " class = " org.springframework.jdbc.core.JdbcTemplate "
13 . property name = " exceptionTranslator " ref = " vehicleDuplicateKeyTranslator " /
14 . property name = " dataSource " ref = " dataSource " /
15 . / bean
16 . bean id = " vehicleDAO " class = " org.ourpioneer.vehicle.dao.VehicleDAOImpl "
17 . property name = " jdbcTemplate " ref = " jdbcTemplate " /
18 . / bean

  调整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 . … …
16 . }

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

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

你可能感兴趣的:(Spring,Java,数据库,Web)