一、使用场景
本次集成是针对单/多数据源,但是不是动态数据源的 Java 后端项目;我这次更改的需求是将一个多模块的项目更改为动态的多数据源,并且新增切换数据源的 AOP,让其能够在使用注解的方式自动切换数据源。同时业务的数据库需要跟随租户的新增而新增。
二、使用组件
主要使用 dynamic-datasource 的多数据源仓库。
三、具体方法
1、配置 pom 文件
1 2 3 4 5 6
| <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.5.1</version> </dependency>
|
这里我使用的版本是 3.5.1,据说 3.2版本一下支持比较少
2、配置 application.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
| server: port: 6666 servlet: context-path: /text
spring: autoconfigure: exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure datasource: druid: stat-view-servlet: enabled: true loginUsername: root loginPassword: 111111 dynamic: primary: master strict: false datasource: master: url: jdbc:mysql://ip-port username: root password: 111111 driver-class-name: com.mysql.jdbc.Driver slave: url: jdbc:mysql://ip-port username: root password: 111111 driver-class-name: com.mysql.jdbc.Driver
|
这个文件中,我最主要的配置属性就是 spring.datasource
,这是数据源的配置
1)其中 spring.datasource.druid
是因为我的项目集成了 Druid,使用 dynamic-datasource
的话需要集成,配置就按照我这样默认配置就行
2)spring.datasource.druid.dynamic
这里的配置属性就在于 primary
设置主数据源了,向我这里是有两个数据库,一个是 master 的用户数据库,一个是 slave 的业务数据库,我把 master 设为主数据库是因为用户这个书库我还是把它认定为静态数据库,不会跟随动态数据库的数据源的新增而新增一个新的业务数据库。
注意:由于我是两个模块,所以这个配置文件新增的数据源配置需要在两个模块的 application.yml
文件中都进行添加。
3、AOP 切面代码
1 2 3 4 5 6
| @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface DynamicDataSourChange { String dataSource() default DataSourceType.DATA_SOURCE_SYS; }
|
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
|
public class DataSourceType { public static final String DATA_SOURCE_MASTER = "master"; public static final String DATA_SOURCE_SLAVE = "slave";
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
public static void setDbType(String dbType) { contextHolder.set(dbType); } public static String getDbType() { return contextHolder.get(); } public static void clearDbType() { contextHolder.remove(); } }
|
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| @Aspect @Component @Order(1) public class DynamicDataSourceAspect {
@SuppressWarnings("rawtypes") @Before("@annotation(com.test.DynamicDataSourChange)") public void before(JoinPoint point){ Class<?> className = point.getTarget().getClass(); DynamicDataSourChange dataSourceAnnotation = className.getAnnotation(DynamicDataSourChange.class); if (dataSourceAnnotation != null ) { String methodName = point.getSignature().getName(); Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes(); String dataSource = DataSourceContextHolder.DATA_SOURCE_SYS; try { Method method = className.getMethod(methodName, argClass); if (method.isAnnotationPresent(DynamicDataSourChange.class)) { DynamicDataSourChange annotation = method.getAnnotation(DynamicDataSourChange.class); dataSource = annotation.dataSource(); if (dataSource.equals(DataSourceContextHolder.DATA_SOURCE_MASTER)) { HttpServletRequest request = WebContext.getRequest(); dataSource = dataSource + "_" + request.getAttribute(ChangeId).toString(); } } } catch (Exception e) { e.printStackTrace(); }
DynamicDataSourceContextHolder.poll(); DynamicDataSourceContextHolder.push(dataSource);
}
} @SuppressWarnings("rawtypes") @After("@annotation(com.test.DynamicDataSourChange)") public void after(JoinPoint point){ Class<?> className = point.getTarget().getClass(); DynamicDataSourChange dataSourceAnnotation = className.getAnnotation(DynamicDataSourChange.class); if (dataSourceAnnotation != null) { String methodName = point.getSignature().getName(); Class[] argClass = ((MethodSignature) point.getSignature()).getParameterTypes(); String dataSource = DataSourceContextHolder.DATA_SOURCE_MASTER; try { Method method = className.getMethod(methodName, argClass); if (method.isAnnotationPresent(DynamicDataSourChange.class)) { DynamicDataSourChange annotation = method.getAnnotation(DynamicDataSourChange.class); dataSource = annotation.dataSource(); } } catch (Exception e) { e.printStackTrace(); } if (dataSource != null && !DataSourceContextHolder.DATA_SOURCE_MASTER.equals(dataSource)) { DynamicDataSourceContextHolder.clear(); } } } }
|
四、使用方法
1、加注解,这边建议在 impl 层里面加注解
1 2 3 4 5 6 7 8 9 10 11
| @Service() @DynamicDataSourceAnnotation public class SmartlockServiceImpl implements SmartlockService { @Override @DynamicDataSourceAnnotation(dataSource = DataSourceType.DATA_SOURCE_MASTER) public List getList() throws Exception { ####### 业务代码 ####### }
|
2、手动切换数据源
1 2
| DynamicDataSourceContextHolder.clear(); DynamicDataSourceContextHolder.push("slave");
|