解决三大问题
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 中包含了事物的四中属性:
- 事物的隔离级别
- 事物的传播行为
- 是否只读
- 超时时间
TransactionStatus封装事物的对象,提供操作事务,查看事物状态的方法
- setRollbackOnly:标记事务为Rollback_only,使其回滚
- isRollbackOnly:查看是否被标记为RollbackOnly
- 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隔离原则,确保不发生脏读、不可重复读、和幻读,但执行效率最低。