Spring Boot從入門到精通-mybatis多資料來源
在上一節我們通過Spring Boot集成了mybatis,在某些特定的場景下可能會需要我們使用到多資料來源。本節來介紹Spring Boot整合mybatis多資料來源的一種解決方案。
- 由於我們會用到Spring Boot aop,因此在pom.xml中新增依賴
<!-- springboot-aop包,AOP切面註解,Aspectd等相關注解 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
- 在專案路徑下新建annotation包,自定義註解便於切換資料來源
package com.example.demo.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自定義註解 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface DS { String value() default "datasource1"; }
專案路徑下新建config包
- 新建DataSourceContextHolder.java類,用於儲存本地資料來源
package com.example.demo.config; /** * 儲存本地資料來源 */ public class DataSourceContextHolder { /** * 預設資料來源 */ public static final String DEFAULT_DS = "datasource1"; /** * ThreadLocal之後會進行講解 */ private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); // 設定資料來源名 public static void setDB(String dbType) { System.out.println("切換到{"+dbType+"}資料來源"); contextHolder.set(dbType); } // 獲取資料來源名 public static String getDB() { return (contextHolder.get()); } // 清除資料來源名 public static void clearDB() { contextHolder.remove(); } }
- 新建DynamicDataSource.java類用於獲取本地資料來源
package com.example.demo.config; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * 獲取本地資料來源 */ public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { System.out.println("資料來源為"+DataSourceContextHolder.getDB()); return DataSourceContextHolder.getDB(); } }
- 新建DataSourceConfig.java類讀取配置
package com.example.demo.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; /** * 多資料來源配置類 * */ @Configuration public class DataSourceConfig { //資料來源1 @Bean(name = "datasource1") @ConfigurationProperties(prefix = "spring.datasource.db1") // application.properteis中對應屬性的字首 public DataSource dataSource1() { return DataSourceBuilder.create().build(); } //資料來源2 @Bean(name = "datasource2") @ConfigurationProperties(prefix = "spring.datasource.db2") // application.properteis中對應屬性的字首 public DataSource dataSource2() { return DataSourceBuilder.create().build(); } /** * 動態資料來源: 通過AOP在不同資料來源之間動態切換 * @return */ @Primary @Bean(name = "dynamicDataSource") public DataSource dynamicDataSource() { DynamicDataSource dynamicDataSource = new DynamicDataSource(); // 預設資料來源 dynamicDataSource.setDefaultTargetDataSource(dataSource1()); // 配置多資料來源 Map<Object, Object> dsMap = new HashMap(); dsMap.put("datasource1", dataSource1()); dsMap.put("datasource2", dataSource2()); dynamicDataSource.setTargetDataSources(dsMap); return dynamicDataSource; } /** * 配置@Transactional註解事物 * @return */ @Bean public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(dynamicDataSource()); } }
- 新建DynamicDataSourceAspect.java類用於自定義註解 + AOP的方式實現資料來源動態切換。
package com.example.demo.config; import com.example.demo.annotation.DS; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** * 自定義註解 + AOP的方式實現資料來源動態切換。 * */ @Aspect @Component public class DynamicDataSourceAspect { @Before("@annotation(DS)") public void beforeSwitchDS(JoinPoint point, DS DS){ //獲得當前訪問的class Class<?> className = point.getTarget().getClass(); //獲得訪問的方法名 String methodName = point.getSignature().getName(); //得到方法的引數的型別 Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes(); String dataSource = DataSourceContextHolder.DEFAULT_DS; try { // 得到訪問的方法物件 Method method = className.getMethod(methodName, argClass); // 判斷是否存在@DS註解 if (method.isAnnotationPresent(DS.class)) { DS annotation = method.getAnnotation(DS.class); // 取出註解中的資料來源名 dataSource = annotation.value(); } } catch (Exception e) { e.printStackTrace(); } // 切換資料來源 DataSourceContextHolder.setDB(dataSource); } @After("@annotation(DS)") public void afterSwitchDS(JoinPoint point, DS DS){ DataSourceContextHolder.clearDB(); } }
以上程式碼需要配合application.yml中的配置。yml中資料來源配置如下:
spring: datasource: db1: jdbc-url: jdbc:mysql:/localhost:3306/test1?useUnicode=true&characterEncoding=UTF-8&useSSL=false&failOverReadOnly=false username: test1 password: 123456 type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver db2: jdbc-url: jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=UTF-8&useSSL=false&failOverReadOnly=false username: test2 password: 123456 type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver
-
在啟動類中去掉資料來源的自動配置:
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) - service方法小小的改造一下,在之前的基礎上加上自定義註解,通過註解來動態切換資料來源
@DS("datasource2") public List<User> testMapper () { return userMapper.selectAllUser(); }
啟動專案,呼叫controller中的介面,從控制檯的輸出可以看到資料來源已經實現了動態切換。