分库分表报错:Can not update sharding value for table ‘xxxx‘

报错背景:在做公司项目的时候,因为数据量庞大,涉及到了分库分表,增删查一般没什么问题,唯独修改的时候出现了错误

## 分库分表配置(举例)
spring:
  jdbc:
    algorithm-inline-expression: db_resource_${0..1}        # 数据库两个
    sharding-column: company_id                             # 分库的分片键
    algorithm-expression: db_resource_${company_id % 2}     # 分库的方式:company_id取余
    inline-database-config:
      - dbname: tablename_0
        url: jdbc:mysql://ip:port/
        username: ****
        password: ******
      - dbname: tablename_1
        url: jdbc:mysql://ip:port/
        username: ****
        password: ******
    inline-table-config:
      - logic-table: xxx_table_data
        algorithm-inline-expression: xxx_table_data_${0..1}
        sharding-column: id                                # 分表的分片键
        algorithm-expression: xxx_table_data_${id % 2}
org.springframework.dao.DataIntegrityViolationException: 
### Error updating database.  Cause: java.sql.SQLException: Can not update sharding value for table `xxx_table_data`.
### The error may exist in xxx.java(路径) (best guess)
### The error may involve xxxMapper(路径).update-Inline
### The error occurred while setting parameters
### SQL: UPDATE xxx_table_data SET company_id = ?, ...(省略) WHERE (id = ? AND company_id = ?)
### Cause: java.sql.SQLException: Can not update sharding value for table `biz_purchase_data`.
; Can not update sharding value for table `xxx_table_data`.; nested exception is java.sql.SQLException: Can not update sharding value for table `xxx_table_data`.

上面的报错语句中,update语句中的 set company_id = ? 可能是报错的关键(本人猜测),从网上其他相关资料得知,分库分表中进行更新数据的话,不能更新分片键的值,因为这是确保分库分表的关键,所以需要让mybatis-plus生成的SQL语句中不带有set company_id,但是使用了很多方式都没有成功解决。

后来思考了下,因为之前每次都将实体类作为对象传入到更新方法中

/*比如*/
super.update(bizPurchaseData, wrapper);
/*比如*/
sysCompanyService.saveOrUpdateBatchByWrapper(companyList, co -> new LambdaQueryWrapper<SysCompany>().eq(SysCompany::getDb, KFC.mdb(co.getId())));
/*等等*/

而实体类中都是带有company_id字段的,即使设置为null obj.setCompanyId(null);也不能取消掉set company_id的SQL语句。

解决办法:所以对症下药,不使用实体类进行更新,纯粹的使用wrapper对象

LambdaUpdateWrapper<XxxTableData> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(XxxTableData::getId, xxxTableData.getId())
                .eq(XxxTableData::getCompanyId, xxxTableData.getCompanyId())
                .set(F.isNotEmpty(xxxTableData.getCode()), XxxTableData::getCode, xxxTableData.getCode())
                .set()
                .set()
                .set();
                // 可能会修改的值都用set拼接,并判断是否为空
// 最后调用更新时,不传实体类,仅仅使用LambdaUpdateWrapper对象
return super.update(null, wrapper);
// 不要使用update(wrapper); 因为wrapper设置了泛型,mybatis会通过反射获取到实体类,还是会按照实体类的字段进行set,结果还是报错

这样就可以实现更新了,如果是想批量或者想要代码更简洁,也可以再次封装,原理是不使用实体类即可。

因为这个问题困扰了很长时间,加上网上对该报错的直接解决方式很少,所以说的内容有些多,希望能帮助大家。如果有说错或者不足的地方,欢迎大家到评论区补充~

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值