spring的事务
- spring提供了许多抽象给数据访问层,使得在不同框架当中可以使用一样的方式进行数据访问操作。其中就包含事务抽象(一致的事务模型)
- spring事务抽象的核心接口PlatformTransactionManager 平台事务管理器,这个事务管理器当中会定义一系列事务操作、查询的方法(commit、rollback、getTransaction等)其中getTransaction会返回一个TrancactionStatus(代表这个事务的一些状态值)
spring通过这个事务管理器接口给各个平台框架提供对应的事务管理器
- DataSourceTransactionManager(Spring JDBC或iBatis)
- HibernateTransactionManager(Hibernate)
- JtaTransactionManager(Jta)
- 定义事务的详细信息TransactionDefinition
- Propagation
- Isolation
- Timeout
- Read-only status
- 事务传播特性共七种(官方文档是三种0、3、6)
- PROPAGION_REQUIRED:0,默认使用当前已有的事务,如果不存在,就新建一个(默认)
- PROPAGION_SUPPORTS:1,支持当前事务,如果不存在,就不使用事务
- PROPAGION_MANDATORY:2,当前一定要有事务,不然抛异常
- PROPAGION_REQUIRES_NEW:3,如果有事务存在,挂起当前事务,创建一个新的事务;两个事务是之间的回滚都不会互相影响。
- PROPAGION_NOT_SUPPORTED:4,以非事务方式运行,如果有事务存在,挂起当前事务
- PROPAGION_NEVER:5,不支持事务,有事务抛出异常
- PROPAGION_NESTED:6,嵌套。如果当前事务存在,就在当前事务再起一个事务嵌套执行(里面的事务拥有自己的事务相关属性,也包括自己的回滚状态,里面事务回滚,不会影响外面大的事务)(外面大的事务回滚,里面嵌套的事务也会回滚)
- 事务隔离特性,spring中值是-1(跟随数据库设置的隔离级别)
为什么spring同一个类中一个非事务方法调用另一个事务方法会失效
@Component
public class FooServiceImpl implements FooService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
@Transactional(rollbackFor = RollbackException.class)
public void insertThenRollback() throws RollbackException {
jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('BBB')");
throw new RollbackException();
}
@Override
public void invokeInsertThenRollback() throws RollbackException {
insertThenRollback();
}
}
- 方法insertThenRollback()使用了声明式事务,具体操作是插入一条数据之后手动抛出异常,此时事务会回滚;在其他类中注入FooServiceImpl并调用insertThenRollback()方法,该事务是生效的。
- 方法invokeInsertThenRollback()在内部调用了具有声明式事务的insertThenRollback()方法,但是发现insertThenRollback()该方法的事务并没起效,也就是FOO表中的却插入了一条记录,并没有因为手动抛出异常而回滚;
- 原因是因为spring的声明式事务使用的是AOP生成了一个代理对象,只有调用那个代理对象的方法,事务才有效;
- invokeInsertThenRollback()方法内部中调用了insertThenRollback(),是通过自身的实例进行调用,而spring的声明式事务创建的代理对象是在这个自身实例的基础上加了一层事务操作的封装,并且把它放入ioc容器中。此时其他类通过@Autowired引用的FooServiceImpl类其实就是一个封装了事务操作的代理类;
- 所以如果想要invokeInsertThenRollback()方法也具有事务能力,那么只需要在FooServiceImpl类中注入自己,并使用这个注入对象调用insertThenRollback()方法即可;
@Autowired
private FooServiceImpl fooService;
@Override
public void invokeInsertThenRollback() throws RollbackException {
fooService.insertThenRollback();
}
@Override
public void invokeInsertThenRollback() throws RollbackException {
((FooService)(AopContext.currentProxy())).insertThenRollback();
}
- 使用声明式事务时,@Transactional注解请加在实现类上,不要加在接口上。因为最终作为一个bean注入spring ioc容器中的,一定是个实现类;
- 编程式事务也很有用
- 编程式事务控制粒度更细(声明式事务最小粒度就是一个方法)
- 可以在代码中明显地看到了哪里使用了事务,并且在何处开始、提交等。不容易漏写对方法使用事务的控制;