1. 程式人生 > >SpringBoot 動態資料來源 用註解切換

SpringBoot 動態資料來源 用註解切換

使用版本SpringBoot 1.5.9

動態切換資料來源,mysql ,oracle 在專案中動態切換,或者 兩個mysql進行切換

引入依賴

<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>druid-spring-boot-starter</artifactId>
	<version>1.1.6</version>
</dependency>
<dependency>
	<groupId>org.mybatis.spring.boot</groupId>
	<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

配置程式碼


@Component
public class DatabaseContext {
    //建立 ThreadLocal 用餘存儲唯一的執行緒變數
    private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<>();

    //新增
    public static void setDatabaseType(DatabaseType type) {
        contextHolder.set(type);
    }
    //獲取
    public static DatabaseType getDatabaseType() {
        return contextHolder.get();
    }
    //刪除   
    /*
     *注意使用完成之後 需要刪除執行緒變數 
     *ThreadLocal不會自動清理
     */
    public static void clearDBKey() { contextHolder.remove(); }

}

//定義一個列舉對應名稱
public enum DatabaseType {
    mysql1,mysql2
}

//自定義註解
@Target({ ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TargetDataSource {
    DatabaseType value();
}

//配置類

@Configuration
@MapperScan("xxxx")//包名
@EnableTransactionManagement
public class DataSourceConfig {

    @Resource
    private DataSourceProperties dataSourceProperties;

    private static Logger logger = LoggerFactory.getLogger(DataSourceConfig.class);

    @Autowired
    private Environment env;

    /**
     *  構造多資料來源連線池
     */
    @Bean
    @Primary
    public DynamicDataSource dynamicDataSource() throws SQLException{

        Map<Object,Object> druidDataSourceMap = new ConcurrentHashMap<>();
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql:///test1");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUsername("root");
        dataSource.setPassword("xxxxx");
        
        DruidDataSource dataSource2 = new DruidDataSource();
        dataSource2.setUrl("jdbc:mysql:///test2");
        dataSource2.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource2.setUsername("root");
        dataSource2.setPassword("xxxxx");
        
        //key 值對應enum裡的名稱
        druidDataSourceMap.put(DatabaseType.mysql1,dataSource);
        druidDataSourceMap.put(DatabaseType.mysql2,dataSource2);        

        DynamicDataSource dataSource = new DynamicDataSource();
        // 該方法是AbstractRoutingDataSource的方法
        dataSource.setTargetDataSources(druidDataSourceMap);
        //需要用 DatabaseType 型別獲取
        DruidDataSource druidDataSource = (DruidDataSource) druidDataSourceMap.get(DatabaseType.valueOf(defaultsKys));
        logger.info("load defaults : " + defaultsKys);
        // 預設使用的datasource
        dataSource.setDefaultTargetDataSource(druidDataSource);
        return dataSource;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource) throws Exception {
        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dynamicDataSource);
        sessionFactoryBean.setTypeAliasesPackage(env.getProperty("mybatis.type-aliases-package"));
        sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapper-locations")));
        return sessionFactoryBean.getObject();
    }

    @Bean
    public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception {
        return new DataSourceTransactionManager(dataSource);
    }

}

 AOP實現切換

@Aspect
@Order(-10)
@Component
public class DynamicDataSourceAspect {

    private static Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);

    @Pointcut("@annotation(targetDataSource)")
    public void pointCut(TargetDataSource targetDataSource) {}

    /*
     *
     * @Before:在方法執行之前進行執行:
     * @annotation(targetDataSource):
     * 會攔截註解targetDataSource的方法,否則不攔截;
     */
    @Before("pointCut(targetDataSource)")
    public void doBefore(JoinPoint point,TargetDataSource targetDataSource){
        //獲取當前的指定的資料來源;
        DatabaseType value = targetDataSource.value();
        //如果不在會使用預設的。
        if (targetDataSource.value()==null){
            //找到的話,那麼設定到動態資料來源上下文中。
            DatabaseContext.setDatabaseType(targetDataSource.value());
        }
    }

    /**
     * 清理
     */
    @After("pointCut(targetDataSource)")
    public void after(TargetDataSource targetDataSource){
         DatabaseContextHolder.clearDBKey();
    }

}

 測試


@RestController
@RequestMapping("/test")
public class TestController {

    //在訪問方法上加入註解即可

    @TargetDataSource(DatabaseType.mysql1)
    @GetMapping("/test1")
    public String test1(){
        //會呼叫 test1資料庫
    }
    
    @TargetDataSource(DatabaseType.mysql2)
    @GetMapping("/test2")
    public String test2(){
        //會呼叫 test2資料庫
    }
}

以上就是了