ShardingSphere報Sharding value must implements Comparable.的解決過程

一、在使用sharding sphere操作垂直分表數據庫demo的時候,執行插入操作一直報這個莫名奇妙的錯誤,翻譯大致的意思是:分片值必須實現Comparable。首先通過翻譯猜測應該是有兩種可能:①是指實體類需要實現Comparable接口,然後就跑去官網重新看了一邊快速開始,但是並沒有這樣的要求。②可能少配置了什麼,然後對着官網的配置又捋了一邊,沒有配置錯誤,但是陰差陽錯,我發現如果主鍵值自己寫進去就可以插入成功,先說下我的代碼結構
二、代碼結構
因爲是一個demo所以比較簡單,首先數據庫有兩個表,user_1和user_2,
採用springboot+mybatis+sharding-idbc數據庫使用的是mysql
實體類:

import lombok.Data;
import org.apache.ibatis.type.Alias;

@Alias("User")
@Data
public class User {
    private Long uid;
    private String uName;
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.joe.shardingdemo.mapper.UserMapper">
    <insert id="insertUser" parameterType="User" keyColumn="uid" keyProperty="uid">
        INSERT INTO test(`uid`,`u_name`)
        VALUES
        (#{uid},#{uName,jdbcType=VARCHAR})
    </insert>
</mapper>

application.properties文件


# Mybatis配置
# xml文件位置
mybatis.mapper-locations=classpath:mapper/*.xml
# 實體類所在包
mybatis.type-aliases-package=com.joe.shardingdemo.pojo
# 駝峯命名配置
mybatis.configuration.map-underscore-to-camel-case=true


# sharding-sphere配置
# 數據庫配置,分庫用逗號隔開
spring.shardingsphere.datasource.names=ds1

# 數據源配置
spring.shardingsphere.datasource.ds1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds1.url=jdbc:mysql://localhost:3306/test_cas
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=root

# 分表名稱配置,其中的user爲表名,test_cas.user_$->{1..2}表示數據庫test_cas下兩個表user_1和user_2
spring.shardingsphere.sharding.tables.test.actual-data-nodes=ds1.test_$->{1..2}
# 配置分表策略,其中的user爲表名,u_id爲策略判斷的列的名稱,
spring.shardingsphere.sharding.tables.test.table-strategy.inline.sharding-column=uid
# user_$->{u_id % 2 + 1}  表示操作的表爲 user_ + "uid對2取模+1確定操作哪張表"
spring.shardingsphere.sharding.tables.test.table-strategy.inline.algorithm-expression=test_$->{uid % 2 + 1}
# 標明主鍵 user表的主鍵爲u_id
spring.shardingsphere.sharding.tables.test.key-generator.column=uid
# 標明主鍵通過雪花算法自動生成
spring.shardingsphere.sharding.tables.test.key-generator.type=SNOWFLAKE


# 開啓打印sql語句
spring.shardingsphere.props.sql.show=true

測試類

@SpringBootTest
class ShardingDemoApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    void contextLoads() {
        for(int i=0;i<10;i++){
            User user = new User();
            // 如果手動添加主鍵是可以添加的
            user.setUid(1l);
            user.setUName("test");
            userMapper.insertUser(user);
        }
    }

}

三、在測試的時候發現手動添加主鍵的值是可以插入的,所以問題大致定位在主鍵的生成上,因爲是分佈式的,所以shardingSphere爲我們內置了推特的雪花算法,我在主鍵配置那裏也是指定了SNOWFLAKE
在這裏插入圖片描述在這裏插入圖片描述
所以首先,看主鍵是否有生成,shardingsphere雪花算法相關的類是SnowflakeShardingKeyGenerator.generateKey(),所以在這個方法上打斷點,發現並沒有執行這個方法,所以感覺問題又進一步縮小了,應該是主鍵沒有自動生成導致的。查看網上資料,sharding在插入的時候,會調用ShardingInsertOptimizeEngine.optimize()來執行插入操作
在這裏插入圖片描述
在這裏看到有對應的生成主鍵的方法,打端點,報錯的具體位置就是這裏。
在這裏插入圖片描述
打斷點進這個方法,這個方法先獲取需要自動生成主鍵的列名,仔細看return那裏的三目運算符,會做一個判斷,如果要生成的列名包含在insertColumns(列名數組)裏面,就會調用findGeneratedKey去找主鍵的值,反之,纔會調用createGeneratedKey方法生成主鍵,createGeneratedKey會調用剛纔說的SnowflakeShardingKeyGenerator.generateKey()用雪花算法生成主鍵。所以正確的是讓程序去調用createGeneratedKey生成而不是去查找,因爲本來就沒有主鍵值,所以會報錯,具體可以在往深了打斷點,本質就是調用原生JDBC的ParperStatment去放值的時候放了一個null,報錯的。
所以關鍵就是要去看這個insertColumns是怎麼生成的,往上一層找調用這個方法的方法,發現是通過我們在mapper中配置的sql語句解析出的這個insertColumns,所以在這裏我們只需要修改mapper文件,將主鍵去掉就可以了。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.joe.shardingdemo.mapper.UserMapper">
    <insert id="insertUser" parameterType="User" keyColumn="uid" keyProperty="uid">
        INSERT INTO test(/*`uid`,*/`u_name`)
        VALUES
        (/*#{uid},*/#{uName,jdbcType=VARCHAR})
    </insert>
</mapper>

寫sql的時候不用寫主鍵列,sharding sphere會幫我們自動生成的,並動態插入,好了,問題解決。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章