基于baomidou / dynamic-datasource-spring-boot-starter的采用jdbc自动更新的动态多数据源工具。
可采用数据库和配置文件维护数据源
自动加载、移除数据源(数据来源为数据库方式)
可以使用接口参数自动切换数据源
同一数据源下切换schema(对于postgresql用的多)
多层数据源嵌套切换使用方案
自定义数据源切换逻辑
dynamic-source是基于dynamic-datasource-spring-boot-starter 的一种工具,该工具是为了解决数据源动态切换而创建的。
组件 | 版本 |
---|---|
java | 11 |
springboot | 2.5.2 |
mysql | 5.7 |
postgresql | 14.5 |
dynamic-datasource-spring-boot-starter | 3.4.1 |
<dependency>
<groupId>io.github.wangxvdonggroupId>
<artifactId>dynamic-datasourceartifactId>
<version>1.0.7version>
dependency>
implementation 'io.github.wangxvdong:dynamic-datasource:1.0.6'
比如我是用的postgresql
<dependency>
<groupId>org.postgresqlgroupId>
<artifactId>postgresqlartifactId>
<version>${postgresql.version}version>
dependency>
create table datasource_properties
(
key varchar(128) not null
constraint datasource_properties_pk
check ( key ~ '^ds-.*$' )
primary key,
url varchar(1024) not null,
username varchar(128) not null,
password varchar(256) not null,
driver_class_name varchar(128) not null,
schema varchar(128),
description varchar(1024),
created_at timestamp with time zone default CURRENT_TIMESTAMP,
updated_at timestamp with time zone
);
comment on table datasource_properties is '数据源配置表';
comment on column datasource_properties.key is '数据源标识';
comment on column datasource_properties.url is 'jdbc链接';
comment on column datasource_properties.username is '账户名称';
comment on column datasource_properties.password is '账户密码';
comment on column datasource_properties.driver_class_name is '数据库驱动类';
comment on column datasource_properties.schema is '数据库schema,(pgsql schema名称)';
comment on column datasource_properties.description is '数据源描述';
CREATE OR REPLACE FUNCTION update_modified_column()
RETURNS TRIGGER AS $$
BEGIN NEW.updated_at = now();
RETURN NEW;
END;
$$ language 'plpgsql';
create trigger update_table_name_update_at
before update
on datasource_properties
for each row
execute procedure update_modified_column();
create table datasource_properties
(
`key` varchar(128) not null comment '数据源标识',
url varchar(1024) not null comment 'jdbc链接',
username varchar(128) not null comment '账户名称',
password varchar(256) not null comment '账户密码',
driver_class_name varchar(128) not null comment '数据库驱动类',
`schema` varchar(128) not null comment '数据库schema,(pgsql schema名称)',
description varchar(1024) null comment '数据源描述',
created_at timestamp default CURRENT_TIMESTAMP not null ,
updated_at timestamp default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP ,
constraint datasource_properties_pk
primary key (`key`),
check ( `key` like 'ds-%')
) comment '数据源配置表';
# 因为mysql8.0以下的版本不支持check 约束,所以为了兼容旧版本提供了下面这个触发器来检查数据源配置key字段的检查
delimiter //
create trigger check_datasource_key_prefix before insert on datasource_properties
for each row
begin
if ! ( NEW.key like 'ds-%') then
signal sqlstate '50000' set message_text = 'datasource_properties column key must to begin by ds- ';
end if;
end;
delimiter ;
delimiter //
create trigger check_datasource_key_prefix before update on datasource_properties
for each row
begin
if ! ( NEW.key like 'ds-%') then
signal sqlstate '50000' set message_text = 'datasource_properties column key must to begin by ds- ';
end if;
end;
delimiter ;
spring:datasource:dynamic:datasource:jdbcdatasource
: # 指定使用jdbc 数据来源,必须用这个属性名!!!
配置后则开启JDBC数据源管理逻辑
spring:
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
lazy: true
datasource:
master:
url: jdbc:postgresql://192.168.99.77:5432/ddd?sslmode=disable¤tSchema=ddd&timezone=Asia/Shanghai&allowMultiQueries=true
username: ddd11
password: ddd11
driver-class-name: org.postgresql.Driver # 3.2.0开始支持SPI可省略此配置
schema: ddd
jdbcdatasource: # 指定使用jdbc 数据来源,必须用这个属性名!!!
url: jdbc:postgresql://101.111.222.121:5432/postgres?sslmode=disable¤tSchema=public&timezone=Asia/Shanghai&allowMultiQueries=true
username: postgres
password: 123321
driver-class-name: org.postgresql.Driver # 3.2.0开始支持SPI可省略此配置
schema: public
# slave_2:
# url: ENC(xxxxx) # 内置加密,使用请查看详细文档
# username: ENC(xxxxx)
# password: ENC(xxxxx)
# driver-class-name: com.mysql.jdbc.Driver
#......省略
#以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2
数据库表:datasource_properties
字段key
必须以”ds-
“开头
key | url | username | password | driver_class_name | schema | description | created_at | updated_at |
---|---|---|---|---|---|---|---|---|
ds-2 | jdbc:postgresql://192.168.88.99:5432/ddd?sslmode=disable¤tSchema=ddd&timezone=Asia/Shanghai&allowMultiQueries=true | username | userpassword | org.postgresql.Driver | public | NULL | 2022-09-04 14:03:41.247301 +00:00 | 2022-09-04 14:03:41.247301 +00:00 |
在datasource_properties
表里新增删除配置,程序里会跟着移除/加载数据源,1分钟更新一次,不会刷新老的数据源连接。只支持新增和删除记录,更新数据库记录没有程序数据源里面不会有动作。
继承cn.rocky.dynamicdatasource.service.AbstractCheckoutDataSource
就可以让数据源和schema根据自己的逻辑进行切换。
实现push/poll方法。
根据dsKey获取DataSourceProperty,比如schema等信息。
cn.rocky.dynamicdatasource.config.JdbcDynamicDataSourceConfig#getDataSourcePropertyByKey
代码如下(示例):
@Service
public class CustomizeCheckoutDataSourceImpl extends AbstractCheckoutDataSource {
@Autowired
private JdbcDynamicDataSourceConfig jdbcDynamicDataSourceConfig;
/**
* 切入数据源逻辑
* @param proceedingJoinPoint
*/
@Override
public void push(ProceedingJoinPoint proceedingJoinPoint) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String dsKey = request.getHeader("token");
String schema;
{
DataSourceProperty property = jdbcDynamicDataSourceConfig.getDataSourcePropertyByKey(dsKey); // 通过key获取数据源配置
schema = property.getSchema();
}
Stack<String> schemaStack = (Stack<String>) AbstractCheckoutDataSource.getThreadLocal().get();
if (schemaStack == null) {
schemaStack = new Stack<>();
}
schemaStack.push(schema); // 加入schema限定名
AbstractCheckoutDataSource.getThreadLocal().set(schemaStack); // 加入schema限定名
DynamicDataSourceContextHolder.push(dsKey); // 切入数据源
}
/**
* 切出数据源逻辑
*/
@Override
public void poll() {
DynamicDataSourceContextHolder.poll(); // 切出数据源
Stack<String> schemaStack = (Stack<String>) AbstractCheckoutDataSource.getThreadLocal().get();
schemaStack.pop(); // 移除schema限定名
AbstractCheckoutDataSource.getThreadLocal().set(schemaStack);
}
}
此注解会走AbstractCheckoutDataSource 的切换逻辑动态的选择数据源。
/**
* 嵌套使用数据源
*/
@JdbcDS
public void nestedCheckoutDs(){
List<Fields> fields = fieldsMapper.selectList(null);
log.info("jdbc data:{}", fields);
}
这个注解就是直接切换到指定的数据源。
/**
* 手动切数据源
*/
@DS("ds-2")
public void useDs2() {
List<Fields> fields = fieldsMapper.selectList(null);
log.info("ds2 data:{}", fields);
}
nestedCheckoutDs方法正常走切换逻辑,但是这个方法里面的对象是被AOP代理invoke的,不会触发AOP这个时候只有被AopContext.currentProxy()取出的对象才能让springAOP生效。
/**
* 嵌套使用数据源
*/
@JdbcDS
public void nestedCheckoutDs(){
List<Fields> fields = fieldsMapper.selectList(null);
log.info("jdbc data:{}", fields);
((CheckoutDataSourceServiceImpl) (AopContext.currentProxy())).useDs1();
((CheckoutDataSourceServiceImpl) (AopContext.currentProxy())).useDs2();
useDs1();
useDs2();
}
以上就是今天要讲的内容,本文简单介绍了dynamic-datasource的使用,而dynamic-datasource提供了能使我们快速便捷地处理数据源切换的方案。