使用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>
Spring的JDBC模块在自定义异常处理上也非常灵活,可以选择自己喜欢的方式来实现。