事务的传播机制/required 跟 required new 的使用与区别

1.1 事务的隔离级别

  ISOLATION_READ_UNCOMMITTED(读未提交)

  ISOLATION_READ_COMMITTED(读已提交)

  ISOLATION_REPEATABLE_READ(可重复读)

  ISOLATION_SERIALIZABLE(序列化)    

  ISOLATION_DEFAULT(使用数据库默认隔离级别)

1.2 脏读,不可重复读以及幻读

  脏读:一个事务读取到另一事务未提交的更新新据。

  不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的更新数据。

  幻读:事务1正常查询一次,之后事务2插入满足事务1查询条件的新行,再次用事务1查询,得到多出来的数据。

1.3 事务的传播属性

  PROPAGATION_REQUIRED:  支持当前事务,没有则新建

  PROPAGATION_REQUIRESNEW:  新建事务,如果当前存在事务,把当前事务挂起

  PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行

  PROPAGATION_MANDATORY:支持当前事务,如果当前没有事务,就抛出异常

  PROPAGATION_NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,就把当前事务挂起。也就是说业务方法不需要事务

  PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。也就是说业务方法绝对不能在事务范围内执行

  PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中。 如果没有活动事务, 则按REQUIRED属性执行  

二. 事务的回滚及传播机制总结

  1. 首先要注意的是 spring 的默认回滚,roll-back = “runtimeException”,即 spring 只回滚接收到的运行时异常,对于其他异常则不回滚;

  源码解析:

复制代码
/**
     * Handle a throwable, completing the transaction.
     * We may commit or roll back, depending on the configuration.
     * @param txInfo information about the current transaction
     * @param ex throwable encountered
     */
    protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
        if (txInfo != null && txInfo.hasTransaction()) {
            if (logger.isTraceEnabled()) {
                logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
                        "] after exception: " + ex);
            }
            if (txInfo.transactionAttribute.rollbackOn(ex)) {
                try {
                    txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
                }
                catch (TransactionSystemException ex2) {
                    logger.error("Application exception overridden by rollback exception", ex);
                    ex2.initApplicationException(ex);
                    throw ex2;
                }
...
复制代码

  注意上述有一段是 txInfo.transactionAttribute.rollbackOn(ex) 方法,跟进此方法:

复制代码
/**
     * The default behavior is as with EJB: rollback on unchecked exception.
     * Additionally attempt to rollback on Error.
     * <p>This is consistent with TransactionTemplate's default behavior.
     */
    public boolean rollbackOn(Throwable ex) {
        return (ex instanceof RuntimeException || ex instanceof Error);
    }
复制代码

  所以,默认不修改的话,只回滚 RuntimeException 或者 Error

  2. 对此我们可以针对性的添加需要回滚的策略,或者直接显示设置 roll-back = “Exception”,即回滚所有异常;

  3. 需要注意的是:普通情况下,如果异常被 try-catch 处理了,并为进一步抛出异常,那么由于 spring 并未接受到异常, 所以也不会回滚;

   本周讨论补充知识:

  4. 异常分为 应用层抛出 的异常,以及 数据库 的异常,如果是前者,可以正常应用 spring 的事务传播规则,如果是后者,那么一旦发生,数据库端就已经放弃事务,也就没有回滚一说();

  例: int 10/0 为应用层的异常,可以正常回滚; insert 提交一个已存在的 唯一字段,那么数据库会报错,由于事务本身也是由 spring 告之数据库处理,这种情况下,由于 数据库已经放弃事务,那么 spring 层面也不能进行事务回滚;

  5. 正常情况下不会出现一个接口的 bean 进行嵌套调用,如果有,该情况下,由于代理类不再使用 spring 动态代理中的接口实现的方式,而是采用 cglib 的继承实现方式,同样也会导致传播失效;

  6. 事务的传播机制较为常用的为:required,required-new,support。讨论结果为 required-new 跟 required 相比,使用应较为谨慎,原因:同一事务粒度更细更可控,同时也能避免 嵌套事务 可能导致的复杂情况,当然,具体情况需要根据具体业务来定,比如本次讨论中的项目,由于每一次操作的对象都要及时被可能存在的另一进程知悉,这种情况下需要 required-new 来实时开启事务告之其他进程;

  查询总结过程中发现的其它知识点

  1. 注意的小点,MySQL 中 InnoDB 支持事务,MyIsam 并不支持事务;

  2. spring 已经提供了异常处理机制,其基类为 DataAccessException ,它是 RuntimeExcption 的一个子类,据此可以知道,所有从 数据库返回的异常,都会被 spring 处理后 归属于 RuntimeException 中,其体系如下:

  

2. 在 org.springframework.jdbc.support包下有sql-error-codes.xml文件,里面预定义了一些错误代码和信息,其 bean 为 HSQL,在 第8行,我们可以发现重复插入的 code 为 -104;

复制代码
  bean id="HSQL" class="org.springframework.jdbc.support.SQLErrorCodes"    
        property name="databaseProductName"     
            valueHSQL 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     
复制代码

  3. 此外,我们可以知道发散性的看,我们完全可以自定义数据库异常的信息,方法如下:

  3.1 重新新建一个sql-error-codes.xml代码,并将它放到类路径的根目录下,这样Spring会发现它并使用我们自定义的文件。同时HSQL的bean的名称不要改,并将useSqlStateForTranslation置为false,就可以使用我们自己定义的异常类;

复制代码
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 
复制代码

  3.2 可以看到结果,控制台所打印的异常类型为我们自己定义的异常类型;

  3.3 除此之外,还可以实现SQLExceptionTranslator接口,并在JDBC模板中注入其实例来实现异常控制

复制代码
   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. } 
复制代码

  3.4 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 
复制代码

  3.5 测试结果

 

三. 总结

  其实之前对事务的理解是比较基础的,经过周六的交流和学习,才对事务有了略深一层的理解,但同时也愈发觉得事务的博大精深应远不止于此,本文大部分都是结合各位

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值