mybatis多数据源配置与详解
记录下mybatis多数据源的使用过程,并对代码作用进行简述(只是个人对代码功能的猜测,如有错误请在评论区指出)。
目录
1,下载案例
https://gitee.com/leonzhang2013/mybaits__multiple_data_sources
ps:找了很多资料,这个案例是其中很详细的。备用地址:https://download.csdn.net/download/qq_42892858/13099539
2,案例过程讲解
除了选框选中的部分,其他和普通项目一样。
2.1 DatabaseContextHolder类
这个类就像一个箱子,A把要使用数据源的名字放进去,B操作数据的时候读箱子里的内容就知道要操作那个数据库。
public class DatabaseContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal(); //这就是箱子
public static void setDataSourceType(String dataSourceType) {
System.out.println("正在设置 数据 源:"+dataSourceType);
contextHolder.set(dataSourceType);
}
public static String getDataSourceType() {
System.out.println("获取当前的数据源:"+contextHolder.get());
return contextHolder.get();
}
}
2.2 DataSourceAop类
上边说A操作箱子,DataSourceAop在这就相当于一个操作箱子的角色(当然可以通过其实方式操作箱子)
为什么用Aop,因为Aop是每次操作都会执行的,就可以通过辨别操作者来更改箱子中的内容
@Aspect
@Component
public class DataSourceAop {
//MultDataSource是多数据源,所有的数据源都存放在这里面
@Autowired
MultDataSource multDataSource;
@Before("execution(* com.example.mybatis.mapper..*.get*(..)) || " +
"execution(* com.example.mybatis.mapper..*.list*(..)) ||"+
"execution(* com.example.mybatis.mapper..*.select*(..))")
public void setReadDataSource(){
DatabaseContextHolder.setDataSourceType(getReadKey());
System.out.println("我是读");
}
@Before("execution(* com.example.mybatis.mapper..*.add*(..)) || " +
"execution(* com.example.mybatis.mapper..*.update*(..)) ||"+
"execution(* com.example.mybatis.mapper..*.insert*(..)) ||"+
"execution(* com.example.mybatis.mapper..*.delete*(..))")
public void setWriteDataSource(){
DatabaseContextHolder.setDataSourceType(getMainKey());
System.out.println("我是写");
}
/**
* 轮询方式
* 这里没什么难的,就是选择往箱子中放什么内容。与主题无关
*/
int m = 0;
public String getMainKey(){
List<String> readKeys = multDataSource.getMainKeys();
m ++;
m = m%readKeys.size();
return readKeys.get( m );
}
int i = 0;
public String getReadKey(){
List<String> readKeys = multDataSource.getReadKeys();
i ++;
i = i%readKeys.size();
return readKeys.get( i );
}
}
2.3 DynamicDataSource类
此类继承了AbstractRoutingDataSource类,并重写了determineCurrentLookupKey方法。在这个方法中返回了要操作数据源的名字,这个名字就是在箱子(DatabaseContextHolder)中获取的,可见这个类就相当于角色B。但是要注意mybatis现在是不知道这个类存在的。
public class DynamicDataSource extends AbstractRoutingDataSource{
@Override
protected Object determineCurrentLookupKey() {
String dataSourceType = DatabaseContextHolder.getDataSourceType();
System.out.println("动态获取到的 数据源key == "+dataSourceType);
return dataSourceType;
}
}
2.4 MyDatabase类
这个类就是存放数据源的配置,没要点
@Data
public class MyDatabase {
private String password;
private String driver;
private String username;
private String url;
private String type;
}
2.5MultDataSource类
这个类是核心类,也是直接和mybatis打交道的类。
/**
* @Author: zll
* @Date @{DATE} 21:26
*/
//配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法
@Configuration
//在编译之后会生成相应的接口实现类
@MapperScan(basePackages = "com.example.mybatis.mapper")
//加载指定配置文件 multidatabase.properties
@PropertySource(value = "classpath:multidatabase.properties", encoding = "utf-8")
//从配置文件中提取my开头的句子
@ConfigurationProperties("my")
@Data
public class MultDataSource {
public static final String MAIN = "main";
public static final String READ = "read";
public List<String> mainKeys = new ArrayList<>();
public List<String> readKeys = new ArrayList<>();
@Value("${my.datasource.driver}")
private String driver;
/**
* 读取配置文件。由以下两个注解实现
* 也可以说运行时,n个数据源信息(由multidatabase.properties提供)会由spring自动放在里面
* @PropertySource(value = "classpath:multidatabase.properties", encoding = "utf-8")
* @ConfigurationProperties("my")
*/
private List<MyDatabase> datasource;
//这个方法返回druid数据源,创建数据源信息由datasource提供
public DruidDataSource getDataSource(MyDatabase database) {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl(database.getUrl());
druidDataSource.setUsername(database.getUsername());
druidDataSource.setDriverClassName(driver);
druidDataSource.setPassword(database.getPassword());
druidDataSource.setInitialSize(1);
druidDataSource.setMaxWait(6000);
druidDataSource.setMinIdle(8);
return druidDataSource;
}
//这个方法管理所有的数据源, setTargetDataSources设置当前的数据库
@Bean
//用@Primary 告诉spring 在犹豫的时候优先选择哪一个具体的实现。
@Primary
public DynamicDataSource dataSource() {
Map<Object, Object> targetDataSources = new HashMap<>();
//对targetDataSources初始化,完成后targetDataSources存放所有数据源信息
for (int i = 0; i < datasource.size(); i++) {
String type = datasource.get(i).getType();
DruidDataSource dataSource = getDataSource(datasource.get(i));
if (MAIN.equals(type)) {
mainKeys.add(MAIN+i);
targetDataSources.put(MAIN+i,dataSource);
} else {
readKeys.add(READ+i);
targetDataSources.put(READ+i,dataSource);
}
}
DynamicDataSource dataSource = new DynamicDataSource();
// 将所有的数据源提交给dataSource返回给mybatis
dataSource.setTargetDataSources(targetDataSources);
// 告诉mybatis默认使用的数据源,在这个项目中这个数据源不会使用,因为aop的原因每次执行前都会指定一个数据源
dataSource.setDefaultTargetDataSource(targetDataSources.get(mainKeys.get(0)));
return dataSource;
}
//DynamicDataSource虽然知道要调用的数据源,但mybatis不知道,这里就相当于把DynamicDataSource告诉mybatis.这样mybatis每次操作数据库时
//都要先从DynaicDataSource中获取要操作数据库的名字,然后在去操作数据库
@Bean
public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception {
return new DataSourceTransactionManager(dataSource);
}
}