实现案例springboot+mybatis-plus+shardingJdbc5

1.准备工作,springboot+mybatis-plus工程,创建两个数据库,各包含t_pro_area,t_pro_fram两张表,字段随意

2.引入shardingjdbc jar

<!– 分库分表 –>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.1.2</version>
</dependency>
3.yml文件配置

spring:
shardingsphere:
props:
# 日志显示具体的SQL
sql-show: false
# 是否开启
datasource:
# 数据源(逻辑名字)
names: ds0,ds1
# 配置数据源
ds0:
type: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://localhost:3306/fishery-1?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimeZone=GMT%2B8&allowPublicKeyRetrieval=true
username: root
password: 123456
ds1:
type: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://localhost:3306/fishery-2?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimeZone=GMT%2B8&allowPublicKeyRetrieval=true
username: root
password: 123456

# 分片的配置
rules:
sharding:
# 表的分片策略
tables:
# 逻辑表的名称
t_pro_fram:
# 数据节点配置,采用Groovy表达式
actual-data-nodes: ds$->{0..1}.t_pro_fram
# 配置策略
database-strategy: #分库策略
standard:
sharding-column: sort # 分片列名称
sharding-algorithm-name: user_inline # 分片算法名称

# table-strategy: #分表策略
# standard:
# sharding-column: sort
# sharding-algorithm-name: database_alg

#分片算法名
sharding-algorithms:
user_inline:
type: inline
props:
algorithm-expression: ds$->{sort % 2}
测试controller

@RestController
@RequestMapping(“test”)
public class tController {

@Autowired
private ProFarmService proFarmService;

@GetMapping(“/list”)
public String list() {

for (int i = 1 ; i < 10; i++) {
ProFarm test =new ProFarm();
test.setCusNo(“嗡嗡嗡”+i);
test.setSort(i);
proFarmService.save(test);
}
retun “seccess”;
}
}
规则ds$->{sort % 2},实现按表字段sort取余,分配到ds0或ds1数据库中。分表同理,与否相同不做演示。

4.自定义分库规则实现StandardShardingAlgorithm接口

public class DatabaseAlgorithm implements StandardShardingAlgorithm<String> {
@Override
public String doSharding(Collection<String> collection, PreciseShardingValue<String> preciseShardingValue) {
System.out.println(“123”);

for(String aa : collection){ //所有库或表
System.out.println(aa);
//执行逻辑,返回需要执行的库或表
}

//返回需要具体操作的库或表名
return null;
}

@Override
public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<String> rangeShardingValue) {
System.out.println(“123+”);
return null;
}

@Override
public Properties getProps() {
System.out.println(“123+getProps”);
return null;
}

@Override
public void init(Properties properties) {
System.out.println(“123+init”);
}
}
yml修改

# 分片的配置
rules:
sharding:
# 表的分片策略
tables:
# 逻辑表的名称
t_pro_fram:
# 数据节点配置,采用Groovy表达式
actual-data-nodes: ds$->{0..1}.t_pro_fram
# 配置策略
database-strategy: #分库策略
standard:
sharding-column: sort # 分片列名称
sharding-algorithm-name: database_alg # 分片算法名称

# table-strategy: #分表策略
# standard:
# sharding-column: sort
# sharding-algorithm-name: database_alg

#分片算法名
sharding-algorithms:
user_inline:
type: inline
props:
algorithm-expression: ds$->{sort % 2}

database_alg:
type: CLASS_BASED #类引用
props:
strategy: STANDARD
algorithmClassName: com.iot.common.DatabaseAlgorithm #实现接口的全定义路径
添加了新的算法定义,database_alg

5.自定义Hint分库分表算法

如果需要用额外数据进行分库分表判断,没有用到数据表字段,比如实现多租户,需要取当前登录标识。

实现HintShardingAlgorithm接口

 

