问题
如同标题所描述使用场景为
使用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都会进行判断这里也是可以优化的点
希望能帮助到有需要的程序猿吧(~ ̄▽ ̄)~