为了更细粒度的事务划分,Spring提供如下两种方式的编程式事务管理:
1 PlatformTransactionManager
你也可以使用 org.springframework.transaction.PlatformTransactionManager 来直接管理你的事务。只需通过bean的引用,简单的把你在使用的PlatformTransactionManager 传递给你的bean。 然后,使用TransactionDefinition和TransactionStatus对象, 你可以启动,回滚和提交事务。
/
DefaultTransactionDefinition def = new DefaultTransactionDefinition(); // explicitly setting the transaction name is something that can only be done programmatically def.setName("SomeTxName"); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); TransactionStatus status = txManager.getTransaction(def); try { // 执行业务逻辑 } catch (MyException ex) { txManager.rollback(status); throw ex; } txManager.commit(status);
2 TransactionTemplate
PlatformTransactionManager中部分代码是可以重用的,所以spring对其进行了优化,采用模板方法模式就其进行封装,主要省去了提交或者回滚事务的代码。
若你选择编程式事务管理,Spring推荐使用 TransactionTemplate。 类似使用JTA的 UserTransaction API (除了异常处理的部分稍微简单点)。
2.1 简介
TransactionTemplate 采用与Spring中别的模板同样的方法,如 JdbcTemplate 。
使用回调机制,将应用代码从样板式的资源获取和释放代码中解放。
同一个事务管理的代码调用逻辑中,每次执行 SQL,都是基于同一连接,所有连接都是从一个公共地方TransactionSynchronizationManager获取。
所以获取连接操作不应该在 ORM 层框架,而是由 Spring 维护。
JdbcTemplate源码
@Override @Nullable public <T> T execute(ConnectionCallback<T> action) throws DataAccessException { Assert.notNull(action, "Callback object must not be null"); Connection con = DataSourceUtils.getConnection(obtainDataSource()); try { // Create close-suppressing Connection proxy, also preparing returned Statements. Connection conToUse = createConnectionProxy(con); return action.doInConnection(conToUse); } catch (SQLException ex) { // Release Connection early, to avoid potential connection pool deadlock // in the case when the exception translator hasn't been initialized yet. String sql = getSql(action); DataSourceUtils.releaseConnection(con, getDataSource()); con = null; throw translateException("ConnectionCallback", sql, ex); } finally { DataSourceUtils.releaseConnection(con, getDataSource()); } }
从给定的数据源获取连接。 知道绑定到当前线程的相应连接,例如在使用DataSourceTransactionManager 。 如果事务同步处于活动状态,例如在JTA事务中运行时,则将连接绑定到线程。
mybatis 中的各种 Mapper,其实底层就是通过使用 sqlSession 执行的。
而不管是 jdbcTemplate 还是 mybatis,获取连接都是通过 Spring的TransactionSynchronizationManager:
为何使用ThreadLocal存储资源?
若一个线程请求事务处理过程还在正常进行,但同时另一个线程并发也请求了同一事务的处理方法而且出现了异常要回滚了,若两个线程共用同一个数据库连接,就会导致回滚时影响原来正常执行的线程!所以连接对象是使用 ThreadLocal 存储的。就是为了避免多线程共用同一个连接,导致回滚时出现数据错乱。
使用 TransactionTemplate 会增加你的代码与Spring的事务框架和API间的耦合。所以是否使用编程式事务管理由你自己决定。
应用代码必须在一个事务性的上下文中执行,这样就会像这样一样显式的使用TransactionTemplate。作为应用程序员, 会写一个 TransactionCallback 的实现, (通常会用匿名类实现 )这样的实现会包含所以你需要在该事务上下文中执行的代码。
然后你会把一个你自己实现TransactionCallback的实例传递给TransactionTemplate暴露的execute 方法。
public class SimpleService implements Service { // single TransactionTemplate shared amongst all methods in this instance private final TransactionTemplate transactionTemplate; // use constructor-injection to supply the PlatformTransactionManager public SimpleService(PlatformTransactionManager transactionManager) { Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null."); this.transactionTemplate = new TransactionTemplate(transactionManager); } public Object someServiceMethod() { return transactionTemplate.execute(new TransactionCallback() { // the code in this method executes in a transactional context public Object doInTransaction(TransactionStatus status) { updateOperation1(); return resultOfUpdateOperation2(); } }); } }
如果不需要返回值,更方便的是创建一个 TransactionCallbackWithoutResult 匿名类
transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { updateOperation1(); updateOperation2(); } });
回调方法内的代码可以通过调用 TransactionStatus 对象的 setRollbackOnly() 方法来回滚事务。
transactionTemplate.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { try { updateOperation1(); updateOperation2(); } catch (SomeBusinessExeption ex) { status.setRollbackOnly(); } } });