Appearance
00. 前言
在实际开发中,即使在微服务盛行的今天,在一个系统中可能会有用到多个数据源(最简单的读写分离),但mybatis默认只有一个数据源,我们想用多个数据源的话,就只能自己去控制数据源,当前采用的是拦截器拦截自定义注解,在执行sql之前切换到具体的数据源,并在执行完之后销毁数据源。
01. 使用方法
1.在application-dev.yml配置数据源
# 数据源配置
datasource:
master:
# 原框架数据库配置,涉及的到太多,没办法删除
dbtype: Oracle
dbname: PDBORCL
host: 192.168.3.210
port: 1521
# 表空间(当数据库为Oracle、达梦DM8、金仓KingbaseES时表空间必须指定,其他数据库为空即可)
tablespace: JNPF
# 共用配置
username: JNPF
password: ENC(Jb1Yv5tBdfWAaJVEUSKsmg==) #密码:JNPF
# 多数据源配置
jdbcUrl: jdbc:oracle:thin:@//192.168.3.210/pdborcl
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: oracle.jdbc.OracleDriver
slave1:
dbtype: Oracle
dbname: PDBORCL
host: 192.168.3.210
port: 1521
# 表空间(当数据库为Oracle、达梦DM8、金仓KingbaseES时表空间必须指定,其他数据库为空即可)
tablespace: XM_BDCDC_TLW
# 共用配置
username: XM_BDCDC_TLW
password: ENC(BtB1Bm3D/zvKdSC6pVXL1Xqbt+j7Qgpm) #XM_BDCDC_TLW
# 多数据源配置
jdbcUrl: jdbc:oracle:thin:@//192.168.3.210:1521/pdborcl
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: oracle.jdbc.OracleDriver
slave2:
dbtype: Oracle
dbname: PDBORCL
host: 192.168.3.210
port: 1521
# 表空间(当数据库为Oracle、达梦DM8、金仓KingbaseES时表空间必须指定,其他数据库为空即可)
tablespace: XM_BDCDC_WS_TLW
# 共用配置
username: XM_BDCDC_WS_TLW
password: ENC(ScfQHDnNLpDNcCPZqbMWikIvBT8ZaF7c) #XM_BDCDC_WS_TLW
# 多数据源配置
jdbcUrl: jdbc:oracle:thin:@//192.168.3.210:1521/pdborcl
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: oracle.jdbc.OracleDriver
slave3:
dbtype: Oracle
dbname: PDBORCL
host: 192.168.3.210
port: 1521
# 表空间(当数据库为Oracle、达梦DM8、金仓KingbaseES时表空间必须指定,其他数据库为空即可)
tablespace: XM_BDCDCCG_TLW
# 共用配置
username: XM_BDCDCCG_TLW
password: ENC(rJZED+J5v6KaLuwnyOhvcCfN2vUR4edw) #XM_BDCDCCG_TLW
# 多数据源配置
jdbcUrl: jdbc:oracle:thin:@//192.168.3.210:1521/pdborcl
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: oracle.jdbc.OracleDriver
slave4:
slave5:
2.在DynamicDataSourceConfig中从yml文件读取多个数据源进行解析,生成多数据源进行存储,目前默认配置5个,如果数据源较多,采用配置枚举
- 使用**@DbChoose**切换数据源。
@DbChoose可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解。
注解 | 结果 |
---|---|
没有@DbChoose | 默认数据源 |
@DbChoose("dsName") | dsName为具体某个库的名称 |
@Service
@DbChoose("slave1")
public class UserServiceImpl implements UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
public List selectAll() {
return jdbcTemplate.queryForList("select * from user");
}
@Override
@DbChoose("slave2")
public List selectByCondition() {
return jdbcTemplate.queryForList("select * from user where age >10");
}
}
02. 事务
基础知识
问:使用了事务如@Transational 无法切换数据源?
答: 是的,基于springAop的方案来进行多数据源的管理和切换的,要想保证多个库的整体事务则需要分布式事务。
问:为什么使用了事务如@Transational就无法切换数据源?
答:开启了事务后,spring事物管理器会保证在事务下整个线程后续拿到的都是同一个connection。
问:事务下无法切换数据源我知道了,那我单库的事务的可以用吗?
答:完全可以的。 只要事务下不切换数据源就OK。
1.在启动类中添加DataSourceTransactionManagerAutoConfiguration.class
2.使用@MoreTransaction注解进行事务控制
@MoreTransaction({"master","slave1","slave2","slave4"}) 如果一个方法中用到多个数据源,需要把所有数据源配置到@MoreTransaction中,没配置的数据源会导致切库失败
@Service
public class UserServiceImpl implements UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
public List selectAll() {
return jdbcTemplate.queryForList("select * from user");
}
@Override
@MoreTransaction({"master","slave1","slave2","slave4"})
public List selectByCondition() {
return jdbcTemplate.queryForList("select * from user where age >10");
}
}
03. 常见数据源切换失败问题
1.使用事务@Transactional 不能使用事务@Transactional,否则数据源不会切换,使用的还是第一次加载的数据源;使用@MoreTransaction.
public UserService {
@Transactional
@DbChoose("first")
public void test1() {
// do something
}
}
2.使用this指针 本数据源切换使用的是AOP拦截,使用this会导致AOP失效。具体参考文档
public UserService {
@DbChoose("first")
public void test1() {
this.save();
}
}
3.方法内部调用 查看以下示例 回答 外部调用userservice.test1()
能在执行到test2()
切换到second数据源吗?
public UserService {
@DbChoose("first")
public void test1() {
test2();
}
@DbChoose("second")
public void test2() {
// do something
}
}
04. 参考文档
1.springboot下mybatis多数据源的配置https://blog.csdn.net/nlx1450161741/article/details/115753151?spm=1001.2014.3001.5501
2.多数据源下事务控制https://blog.csdn.net/nlx1450161741/article/details/115873502?spm=1001.2014.3001.5501
3.this指针造成AOP失效https://blog.csdn.net/Zong_0915/article/details/126468545