spring 嵌套事务中遇到的坑

场景:

在某个业务场景需要向关系表x 中增加一条记录,但是x 中有唯一约束.

解决方案:

最直接的解决方案是这样的sql

INSERT INTO table (a,b,c) VALUES (1,2,3)  ON DUPLICATE KEY UPDATE c=c+1;

但是postgre 9.3 版本中不支持这样的语法

因此变通解决方案

先增加当发发生异常时进行更新

@Service("userAppDefaultOrganizationService")
@Transactional
public class DefaultXxxService implements XxxService {

	@Autowired
	private DefaultXxxDao userAxDao ;

	@Override
	public void saveOrUpdate(XxxEntity userAx) {

		if (userAx== null) {
			return;
		}
		
		try {
			//新增时独立事务执行当发生异常时执行一次update
			userAxDao.saveUserAx(userAx);

		} catch (Exception e) {
			userAxDao.updateUserAx(userAx);
		}
	}
  
    //要单独开启事务,否则出现唯一索引异常时 整个事务会回滚,外部在做update 会提示事务已经被回滚不能操作 
   @Transactional(propagation = Propagation.REQUIRES_NEW , rollbackFor=Exception.class)
	public void saveUserAx(UserAxEntity userAx){
		userAxMapper.addUserAx(userAx);
	}

	public void updateUserAx(UserAxEntity userAx){
		userAxMapper.updateUserAx(userAx);
	}
   @Transactional(propagation = Propagation.REQUIRES_NEW , rollbackFor=Exception.class)
	public void deleteUserAx(UserAxEntity userAx){
		userAxMapper.deleteUserAx(userAx);
	}

ok 这样测试可以通过了

但是在实际的应用场景中发现了新的问题,嵌套事务时发生死锁

  @Override
    public Long findOrAddUserAx(String appKey, Long userId) {

	```
					//当不在清理掉用无效数据
					userAxService.deleteUserAx(uado);
	````
			//重新计算
    ````
			if(orgId != null){
				//本次新计算出了org,此时 保存新的uado到db 中
			    uado = new UserAxEntity();
			    uado.setUserId(userId);
			    uado.setAppKey(appKey);
			    uado.setOrgId(orgId);
			    addUserAx(uado);//执行userAxService.add方法 此时程序停住不执行了
             }

    }

最后分析的原因是 进入findOrAddUserAx 时已经开启事务A 

在执行完                     userAxService.deleteUserAx(uado); 使用的事务A,并没有提交

下边在执行 addUserAx 时 内部开启了新的事务 执行时  因为表中有一个唯一索引  deleteUserAx 占用资源未提交 ,addUserAx 又要去占用这个锁 因此旧导致了死锁(去掉唯一索引上边代码则正常执行)

 

解决方案
1, 识别出特殊的场景直接做update,此时可绕过 独立事务的新增

2,deleteUserAx 增加独立事务

    //要单独开启事务,否则出现唯一索引异常时 整个事务会回滚,外部在做update 会提示事务已经被回滚不能操作 
   @Transactional(propagation = Propagation.REQUIRES_NEW , rollbackFor=Exception.class)
	public void saveUserAx(UserAxEntity userAx){
		userAxMapper.addUserAx(userAx);
	}

	public void updateUserAx(UserAxEntity userAx){
		userAxMapper.updateUserAx(userAx);
	}
   @Transactional(propagation = Propagation.REQUIRES_NEW , rollbackFor=Exception.class)
	public void deleteUserAx(UserAxEntity userAx){
		userAxMapper.deleteUserAx(userAx);
	}

综上 当使用嵌套事务时 一定要注意 可能引发数据库层面的死锁问题 所以尽量避免嵌套事务.

 

转载于:https://my.oschina.net/yangming0322/blog/819269

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值