public class DatabaseHintAlgorithm implements HintShardingAlgorithm<String> {

@Override
public Collection<String> doSharding(Collection<String> collection, HintShardingValue<String> hintShardingValue) {

List<String> shardingResult = new ArrayList<>();
String bb =”ds0″;
for(String aa : collection){
System.out.println(aa);

}

shardingResult.add(bb); //size只能为1,返回需要操作的表或库
System.out.println(bb);
return shardingResult;
}

@Override
public Properties getProps() {
return null;
}

@Override
public void init(Properties properties) {
System.out.println(“DatabaseHintAlgorithm—–init”);
}
}
yml

# 分片的配置
rules:
sharding:
# 表的分片策略
tables:
# 逻辑表的名称
t_pro_fram:
# 数据节点配置,采用Groovy表达式
actual-data-nodes: ds$->{0..1}.t_pro_fram
# 配置策略
database-strategy: #分库策略
hint:
sharding-algorithm-name: hint_alg

# table-strategy: #分表策略
# standard:
# sharding-column: sort
# sharding-algorithm-name: database_alg

#分片算法名
sharding-algorithms:
user_inline:
type: inline
props:
algorithm-expression: ds$->{sort % 2}

database_alg:
type: CLASS_BASED #类引用
props:
strategy: STANDARD
algorithmClassName: com.iot.common.shardingJdbc.DatabaseAlgorithm #实现接口的全定义路径

hint_alg:
type: CLASS_BASED
props:
strategy: HINT
algorithmClassName: com.iot.common.shardingJdbc.DatabaseHintAlgorithm

添加了hint_alg 算法,此时还没完成,现在运行发现不进入hint的自定义算法中,需要添加HintManager 操作,

addDatabaseShardingValue(“t_pro_fram”,”1″);表示进行t_pro_fram表相关操作时,在把1传值到自定义的算法的参数hintShardingValue,addTableShardingValue(“”,””)同理,传到表算法定义的参数上,没有指定会爆错。
不想一个个表添加,使用hintManager.setDatabaseShardingValue(“ds1”);所有定义算法的表都传ds1.

多租户实现思路:可创建Aop拦截,在所有方法执行前,执行相关逻辑,把标识放入hintManager,方法后统一执行close();
@RestController
@RequestMapping(“test”)
public class tController {

@Autowired
private ProFarmService proFarmService;

@GetMapping(“/list”)
public String list() {

// Hint分片策略必须要使用 HintManager工具类
HintManager hintManager = HintManager.getInstance();
hintManager.addDatabaseShardingValue(“t_pro_fram”,”1″);
// 直接指定对应具体的数据库
//hintManager.setDatabaseShardingValue(“ds1”);

for (int i = 1 ; i < 10; i++) {
ProFarm test =new ProFarm();
test.setCusNo(“嗡嗡嗡”+i);
test.setSort(i);
proFarmService.save(test);
}

hintManager.close();

retun “seccess”;
}
}
6.默认分库策略default-database-strategy

一般我们很多表执行分库算法,不想一个个添加,发现官网有配置默认分库策略

误区:没有配置分库分表策略的会走default-database-strategy?
答:不会,具体ShardingShpere配置default-database-strategy或default-table-strategy失效原因_Rico_Yip的博客-CSDN博客_table-strategy

5版本没有了默认数据库配置,如果没有配置规则,会默认操作第一个数据源,如ds0

默认策略正确使用如下,配置了t_pro_area,但没有具体实现策略,就是走默认策略

# 分片的配置
rules:
sharding:
# 表的分片策略
tables:
# 逻辑表的名称
t_pro_fram:
# 数据节点配置,采用Groovy表达式
actual-data-nodes: ds$->{0..1}.t_pro_fram
# 配置策略
database-strategy: #分库策略
hint:
sharding-algorithm-name: hint_alg

# table-strategy: #分表策略
# standard:
# sharding-column: sort
# sharding-algorithm-name: database_alg

t_pro_area:
actual-data-nodes: ds$->{0..1}.t_pro_farm

default-database-strategy: #默认分库策略
hint:
sharding-algorithm-name: hint_alg
default-table-strategy: #默认分表策略
none: #没有

#分片算法名
sharding-algorithms:
user_inline:
type: inline
props:
algorithm-expression: ds$->{sort % 2}

