shardingsphere 使用 hibernate jpa save 方法修改数据时 提示 Can not update sharding key 的解决方案

问题

如同标题所描述使用场景为
使用shardingsphere进行分库处理 包含分库键 String did 后面直接使用了
当使用save方法进行新增操作时没问题 但是当使用相同方法进行编辑操作时则会提示
Can not update sharding key xxxx的信息
虽然可以使用@Query注解进行修改操作类似于下面代码

    @Transactional
    @Modifying
    @Query(value = "update post set update_dt = now(),text=:#{#post.text} , images=:#{#post.images},videos=:#{#post.videos}" +
            ",base_like=:#{#post.baseLike},base_share=:#{#post.baseShare},is_top=:#{#post.getIsTop()},tag=:#{#post.tag}  " +
            "where did = :#{#post.did} and id = :#{#post.id}", nativeQuery = true)
    void updatePost(Post post);

但是问题很明显如果我需要更新好几十个字段的时候会很麻烦而且仅仅只是在where语句后面增加了did=xxx这个语句而已
于是经过搜索找到相关issue经过研究发现帖子中出现是因为无法更新sharding column 分库键
目前5.1.0的shardingsphere可以通过比对update where后面的语句如果出现did 并且分库键相同则会进行跳过处理
所以当前问题为如何修改原有save 方法使其能够直接在where id=? 之前或者之后增加分库键的判断语句

方案1

增加注解
@Column(name = “xxx”, updatable = false)
设置不可更新
但是缺点是更新语句会执行全部分片不够完美
原贴见末尾issue链接
在这里插入图片描述

方案2 是一种查询方案的替代

@Embeddable
@Data
public class CompositeId implements Serializable {
    private Long id; // pk
    private String accountId; // sharding key
}

@Table("xxxx")
@Entity 
public class YourClass {
    @EmbeddedId
    private CompositeId compositeId;
}

Then you can do findByCompositeIdAccountId.

没有实际测试过但是update应该是满足不了需求的

在这里之后官方进行了相关更新及支持就是那个where语句判断

方案3 使用拦截器

拦截器如下

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.EmptyInterceptor;
import java.util.HashMap;
import java.util.Map;
@Slf4j
public class AutoAddDidInterceptor extends EmptyInterceptor {

    public static ThreadLocal<Map<String, String>> myTable = new ThreadLocal<>();

    public static void setInterceptorDid(String did){
        Map<String, String> map = new HashMap<>();
        map.put("did_update", did);
        myTable.set(map);
    }
    @Override
    public String onPrepareStatement(String sql) {
        log.debug("sql interceptor worked" );
        Map<String, String> map = myTable.get();
        if (map == null) {
            return sql;
        }
        String did = map.get("did_update");
        if (StringUtils.isEmpty(did) ) {
            return sql;
        }
        //judge update sql
        if(sql.startsWith("update")){
            String[] wheres = sql.split("where");
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append(wheres[0]).append(" where did = '"+did+"' and ").append(wheres[1]);
            log.warn("sql has been modify and now is {}",stringBuffer.toString());
            //clean threadLocal
            myTable.remove();
            return stringBuffer.toString();
        }else{
            return sql;
        }
    }

}

设置启动参数

spring.jpa.properties.hibernate.session_factory.session_scoped_interceptor=com.AutoAddDidInterceptor

使用方法:

   AutoAddDidInterceptor.setInterceptorDid(post.getDid());
  postRepository.save(post);

经过测试可以正常使用

问题

** 问题就是是否会出现sql注入的问题以及相关安全风险**

欢迎各位能提出优化及改进的方案 比如在使用处进行切面或者注解进行优化 再或者对于拦截器的进阶使用目前所有sql都会进行判断这里也是可以优化的点

希望能帮助到有需要的程序猿吧(~ ̄▽ ̄)~

相关资料

shardingsphere官方相关问题及讨论
spring data jpa 动态表名(自定义拦截器)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值