声明式事务@Transaction失效场景

1. 开启事务支持

只需要通过 @EnableTransactionManagement 注解就可以开启声明式事务的支持,提供的可选值有:

  • proxyTargetClass:表示 AOP 代理是基于接口的还是基于类的,默认是基于接口代理。
  • mode:如果是有接口的话可以使用 PROXY ,如果没有接口的话可以使用 ASPECTJ 利用 CgLib 对类做增强。
  • order:指定事务拦截的顺序,默认是最低的优先级,这样可以保证其他的代理类是在事务开启之后执行。

2. @Transactional

开启了对事务注解的支持之后,我们就可以在需要的或者方法上面添加 @Transactional 注解,@Transactional 注解的话也有一些设置的项。

4.1 transactionManager

默认的话就是 DataSourceTransactionManager

4.2 propagation

事务的传播机制,默认的话是 REQUIRED ,多个方法调用共享同一个事务。

4.3 isolation

事务的隔离级别,这个跟使用的数据库有关系,默认的话是使用数据库默认的隔离级别。

4.4 timeout

事务超时时间。

4.5 readOnly

是否只读。

4.6 rollbackFor

或者方法中抛出的异常与 rollbackFor 中指定的异常 instanceof 返回 true 的情况下会回滚事务。

4.7 rollbackForClassName

rollbackFor 类似,只不过指定的是异常的名称。

4.8 noRollbackFor

遇到指定的异常时候不会回滚事务

4.9 noRollbackForClassName

noRollbackFor 类似,只不过指定的是异常的名称。

3. 没有通过代理对象执行

Spring 当中,声明式事务其实是为类做了一个代理,既然是代理类,也就是说需要去调用代理类才能够执行到需要被增强的方法,如果是在方法内部做调用的话,也就是没有走到代理方法或者说没有做增强处理,例如:

@Component
public class FooServiceImpl implements FooService {
    @Override
    @Transactional(rollbackFor = RollbackException.class)
    public void insertThenRollback() throws RollbackException {
        this.jdbcTemplate.execute("INSERT INTO FOO (BAR) values('BBB')");
        throw new RollbackException();
    }

    @Override
    public void invokeInsertThenRollback() throws RollbackException {
        this.insertThenRollback();
    }
}
@Override
public void run(String... args) throws Exception {
    try {
        this.fooService.invokeInsertThenRollback();
    } catch (RollbackException e) {
        // ignore.
    }
    log.info("bbb: {}.", this.countByBAR("BBB"));
}

日志输出如下:

bbb: 1.

要解决这个问题我们可以有以下几种方式:

3.1 通过当前代理对象调用自身实例

@Override
public void invokeInsertThenRollback() throws RollbackException {
    ((FooService) AopContext.currentProxy()).insertThenRollback();
}

3.2 注入自身实例

@Autowired
private FooService fooService;

@Override
public void invokeInsertThenRollback() throws RollbackException {
    this.fooService.insertThenRollback();
}

3.3 再加一层事务

@Override
@Transactional(rollbackFor = RollbackException.class)
public void invokeInsertThenRollback() throws RollbackException {
    this.insertThenRollback();
}

4. 不能对目标方法增强

@Transactional 只能支持 public 修饰方法,否则事务会失效,官方文档地址:Spring官方文档:声明式事务方法可见性

@Autowired
private FooServiceImpl fooService;

public void invokeInsertThenRollback() throws RollbackException {
    this.fooService.insertThenRollback();
}
@Transactional(rollbackFor = RollbackException.class)
protected void insertThenRollback() throws RollbackException {
    this.jdbcTemplate.execute("INSERT INTO FOO (BAR) values('BBB')");
    throw new RollbackException();
}

5. rollbackException与抛出的异常返回false

@Transactional 注解默认回滚异常是 RuntimeException 级别的异常,也就是非受检异常,如果是遇到了受检异常那么事务不会回滚:

public class RollbackException extends Exception {
}
@Transactional
public void insertThenRollback() throws RollbackException {
    this.jdbcTemplate.execute("INSERT INTO FOO (BAR) values('BBB')");
    throw new RollbackException();
}
public void invokeInsertThenRollback() throws RollbackException {
    this.fooService.insertThenRollback();
}

6. progagtion的选择问题

比如以下方法返回统计的 BBB1,有一条被回滚,有一条被成功执行。

@Override
@Transactional(rollbackFor = RollbackException.class,propagation = Propagation.REQUIRES_NEW)
public void insertThenRollback() {
    this.jdbcTemplate.execute("INSERT INTO FOO (BAR) values('BBB')");
}

@Override
@Transactional(rollbackFor = RollbackException.class)
public void invokeInsertThenRollback() throws RollbackException {
    this.jdbcTemplate.execute("INSERT into FOO (BAR) values('BBB')");
    this.fooService.insertThenRollback();
    throw new RollbackException();
}

你可能感兴趣的:(声明式事务@Transaction失效场景)