一、添加maven坐標
<!-- aop --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!-- jdbc --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- mybatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency>
二、加入Mybtis配置類(方便測試)
/** * @author zhangboqing * @date 2018/8/3 * * Mybatis配置 */ @Configuration @MapperScan(basePackages = {"com.zbq.springbootdemo.dao"}, sqlSessionFactoryRef = "sqlSessionFactory") //或者直接在Mapper類上面添加注解@Mapper,建議使用上面那種,不然每個mapper加個注解也挺麻煩的 public class MyBatisConfig { }
三、加入多數據源配置
1)修改application.yml添加數據庫配置屬性
spring: datasource: primary: hikari: connection-test-query: SELECT 1 FROM DUAL connection-timeout: 600000 maximum-pool-size: 500 max-lifetime: 1800000 minimum-idle: 20 validation-timeout: 3000 idle-timeout: 60000 connection-init-sql: SET NAMES utf8mb4 jdbc-url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull username: root password: 123 driver-class-name: com.mysql.jdbc.Driver secondary: hikari: connection-test-query: SELECT 1 FROM DUAL connection-timeout: 600000 maximum-pool-size: 500 max-lifetime: 1800000 minimum-idle: 20 validation-timeout: 3000 idle-timeout: 60000 connection-init-sql: SET NAMES utf8mb4 jdbc-url: jdbc:mysql://localhost:3326/test?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull username: root password: 123 driver-class-name: com.mysql.jdbc.Driver
2)添加DataSourceConfig配置類(自定義DataSource數據源)
/** * @author zhangboqing * @date 2019-11-17 */ @Configuration // 自定義數據源一定要排除SpringBoot自動配置數據源,不然會出現循環引用的問題,The dependencies of some of the beans in the application context form a cycle @EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class}) public class DataSourceConfig { @Bean(name = "primary") @ConfigurationProperties(prefix = "spring.datasource.primary.hikari") public DataSource primaryDataSource() { return DataSourceBuilder.create().build(); } @Bean(name = "secondary") @ConfigurationProperties(prefix = "spring.datasource.secondary.hikari") public DataSource secondaryDataSource() { return DataSourceBuilder.create().build(); } /** * 動態數據源 * 通過AOP+注解實現動態切換 * * @return */ @Primary @Bean(name = "dynamicDataSource") public DataSource dataSource() { DynamicDataSourceRouter dynamicDataSource = new DynamicDataSourceRouter(); // 默認數據源 dynamicDataSource.setDefaultTargetDataSource(primaryDataSource()); // 配置多數據源 Map<Object, Object> dataSourceMap = new HashMap(5); dataSourceMap.put("primary", primaryDataSource()); dataSourceMap.put("secondary", secondaryDataSource()); dynamicDataSource.setTargetDataSources(dataSourceMap); return dynamicDataSource; } /** * 配置@Transactional注解事物 * * @return */ @Bean public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(dataSource()); } }
/** * @author zhangboqing * @date 2019-11-17 */ public class DynamicDataSourceRouter extends AbstractRoutingDataSource{ @Override protected Object determineCurrentLookupKey() { return DataSourceNameContextHolder.getDataSourceName(); } @Override public void setLogWriter(PrintWriter pw) throws SQLException { super.setLogWriter(pw); } }
3)定義 @DataSourceName注解(用於指定sql對應的數據源)
/** * @author zhangboqing * @date 2019-11-17 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @Documented public @interface DataSourceName { /** * 指定數據源名稱 * @return dataSourceName */ String value() default "primary"; }
4)定義DataSourceNameContextHolder類(使用ThreadLocal存放當前線程持有的數據源名稱)
/** * @author zhangboqing * @date 2019-11-17 */ @Slf4j public class DataSourceNameContextHolder { private static final ThreadLocal<String> dataSourceNameContextHolder = new NamedThreadLocal<>("DataSourceContext"); /** 默認數據源名稱 */ public static final String DEFAULT_DATASOURCE_NAME = "primary"; public static void setDataSourceName(String dataSourceName) { log.info("切換到[{}]數據源", dataSourceName); dataSourceNameContextHolder.set(dataSourceName); } public static String getDataSourceName() { return dataSourceNameContextHolder.get() != null ? dataSourceNameContextHolder.get() : DEFAULT_DATASOURCE_NAME; } public static void resetDataSourceName() { dataSourceNameContextHolder.remove(); } }
5)定義DynamicDataSourceAspect切面類(通過AOP的方式攔截指定注解實現數據源切換)
/** * @author zhangboqing * @date 2019-11-17 */ @Aspect @Component public class DynamicDataSourceAspect { @Before("@annotation(dataSourceName)") public void beforeSwitchDataSource(DataSourceName dataSourceName){ // 切換數據源 DataSourceNameContextHolder.setDataSourceName(dataSourceName.value()); } @After("@annotation(com.zbq.springbootdemo.config.multidatasource.DataSourceName)") public void afterSwitchDataSource(){ DataSourceNameContextHolder.resetDataSourceName(); } }
四、添加測試
1)在Mybtis配置類指定的包下定義一個Dao類並使用注解指定數據源
/** * @author zhangboqing * @date 2019-11-21 */ @Repository public interface UserDao { @DataSourceName("secondary") @Select("select * from user order by create_time desc limit 1 ") public User getNewstOne(); // 默認是primary,所以可以不指定 // @DataSourceName("primary") @Select("select * from user order by create_time desc limit 1 ") public User getNewstOne2(); }
2)定義測試類執行
/** * @author zhangboqing * @date 2019-11-21 */ @SpringBootTest @Slf4j class UserServiceImplTest { @Autowired private UserDao userDao; @Test void getNewestOne() { User newestOne = userDao.getNewstOne(); User newestOne2 = userDao.getNewstOne2(); log.info(newestOne.toString()); log.info(newestOne2.toString()); } }
3)執行結果可知多數據源生效,同樣的sql查詢結果分別來自於兩個庫
2019-11-21 21:40:51.124 INFO 8202 --- [ main] c.z.s.service.UserServiceImplTest : User(uid=1, phone=2222222222, createTime=null, updateTime=null) 2019-11-21 21:40:51.124 INFO 8202 --- [ main] c.z.s.service.UserServiceImplTest : User(uid=1, phone=1111111111, createTime=null, updateTime=null)