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。

  • 相关阅读:
    Sqlserver 实际开发中表变量的用法
    Python Day 20 面向对象 (面向对象的组合用法,面向对象的三大特性
    Python Day 19 面向对象(初识面向对象)
    Python Day 18 常用模块(模块和包)
    Python Day 17 常用模块(常用模块一 时间模块,random模块,os模块,sys模块,序列化模块)
    Python Day 15 函数(递归函数、二分查找算法)
    Python Day 14 函数(内置函数,匿名函数(lambda表达式))
    Python Day 13 函数(迭代器,生成器,列表推导式,生成器表达式)
    Python Day 11 + Python Day 12 函数(函数名的应用,闭包,装饰器)
    Python Day 10 函数(名称空间,作用域,作用域链,加载顺序等; 函数的嵌套 global,nonlocal)
  • 原文地址:https://www.cnblogs.com/cczhoufeng/p/2918584.html
Copyright © 2011-2022 走看看