spring事务总结

解决三大问题

1.1繁杂的事务管理API

Spring事物的核心类是 PlatformTransactionManager,叫做事务管理器,定义如下

public interface PlatformTransactionManager extends TransactionManager {
    // 获取事务(新的事务或者已经存在的事务)
    TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
   throws TransactionException;   
    // 提交事务
    void commit(TransactionStatus status) throws TransactionException;
    // 回滚事务
    void rollback(TransactionStatus status) throws TransactionException;
}

getTransaction通过TransactionDefinition获得TransactionStatus 对象。在TransactionDefinition 中包含了事物的四中属性:

  1. 事物的隔离级别
  2. 事物的传播行为
  3. 是否只读
  4. 超时时间

TransactionStatus封装事物的对象,提供操作事务,查看事物状态的方法

  1. setRollbackOnly:标记事务为Rollback_only,使其回滚
  2. isRollbackOnly:查看是否被标记为RollbackOnly
  3. isCompleted:查看事物是否已经完成,提交或者回滚

TransactionStatus事务对象可被传入到commit方法或rollback方法中,完成事务的提交或回滚。

在commit方法中,使用TransactionStatus来判断事物的状态从而决定事物是提交还是回滚

public final void commit(TransactionStatus status) throws TransactionException {
    // 1.检查事务是否已完成
    if (status.isCompleted()) {
        throw new IllegalTransactionStateException(
            "Transaction is already completed - do not call commit or rollback more than once per transaction");
    }

    // 2.检查事务是否需要回滚(局部事务回滚)
    DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
    if (defStatus.isLocalRollbackOnly()) {
        if (defStatus.isDebug()) {
            logger.debug("Transactional code has requested rollback");
        }
        processRollback(defStatus, false);
        return;
    }

    // 3.检查事务是否需要回滚(全局事务回滚)
    if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
        if (defStatus.isDebug()) {
            logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
        }
        processRollback(defStatus, true);
        return;
    }
    
    // 4.提交事务
    processCommit(defStatus);
}

小结:Spring事务通过PlatformTransactionManager、TransactionDefinition和TransactionStatus接口统一事务管理API,并结合策略模式和模板方法决定具体实现。

1.2大量的异常处理代码

spring通过异常转换,将检查异常转换成非检查异常,让我们自行决定是否捕获异常
如下:

protected void doCommit(DefaultTransactionStatus status) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
    Connection con = txObject.getConnectionHolder().getConnection();
    if (status.isDebug()) {
        logger.debug("Committing JDBC transaction on Connection [" + con + "]");
    }
    try {
        con.commit();
    }
    catch (SQLException ex) {
        // 异常转换
        throw new TransactionSystemException("Could not commit JDBC transaction", ex);
    }
}

TransactionSystemException为非检查异常

1.3 业务处理代码与事务管理代码混杂

spring通过aop实现声明式事务,使用@Transaction注解为方法提供事物的支持

事物的失效情况

2.1 非public方法失效

@Transactional只有标注在public级别的方法上才能生效,对于非public方法将不会生效。因为springAOP不支持对private方法和protect方法进行拦截

2.2 自调用失效

public void saveAB(A a, B b)
{
    saveA(a);
    saveB(b);
}

@Transactional
public void saveA(A a)
{
    dao.saveA(a);
}

@Transactional
public void saveB(B b)
{
    dao.saveB(b);
}

在saveAB中调用saveA和saveB方法,两者的@Transactional都将失效。这是因为Spring事务的实现基于代理类,当在内部直接调用方法时,将不会经过代理对象,而是直接调用目标对象的方法,无法被TransactionInterceptor拦截处理。解决办法:

(1)ApplicationContextAware

通过ApplicationContextAware注入的上下文获得代理对象。

public void saveAB(A a, B b)
{
    Test self = (Test) applicationContext.getBean("Test");
    self.saveA(a);
    self.saveB(b);
}

