mongo唯一键冲突造成事务破坏

先上代码

    @Transactional
    fun bindParentQd(childQdName: String, parentQdId: String) {
        try {
            //插入子渠道表
            qdDao.insertChildQd(childQdName, parentQdId)
        } catch (e: DuplicateKeyException) {
            //插入冲突,说明有人插入过了,进行更新
            val cnt = qdDao.bindParentQd(childQdName, parentQdId)
            if (cnt == 0) {
                throw RuntimeException("更新失败,0件")
            }
        }
        //更新父渠道表的子渠道数量
        if (qdDao.incParentQdChildCnt(1, parentQdId) == 0) {
            throw RuntimeException( "子渠道【${childQdName}】绑定的父渠道【${parentQdId}】不存在")
        }
    }

运行后报错:

org.springframework.data.mongodb.MongoTransactionException: Command failed with error 251 (NoSuchTransaction): 'Transaction 1 has been aborted.' on server 127.0.0.1:30000. The full response is {"errorLabels": ["TransientTransactionError"], "operationTime": {"$timestamp": {"t": 1598437778, "i": 2}}, "ok": 0.0, "errmsg": "Transaction 1 has been aborted.", "code": 251, "codeName": "NoSuchTransaction", "$clusterTime": {"clusterTime": {"$timestamp": {"t": 1598437778, "i": 2}}, "signature": {"hash": {"$binary": {"base64": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=", "subType": "00"}}, "keyId": 0}}}; nested exception is com.mongodb.MongoCommandException: Command failed with error 251 (NoSuchTransaction): 'Transaction 1 has been aborted.' on server 192.168.31.226:30000. The full response is {"errorLabels": ["TransientTransactionError"], "operationTime": {"$timestamp": {"t": 1598437778, "i": 2}}, "ok": 0.0, "errmsg": "Transaction 1 has been aborted.", "code": 251, "codeName": "NoSuchTransaction", "$clusterTime": {"clusterTime": {"$timestamp": {"t": 1598437778, "i": 2}}, "signature": {"hash": {"$binary": {"base64": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=", "subType": "00"}}, "keyId": 0}}}

	at org.springframework.data.mongodb.core.MongoExceptionTranslator.translateExceptionIfPossible(MongoExceptionTranslator.java:130)
	at org.springframework.data.mongodb.core.MongoTemplate.potentiallyConvertRuntimeException(MongoTemplate.java:2874)
	at org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:568)
	at org.springframework.data.mongodb.core.MongoTemplate.doUpdate(MongoTemplate.java:1625)
	at org.springframework.data.mongodb.core.MongoTemplate.updateFirst(MongoTemplate.java:1548)
	at yyf256.top.springboottest.dao.QdDao.bindParentQd(QdDao.kt:37)
	at yyf256.top.springboottest.dao.QdDao$$FastClassBySpringCGLIB$$3c385660.invoke(<generated>)

报错原因是走入了catch了得DuplicateKeyException里逻辑,当执行qdDao.bindParentQd(childQdName, parentQdId)更新操作时报错了。先说一下这一段逻辑,有两张表,子渠道表和父渠道表,子渠道可以绑定父渠道,父渠道表里会记录所属子渠道的数量。bindParentQd这个函数就是执行子渠道绑定父渠道的操作,如果子渠道不存在,顺便要落到子渠道表里。所以这里的逻辑就是先执行插入子渠道,如果渠道id冲突了,说明子渠道已经存在,则取更新子渠道表中的父渠道id,最后再去父渠道表里将所属子渠道数量进行+1,但是如果说所属父渠道不存在,则需要将先前的更新或者插入的操作一起回滚了。整个逻辑就是这样,但是当子渠道已经存在,走到qdDao.bindParentQd(childQdName, parentQdId)时就报上面的错了。
    造成这个问题更深层次的原因其实没有找到,以后弄明白了在补上,这里往下说解决方案。将这个事务拆成两个事务:

    @Transactional
    fun insertBindParentQd(childQdName: String, parentQdId: String) {
        //插入子渠道表
        qdDao.insertChildQd(childQdName, parentQdId)
        //更新父渠道表的子渠道数量
        if (qdDao.incParentQdChildCnt(1, parentQdId) == 0) {
            throw BizCodeException(BizCodes.C_5911, "子渠道【${childQdName}】绑定的父渠道【${parentQdId}】不存在")
        }
    }


    @Transactional
    fun updateBindParentId(childQdName: String, parentQdId: String) {
        val cnt = qdDao.bindParentQd(childQdName, parentQdId)
        if (cnt == 0) {
            throw BizCodeException(BizCodes.C_5901, "子渠道【${childQdName}】已关联其他父渠道")
        }
        //更新父渠道表的子渠道数量
        if (qdDao.incParentQdChildCnt(1, parentQdId) == 0) {
            throw BizCodeException(BizCodes.C_5911, "子渠道【${childQdName}】绑定的父渠道【${parentQdId}】不存在】")
        }
    }

然后将DuplicateKeyException这个异常放到事务外面去捕获:

    try {
        qdHelper.insertBindParentQd(it, bindParentQdRequest.parentQdName)
    } catch (e: DuplicateKeyException) {
        qdHelper.updateBindParentId(it, bindParentQdRequest.parentQdName)
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值