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

来源:互联网 发布:音效管理器软件下载 编辑:程序博客网 时间:2024/06/17 13:31

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 测试结果

 

三. 总结

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

原创粉丝点击