(2)AopContext

通过AopContext获得代理对象。

public void saveAB(A a, B b)
{
    Test self = (Test)AopContext.currentProxy();
    self.saveA(a);
    self.saveB(b);
}

(3)@Autowired

通过@Autowired注解注入代理对象。

@Component
public class Test {

    @Autowired
    Test self;

    public void saveAB(A a, B b)
    {
        self.saveA(a);
        self.saveB(b);
    }
    // ...
}

(4)拆分

将saveA、saveB方法拆分到另一个类中。

public void saveAB(A a, B b)
{
    txOperate.saveA(a);
    txOperate.saveB(b);
}

上述两个问题都是由于Spring事务的实现方式的限制导致的问题。下面再看两个由于使用不当容易犯错的两个问题。

2.3检查异常默认不回滚

在默认情况下,抛出非检查异常会触发回滚,而检查异常不会。

根据rollbackOn判断异常是否为回滚异常。只有RuntimeException和Error的实例,即非检查异常,或者在@Transaction中通过rollbackFor属性指定的回滚异常类型,才会回滚事务。否则将继续提交事务。所以如果需要对非检查异常进行回滚,需要记得指定rollbackFor属性,不然将回滚失效。

2.4 catch异常无法回滚

在2.3节中我们说到只有抛出非检查异常或是rollbackFor中指定的异常才能触发回滚。如果我们把异常catch住,而且没抛出,则会导致无法触发回滚,这也是开发中常犯的错误。


但同时我们也可以利用这种机制,用try-catch包裹不用参与事务的数据操作,例如对于写入一些不重要的日志,我们可将其用try-catch包裹,避免抛出异常,则能避免写日志失败而影响事务的提交。

spring事物的传播属性

https://www.javazhiyin.com/46170.html详解了事务的传播属性

1、propagation_required(xml文件中为required)
当前方法必须在一个具有事务的上下文中运行,如有客户端有事务在进行,那么被调用端将在该事务中运行,否则的话重新开启一个事务。(如果被调用端发生异常,那么调用端和被调用端事务都将回滚)

2、propagation_supports(xml文件中为supports)
当前方法不必需要具有一个事务上下文,但是如果有一个事务的话,它也可以在这个事务中运行

3、propagation_mandatory(xml文件中为mandatory)
表示当前方法必须在一个事务中运行,如果没有事务,将抛出异常

4、propagation_nested(xml文件中为nested)
如果当前方法正有一个事务在运行中,则该方法应该运行在一个嵌套事务中,被嵌套的事务可以独立于被封装的事务中进行提交或者回滚。如果封装事务存在,并且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务。如果封装事务不存在,则同propagation_required的一样

5、propagation_never(xml文件中为never)
当方法务不应该在一个事务中运行,如果存在一个事务,则抛出异常

6、propagation_requires_new(xml文件中为requires_new)
当前方法必须运行在它自己的事务中。一个新的事务将启动,而且如果有一个现有的事务在运行的话,则这个方法将在运行期被挂起,直到新的事务提交或者回滚才恢复执行。

7、propagation_not_supported(xml文件中为not_supported)
方法不应该在一个事务中运行。如果有一个事务正在运行,他将在运行期被挂起,直到这个事务提交或者回滚才恢复执行

spring中的事务隔离级别

1、isolation_default
使用数据库默认的事务隔离级别

2、isolation_read_uncommitted
允许读取尚未提交的修改,可能导致脏读、幻读和不可重复读

3、isolation_read_committed
允许从已经提交的事务读取,可防止脏读、但幻读,不可重复读仍然有可能发生

4、isolation_repeatable_read
对相同字段的多次读取的结果是一致的,除非数据被当前事务自生修改。可防止脏读和不可重复读,但幻读仍有可能发生

5、isolation_serializable
完全服从acid隔离原则,确保不发生脏读、不可重复读、和幻读,但执行效率最低。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值