database_alg:
type: CLASS_BASED #类引用
props:
strategy: STANDARD
algorithmClassName: com.iot.common.shardingJdbc.DatabaseAlgorithm #实现接口的全定义路径

hint_alg:
type: CLASS_BASED
props:
strategy: HINT
algorithmClassName: com.iot.common.shardingJdbc.DatabaseHintAlgorithm

AOP统一实现

@Aspect
@Order(-10)
@Component
public class DyanicDataSourceAscept {

//自己配置的读取配置文件,循环所参与策略的表,根据自己实际自定义配置字段
@Autowired
private ShardingJdbcProperties shardingJdbcProperties;

/**
* 定义一个方法,用于声明切入点表达式,方法中一般不需要添加其他代码 使用@Pointcut声明切入点表达式 后面的通知直接使用方法名来引用当前的切点表达式;如果是其他类使用,加上包名即可
*/
@Pointcut(“execution(public * com.*.*.controller.*Controller.*(..)) && !execution(public * com.*.admin.controller.AuthController.*(..))”)
public void declearJoinPointExpression() {
}

/**
* 前置通知
*
* @param joinPoint
*/
public void beforMethod(JoinPoint joinPoint) {

//修改为你自己的用户标识
LoginUser loginUser = UserUtils.getLoginUser();
System.out.println(“ThreadLocal – set beforMethod:”+loginUser.getTenantNo());

//取余数作为表后缀,从1开始
int tableIndex = loginUser.getTenantNo() % shardingJdbcProperties.getTableCount()+1;

//取整作为数据库后缀,从0开始
int dataBaseIndex = loginUser.getTenantNo() / shardingJdbcProperties.getTableCount();

// Hint分片策略必须要使用 HintManager工具类
HintManager hintManager = HintManager.getInstance();
for (String tableName: shardingJdbcProperties.getRoleTables()){
hintManager.addDatabaseShardingValue(tableName,shardingJdbcProperties.getDataBastPrefix()+dataBaseIndex);
hintManager.addTableShardingValue(tableName,shardingJdbcProperties.getTableInfix()+tableIndex);
}
}

/**
* 后置通知(无论方法是否发生异常都会执行,所以访问不到方法的返回值)
*
* @param joinPoint
*/
@After(“declearJoinPointExpression()”)
public void afterMethod(JoinPoint joinPoint) {
System.out.println(“After—————“);
HintManager.clear();
}

/**
* 返回通知(在方法正常结束执行的代码) 返回通知可以访问到方法的返回值!
*
* @param joinPoint
*/
public void afterReturnMethod(JoinPoint joinPoint, Object result) {
/

}

/**
* 环绕通知(需要携带类型为ProceedingJoinPoint类型的参数) 环绕通知包含前置、后置、返回、异常通知;ProceedingJoinPoin 类型的参数可以决定是否执行目标方法 且环绕通知必须有返回值,返回值即目标方法的返回值
*
* @param joinPoint
*/
@Around(value = “declearJoinPointExpression()”)
public Object aroundMethod(ProceedingJoinPoint joinPoint) {
Object result = null;
// 前置通知
beforMethod(joinPoint);

// 执行目标方法
try {
result = joinPoint.proceed();
} catch (Throwable e) {
throw new RuntimeException(e);
}

// 后置通知 – 新建
afterReturnMethod(joinPoint, result);
return result;
}

}
注意事项:

1.如果规则添加了某个表,策略配置错误并且没有配置默认策略,插入的效果会是全库全表,所有库的这张表都插入了数据。

2.没有定义策略的表默认使用第一个数据源

3.如果发现自定义的策略不进入,检查yml的格式,建议先配置简单inil类型,实现后确定了yml格式无误后,切换自定义策略(我就是因为格式问题耗费了好多时间,不报错)

4.如果分库分表都使用hint算法,那么使用addDatabaseShardingValue和addTableShardingValue,一个个表加上去。不能用setDatabaseShardingValue会把TableShardingValue.clen(),导致不进入分表算法。

5.官方文档还有很多其他内置策略,功能可以研究。概览 :: ShardingSphere

————————————————
版权声明:本文为CSDN博主「Michael_Joy」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Michael_Joy/article/details/126078773