1、spring aop实现
首先application-test.yml增加如下数据源的配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
spring: datasource: master: jdbc-url: jdbc:mysql: //master域名:3306/test username: root password: 123456 driver- class -name: com.mysql.jdbc.Driver slave1: jdbc-url: jdbc:mysql: //slave域名:3306/test username: root # 只读账户 password: 123456 driver- class -name: com.mysql.jdbc.Driver slave2: jdbc-url: jdbc:mysql: //slave域名:3306/test username: root # 只读账户 password: 123456 driver- class -name: com.mysql.jdbc.Driver |
1
2
3
4
|
package com.cjs.example.enums; public enum DBTypeEnum { MASTER, SLAVE1, SLAVE2; } |
定义ThreadLocal上下文,将当前线程的数据源进行动态修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public class DBContextHolder { private static volatile ThreadLocal<DBTypeEnum> contextHolder = new ThreadLocal<>(); public static synchronized void set(DBTypeEnum dbType) { contextHolder.set(dbType); } public static synchronized DBTypeEnum get() { return contextHolder.get(); } public static void master() { set(DBTypeEnum.MASTER); } public static void slave() { set(DBTypeEnum.SLAVE1); } public static void slave2(){ set(DBTypeEnum.SLAVE2); } // 清除数据源名 public static void clearDB() { contextHolder.remove(); } } |
重写mybatis数据源路由接口,在此修改数据源为我们上一块代码设置的上下文的数据源
1
2
3
4
5
6
7
8
|
public class MyRoutingDataSource extends AbstractRoutingDataSource { @Nullable @Override protected Object determineCurrentLookupKey() { DBTypeEnum dbTypeEnum=DBContextHolder.get(); return dbTypeEnum; } } |
将yml配置的多数据源手动指定注入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
@Configuration public class DataSourceConfig { @Bean @ConfigurationProperties ( "spring.datasource.master" ) public DataSource masterDataSource() { return DataSourceBuilder.create().build(); } @Bean @ConfigurationProperties ( "spring.datasource.slave1" ) public DataSource slave1DataSource() { return DataSourceBuilder.create().build(); } @Bean public DataSource myRoutingDataSource( @Qualifier ( "masterDataSource" ) DataSource masterDataSource, @Qualifier ( "slave1DataSource" ) DataSource slave1DataSource) { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(DBTypeEnum.MASTER, masterDataSource); targetDataSources.put(DBTypeEnum.SLAVE1, slave1DataSource); MyRoutingDataSource myRoutingDataSource = new MyRoutingDataSource(); myRoutingDataSource.setDefaultTargetDataSource(masterDataSource); myRoutingDataSource.setTargetDataSources(targetDataSources); return myRoutingDataSource; } } |
sqlsession注入以上我们配置的datasource路由
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
@EnableTransactionManagement @Configuration @Import ({TableSegInterceptor. class }) public class MyBatisConfig { @Resource (name = "myRoutingDataSource" ) private DataSource myRoutingDataSource; @Autowired private MybatisConfigProperty mybatisConfigProperty; @Autowired private TableSegInterceptor tableSegInterceptor; @Bean public SqlSessionFactory sqlSessionFactory() throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(myRoutingDataSource); // SpringBoot项目集成mybatis打包为jar运行时setTypeAliasesPackage无效解决 VFS.addImplClass(SpringBootVFS. class ); sqlSessionFactoryBean.setMapperLocations( new PathMatchingResourcePatternResolver().getResources(mybatisConfigProperty.getMapperLocations())); sqlSessionFactoryBean.setTypeAliasesPackage(mybatisConfigProperty.getTypeAliasesPackage()); sqlSessionFactoryBean.setConfigLocation( new PathMatchingResourcePatternResolver().getResource(mybatisConfigProperty.getConfigLocation())); sqlSessionFactoryBean.setPlugins( new Interceptor[]{tableSegInterceptor}); return sqlSessionFactoryBean.getObject(); } @Bean public PlatformTransactionManager platformTransactionManager() { return new DataSourceTransactionManager(myRoutingDataSource); } } |
spring aop拦截指定前缀的service方法,并设置对应所属的上下文
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
@Aspect @Component public class DataSourceAop { @Pointcut ( "!@annotation(com.ask.student.interceptor.annotation.Master) " + "&& (execution(* com.ask.student.service..*.select*(..)) " + "|| execution(* com.ask.student.service..*.get*(..))" + "|| execution(* com.ask.student.service..*.find*(..))" + ")" ) public void readPointcut() { } @Pointcut ( "@annotation(com.ask.student.interceptor.annotation.Master) " + "|| execution(* com.ask.student.service..*.insert*(..)) " + "|| execution(* com.ask.student.service..*.clean*(..)) " + "|| execution(* com.ask.student.service..*.reset*(..)) " + "|| execution(* com.ask.student.service..*.add*(..)) " + "|| execution(* com.ask.student.service..*.update*(..)) " + "|| execution(* com.ask.student.service..*.edit*(..)) " + "|| execution(* com.ask.student.service..*.delete*(..)) " + "|| execution(* com.ask.student.service..*.remove*(..))" ) public void writePointcut() { } @Before ( "readPointcut()" ) public void read() { DBContextHolder.slave(); } @Before ( "writePointcut()" ) public void write() { DBContextHolder.master(); } @After ( "readPointcut()||writePointcut()" ) public void afterSwitchDS(){ DBContextHolder.clearDB(); } } |
以上最后一个方法的作用,在拦截器中获取后及时清除避免导致来回切换当前线程变量延迟问题导致某些操作的数据源错误
DBContextHolder.clearDB();
@After("readPointcut()||writePointcut()")
public void afterSwitchDS(){
DBContextHolder.clearDB();
}
2、mybatis-plus的实现方式
这个方式配置简单,代码少,很多事情mybatis-plus都已经做好了,推荐使用
yml配置如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
datasource: dynamic: primary: master #设置默认的数据源或者数据源组,默认值即为master strict: false #设置严格模式,默认 false 不启动. 启动后在未匹配到指定数据源时候会抛出异常,不启动则使用默认数据源. datasource: master: url: jdbc:mysql: //xxx:3306/db0?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai username: admin password: 123456 driver- class -name: com.mysql.cj.jdbc.Driver type: com.zaxxer.hikari.HikariDataSource hikari: minimum-idle: 5 maximum-pool-size: 15 auto-commit: true idle-timeout: 30000 pool-name: springHikariCP max-lifetime: 1800000 connection-timeout: 30000 connection-test-query: SELECT 1 slave1: url: jdbc:mysql: //xxx:3306/db2?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai username: admin password: 123456 driver- class -name: com.mysql.cj.jdbc.Driver type: com.zaxxer.hikari.HikariDataSource hikari: minimum-idle: 5 maximum-pool-size: 15 auto-commit: true idle-timeout: 30000 pool-name: springHikariCP max-lifetime: 1800000 connection-timeout: 30000 connection-test-query: SELECT 1 slave2: url: jdbc:mysql: //xxx:3306/db3?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai username: admin password: 123456 driver- class -name: com.mysql.cj.jdbc.Driver type: com.zaxxer.hikari.HikariDataSource hikari: minimum-idle: 5 maximum-pool-size: 15 auto-commit: true idle-timeout: 30000 pool-name: springHikariCP max-lifetime: 1800000 connection-timeout: 30000 connection-test-query: SELECT 1 |
使用起来非常简单,只需要加上这个master的注解即可
1
2
3
4
5
6
7
|
@Override @DS ( "master" ) public DestMedia getOneByCodeFromEpg(String code) { QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq( "code" , code); return super .getOne(queryWrapper); } |
总结
本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注服务器之家的更多内容!
原文链接:https://blog.csdn.net/u013309797/article/details/119254805