前言
看到现在网上很多关于ShardingSphere-JDBC的example都是基于4.x或者3.x的,而且官方的example为了复用,结构比较复杂,对新手及其不友好,这里简单梳理一下,从搭建服务到示例,尽可能的方便新手快速入手,本文使用的是ShardingSphere-JDBC的5.0.0-alpha,具体的详细信息请参阅官方:https://shardingsphere.apache.org/
环境搭建
Build Dokcer Mysql
Docker version 19.03.8
//实例1
docker run -p 3306:3306 --name mymysql0 -v $PWD/conf:/Users/duansg/app/docker_mysql/conf.d -v $PWD/logs:/Users/duansg/app/docker_mysql/logs -v $PWD/data:/Users/duansg/app/docker_mysql/data/ -e MYSQL_ROOT_PASSWORD=123456 -d mysql
//实例2
docker run -p 3307:3306 --name mymysql1 -v $PWD/conf:/Users/duansg/app/docker_mysql/conf.d -v $PWD/logs:/Users/duansg/app/docker_mysql/logs -v $PWD/data:/Users/duansg/app/docker_mysql/data/ -e MYSQL_ROOT_PASSWORD=123456 -d mysql
Create Database
Server version: 8.0.22
docker exec -it mysql0 mysql -u root -p
mysql> create database demo00;
Query OK, 1 row affected (0.01 sec)
mysql> exit;
---------------------------------------
docker exec -it mysql1 mysql -u root -p
mysql> create database demo01;
Query OK, 1 row affected (0.01 sec)
mysql> exit;
Create Table
order_info_0,order_info_1
mysql> CREATE TABLE demo00.order_info_0 (
-> ID varchar(32) NOT NULL ,
-> ORDER_CODE varchar(64),
-> STATUS SMALLINT,
-> PRIMARY KEY (ID)
-> ) ENGINE=InnoDB ;
Query OK, 0 rows affected (0.05 sec)
mysql> CREATE TABLE demo00.order_info_1 (
-> ID varchar(32) NOT NULL ,
-> ORDER_CODE varchar(64),
-> STATUS SMALLINT,
-> PRIMARY KEY (ID)
-> ) ENGINE=InnoDB ;
Query OK, 0 rows affected (0.05 sec)
--------
mysql> CREATE TABLE demo01.order_info_0 (
-> ID varchar(32) NOT NULL ,
-> ORDER_CODE varchar(64),
-> STATUS SMALLINT,
-> PRIMARY KEY (ID)
-> )
ENGINE=InnoDB ;
Query OK, 0 rows affected (0.05 sec)
mysql> CREATE TABLE demo01.order_info_1 (
-> ID varchar(32) NOT NULL ,
-> ORDER_CODE varchar(64),
-> STATUS SMALLINT,
-> PRIMARY KEY (ID)
-> )
ENGINE=InnoDB ;
Query OK, 0 rows affected (0.05 sec)
Catalog
Code Catalog
├─com.duansg.fuck.db
│ ├─config
│ │ │ ShardingDataSourceMybatisConfig
│ │ └─ ShardingDataSourceMybatisPlusConfig
│ ├─dao
│ │ └─ OrderInfoMapper
│ ├─entity
│ │ └─ OrderInfo
│ └─service
│ │ OrderInfoService
│ ├─ impl
│ └─ OrderInfoServiceImpl
└─ServerApplication
application.yml
Configuration file
server:
port: 8088
#spring:
# datasource:
# driver-class-name: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://localhost:3306/demo00?serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
# username: root
# password: 123456
mybatis-plus:
#configuration:
#map-underscore-to-camel-case: true
#auto-mapping-behavior: full
mapper-locations: classpath*:/mapper/*Mapper.xml
type-aliases-package: com.duansg.fuck.db.entity
global-config:
# 逻辑删除配置
db-config:
# 删除前
logic-not-delete-value: 1
# 删除后
logic-delete-value: 0
sharding-databases.yml
Configuration file
# 配置真实数据源
dataSources:
# 配置第 1 个数据源
demo00: !!com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/demo00
username: root
password: 123456
# 配置第 2 个数据源
demo01: !!com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3307/demo01
username: root
password: 123456
rules:
# 配置分片规则
- !SHARDING
tables:
# 配置 order_info 表规则
order_info:
actualDataNodes: demo0${0..1}.order_info_${0..1}
# 配置分表策略
tableStrategy:
standard:
shardingColumn: id
shardingAlgorithmName: table_inline
# 主键生成策略
keyGenerateStrategy:
column: id
keyGeneratorName: snowflake
# 配置分库策略
databaseStrategy:
standard:
shardingColumn: order_code
shardingAlgorithmName: database_inline
# 配置分片算法
shardingAlgorithms:
database_inline:
type: INLINE
props:
algorithm-expression: demo0${order_code.hashCode() % 2}
table_inline:
type: INLINE
props:
algorithm-expression: order_info_${id.hashCode() & Integer.MAX_VALUE % 2}
keyGenerators:
snowflake:
type: SNOWFLAKE
props:
worker-id: 123
props:
sql-show: true
Mybatis-plus数据源设置
Mybatis同理,其他代码不作展示,都是正常用法。
/**
* ShardingDataSourceAutoConfig
*
* @author Duansg
* @version 1.0
* @date 2020/12/11 下午4:10
*/
@Configuration
public class ShardingDataSourceMybatisPlusConfig extends MybatisPlusAutoConfiguration {
public ShardingDataSourceMybatisPlusConfig(MybatisPlusProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider, ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider, ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider, ObjectProvider<List<MybatisPlusPropertiesCustomizer>> mybatisPlusPropertiesCustomizerProvider, ApplicationContext applicationContext) {
super(properties, interceptorsProvider, resourceLoader, databaseIdProvider, configurationCustomizersProvider, mybatisPlusPropertiesCustomizerProvider, applicationContext);
}
@Primary
@Bean("dataSource")
public DataSource getDataSource() throws SQLException, IOException {
File file = new File(Thread.currentThread().getClass().getResource("/sharding-databases.yml").getFile());
return YamlShardingSphereDataSourceFactory.createDataSource(file);
}
@Bean("sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource")DataSource dataSource) throws Exception {
return super.sqlSessionFactory(getDataSource());
}
@Bean("sqlSessionTemplate")
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory")SqlSessionFactory sqlSessionFactory) {
return super.sqlSessionTemplate(sqlSessionFactory);
}
}
测试
/**
* Test
*
* @author duansg
* @version 1.0
* @date 2020/12/10 下午10:23
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ServerApplication.class)
public class Test {
@Autowired
private OrderInfoService orderInfoService;
@org.junit.Test
public void insert(){
OrderInfo entity = new OrderInfo();
Random random = new Random();
entity.setOrderCode(String.valueOf(random.nextInt(10000)));
// entity.setOrderCode(String.valueOf(1));
entity.setStatus(0);
boolean save = orderInfoService.save(entity);
System.out.println(save);
}
@org.junit.Test
public void del(){
//在不知道是哪个库的前提下,会删除两个库里面的数据
boolean b = orderInfoService.removeById("1337348308472991745");
System.out.println(b);
}
@org.junit.Test
public void select(){
OrderInfo one = orderInfoService.getOne(new QueryWrapper<OrderInfo>().lambda()
.eq(OrderInfo::getId, "1337348308472991745")
.eq(OrderInfo::getOrderCode, "4082")
);
System.out.println(one);
}
@org.junit.Test
public void update(){
OrderInfo orderInfo = new OrderInfo();
orderInfo.setStatus(1);
boolean update = orderInfoService.update(orderInfo, new QueryWrapper<OrderInfo>().lambda()
.eq(OrderInfo::getId, "1337349695730647042")
.eq(OrderInfo::getOrderCode, "3475")
);
System.out.println(update);
}
验证结果
mysql> select count(1) from demo00.order_info_0;
+----------+
| count(1) |
+----------+
| 11280 |
+----------+
1 row in set (0.05 sec)
mysql> select count(1) from demo00.order_info_1
+----------+
| count(1) |
+----------+
| 11284 |
+----------+
1 row in set (0.05 sec)
mysql> select count(1) from demo01.order_info_0;
+----------+
| count(1) |
+----------+
| 11339 |
+----------+
1 row in set (0.05 sec)
mysql> select count(1) from demo01.order_info_1
+----------+
| count(1) |
+----------+
| 11306 |
+----------+
1 row in set (0.05 sec)
避坑指南
Unable to load authentication plugin ‘caching_sha2_password‘
Server version: 8.0.22
/**
*5.x版本是:default_authentication_plugin=mysql_native_password
*8.x版本就是:default_authentication_plugin=caching_sha2_password
*MySQL的版本升级就是为了更加安全,没有兼容旧的驱动而修改验证插件
*网上有使用运行命令将某个用户的验证模块改为旧版的模块
*不过我建议还是升级一下驱动版本吧
*/
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
Sharding value must implements Comparable.
order_info_${id.hashCode() & Integer.MAX_VALUE % 2
/**
* 原有的分表算法为order_info_${id.hashCode() % 2}
* 通过debug源码找到如下问题点,犯了一个很傻X的问题,就是
* id.hashCode()的范围
*/
public final class InlineShardingAlgorithm implements StandardShardingAlgorithm<Comparable<?>> {
//.....
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Comparable<?>> shardingValue) {
Closure<?> closure = this.createClosure();
closure.setProperty(shardingValue.getColumnName(), shardingValue.getValue());
return closure.call().toString();
}
//.....
}
Invalid bound statement (not found)
mybatis
/** * 问题是在使用insert等通用方法的时出现这个问题可以大致猜想一下,为什么编译通过了,执行却显示找不到方法?
* 通过@Insert能否通过测试?
* 见如上mp配置
*/