文章目录
声明式事务(@Transactional)
基于AOP面向切面的,它将具体业务与事务处理部分解耦,代码侵入性很低,所以在实际开发中声明式事务用的比较多。但是声明式事务用不对在某些场景下容易失效。
代码 demo
@Service
public class LogDAOImpl extends AbstracRepositoryImpl implements ILogDAO {
@Override
@Transactional
public String saveLog(LogAggregateRoot log){
MsLog msLog = new MsLog();
msLog.setUserId(log.getUserId());
return super.save(msLog).getLogId().toString();
}
}
目标类没有被spring管理
demo
//@Service
public class LogDAOImpl extends AbstracRepositoryImpl implements ILogDAO {
@Override
@Transactional
public String saveLog(LogAggregateRoot log){
MsLog msLog = new MsLog();
msLog.setUserId(log.getUserId());
return super.save(msLog).getLogId().toString();
}
}
因为@Transactional 声明式事务基于spring的aop实现的,LogDAOImpl没有被spring管理,就无法开始事务。
同一个类内方法调用
demo
@Service
public class LogDAOImpl extends AbstracRepositoryImpl implements ILogDAO {
@Override
public String save(){
LogAggregateRoot log = new LogAggregateRoot();
return this.saveLog(log);
}
@Override
@Transactional
public String saveLog(LogAggregateRoot log){
MsLog msLog = new MsLog();
msLog.setUserId(log.getUserId());
return super.save(msLog).getLogId().toString();
}
}
因为@Transactional 声明式事务基于spring的aop实现的,同一个类内部方法调用没有经过代理,外部调用方法save() -> ProxyLogDAOImpl.save(),方法save()在类内部调用方法saveLog(LogAggregateRoot log) -> LogDAOImpl.saveLog(LogAggregateRoot log),造成事务失效。
事务方法不是 public
demo
@Service
public class LogDAOImpl extends AbstracRepositoryImpl implements ILogDAO {
@Override
@Transactional
private String saveLog(LogAggregateRoot log){
MsLog msLog = new MsLog();
msLog.setUserId(log.getUserId());
return super.save(msLog).getLogId().toString();
}
}
spring进行事务代理时候获取方法的属性,但是非public的方法无法被获取到属性,不会开始事务,故事务失效。
异常被业务代码 catch
demo
@Service
public class LogDAOImpl extends AbstracRepositoryImpl implements ILogDAO {
@Override
@Transactional
private String saveLog(LogAggregateRoot log){
try {
MsLog msLog = new MsLog();
msLog.setUserId(log.getUserId());
return super.save(msLog).getLogId().toString();
}
catch (Exception e) {
logger.error("LogDAOImpl exception", e);
}
return null;
}
}
类的业务代码中直接把异常catch住了,Spring自然就 catch不到异常,因此事务回滚的逻辑就不会执行,事务就失效了。
rollbackFor属性配置错误
demo
@Service
public class LogDAOImpl extends AbstracRepositoryImpl implements ILogDAO {
@Override
@Transactional
private String saveLog(LogAggregateRoot log){
MsLog msLog = new MsLog();
msLog.setUserId(log.getUserId());
throw new Exception("保存日志错误");
return super.save(msLog).getLogId().toString();
}
}
因为默认回滚的是:RuntimeException,如果你想触发其他异常的回滚,需要在注解上配置一下
@Transactional(rollbackFor = Exception.class)
事务传播属性使用错误
demo
@Service
public class LogDAOImpl extends AbstracRepositoryImpl implements ILogDAO {
@Lazy
@Autowired
private LogDAOImpl logDAOImpl;
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public String save(){
LogAggregateRoot log = new LogAggregateRoot();
return logDAOImpl.saveLog(log);
}
@Override
public String saveLog(LogAggregateRoot log){
MsLog msLog = new MsLog();
msLog.setUserId(log.getUserId());
return super.save(msLog).getLogId().toString();
}
}
我们先对 Propagation类中 Spring事务传播机制进行总结:
PROPAGATION_REQUIRED:要求使用事务,如果当前没有事务,则创建一个新的事务,如果当前存在事务,就加入该事务,该设置是默认也是最常用的设置。
PROPAGATION_SUPPORTS:支持使用事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
PROPAGATION_MANDATORY:强制使用事务,如果当前存在事务,就加入该事务,如果当前不存在事务,则抛出异常。
PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
PROPAGATION_NOT_SUPPORTED:不支持事务,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER:不允许使用事务,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED:内嵌事务,如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。
上述 save()方法的事务传播机制是 Propagation.SUPPORTS,也就是当前方法有事务就假如事务,不存在事务则以非事务执行,因为 saveLog(LogAggregateRoot log)方法不存在事务,所以该方法就以非事务执行,因此事务失效。
此案例是典型的 Spring事务传播机制使用错误,我们只需要将 @Transactional(propagation = Propagation.SUPPORTS) 修改成 @Transactional(propagation = Propagation.REQUIRED),事务就可以生效了。
数据库不支持事务
re:mysql
MySQL 数据库的MyISAM 引擎不支持事务,而 InnoDB 引擎支持事务。