一、在使用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會幫我們自動生成的,並動態插入,好了,問題解決。