在大部分涉及到数据库操作的项目里面,事务控制、事务处理都是一个无法回避的问题。比如,需要对SQL执行过程进行事务的控制与处理的时候,其整体的处理流程会是如下的示意:
首先是要开启事务、然后执行具体SQL,如果执行异常则回滚事务,否则提交事务,最后关闭事务,完成整个处理过程。按照这个流程的逻辑,写一下对应的实现代码:
public void testJdbcTransactional(DataSource dataSource) {
Connection conn = null;
int result = 0;
try {
// 获取链接
conn = dataSource.getConnection();
// 禁用自动事务提交,改为手动控制
conn.setAutoCommit(false);
// 设置事务隔离级别
conn.setTransactionIsolation(
TransactionIoslationLevel.READ_COMMITTED.getLevel()
);
// 执行SQL
PreparedStatement ps =
conn.prepareStatement("insert into user (id, name) values (?, ?)");
ps.setString(1, "123456");
ps.setString(2, "Tom");
result = ps.executeUpdate();
// 执行成功,手动提交事务
conn.commit();
} catch (Exception e) {
// 出现异常,手动回滚事务
if (conn != null) {
try {
conn.rollback();
} catch (Exception e) {
// write log...
}
}
} finally {
// 执行结束,最终不管成功还是失败,都要释放资源,断开连接
try {
if (conn != null && !conn.isClosed()) {
conn.close();
}
} catch (Exception e) {
// write log...
}
}
}
不难发现,上面大段的代码逻辑并不复杂,对于业务而言其实仅仅只是执行了一个insert操作而已。但是杂糅的事务控制代码,显然干扰了业务自身的代码处理逻辑的阅读与理解。
常规项目的代码中,涉及到DB处理的场景很多,如果每个地方都有这么一段事务控制的逻辑,那么整体代码的可维护性将会比较差,想想都令人窒息。
好在,JAVA中很多项目现在都是基于Spring框架进行构建的。得益于 Spring
框架的封装,业务代码中进行事务控制操作起来也很简单,直接加个 @Transactional
注解即可,大大简化了对业务代码的侵入性。那么对 @Transactional
事务注解了解的够全面吗?知道有哪些场景可能会导致 @Transactional
注解并不会如你预期的方式生效吗?知道应该怎么使用 @Transactional
才能保证对性能的影响最小化吗?
下面我们一起探讨下这些问题。
Spring声明式事务处理机制
为了简化业务开发场景对事务的处理复杂度,让开发人员可以更关注于业务自身的处理逻辑,Spring提供了声明式事务的能力支持。
Spring数据库事务约定处理逻辑流程如