SpringBoot動態數據源


1、原理圖

 

2、創建枚舉類

/** * 存數據源key值 */
public enum DataSourceKey { master,salve,migration }

3、創建自定義注解類

/** * 自定義注解 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface DBSource
{ String value() default "master"; }

 4、切換數據源類

/** * @author yehui * 根據線程動態切換數據源 */ @Configuration public class DynamicDataSourceContextHolder { private static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class); /** * 設置默認數據源 */
    public static  String DEFAULT_DS = "master"; /** *用於輪訓計數 */
    private static int counter = 0; /* * 當使用ThreadLocal維護變量時,ThreadLocal為每個使用該變量的線程提供獨立的變量副本, * 所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。 */
    private static final ThreadLocal<String> contextHolder = ThreadLocal.withInitial(() -> DataSourceKey.master.name()); /** *用於在切換數據源時保證不會被其他線程修改 */
    public static Lock lock = new ReentrantLock(); /** * 設置數據源 */
    public static void setDB(String dbType){ log.info("切換到{" + dbType + "}數據源"); contextHolder.set(dbType); } /** * 得到數據源 * */
    public static String getDB(){ return contextHolder.get(); } /** * 使用主數據源 */
    public static void useMasterDataSource() { contextHolder.set(DataSourceKey.master.name()); } /** * 移除數據源 */
    public static void removeDB(){ contextHolder.remove(); } /** * The constant slaveDataSourceKeys. */
    public static List<Object> slaveDataSourceKeys = new ArrayList<>(); /** * 當使用只讀數據源時通過輪循方式選擇要使用的數據源 */
    public static String getSlaveDB(){ lock.lock(); try { int datasourceKeyIndex = counter % slaveDataSourceKeys.size(); counter++; return String.valueOf(slaveDataSourceKeys.get(datasourceKeyIndex)); } catch (Exception e) { log.error(e.getMessage(), e); e.printStackTrace(); return "master"; } finally { lock.unlock(); } } }

5、獲取數據源類

/** * @author yehui * 多數據源的選擇 */
public class DynamicDataSource extends AbstractRoutingDataSource { private static final Logger log = LoggerFactory.getLogger(DynamicDataSource.class); @Override protected Object determineCurrentLookupKey() { log.info("Current DataSource is " + DynamicDataSourceContextHolder.getDB()); return DynamicDataSourceContextHolder.getDB(); } }

6、Aop類

/** * @author yehui * 自定義注解 + AOP的方式實現數據源動態切換。 */ @Aspect @Component public class DynamicDataSourceAspect { private static final Logger log = LoggerFactory.getLogger(DynamicDataSourceAspect.class); @Before("@annotation(DBSource)") public void beforeSwitchDB(JoinPoint joinPoint,DBSource DBSource){ //獲取目標類的方法
        Class<?> aClass = joinPoint.getTarget().getClass(); //獲得訪問的方法名
        String methodName = joinPoint.getSignature().getName(); //得到方法的參數類型
        Class[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes(); String dataSource = DynamicDataSourceContextHolder.DEFAULT_DS; try { Method method = aClass.getMethod(methodName, parameterTypes); if(method.isAnnotationPresent(DBSource.class)){ DBSource db = method.getAnnotation(DBSource.class); //指定數據源
                dataSource = db.value(); }else{ //輪訓設置數據源
                dataSource = DynamicDataSourceContextHolder.getSlaveDB(); } } catch (NoSuchMethodException e) { log.error(e.getMessage(), e); } //設置數據源
 DynamicDataSourceContextHolder.setDB(dataSource); } @After("@annotation(DBSource)") public void afterSwitchDB(DBSource DBSource){ DynamicDataSourceContextHolder.removeDB(); } }

6、application.properties文件

spring.druid.datasource.slave.password=root spring.druid.datasource.slave.username=root spring.druid.datasource.slave.jdbc-url=jdbc:mysql://localhost:3306/study
spring.druid.datasource.slave.driver-class-name=com.mysql.jdbc.Driver spring.druid.datasource.master.password=root spring.druid.datasource.master.username=root spring.druid.datasource.master.jdbc-url=jdbc:mysql://localhost:3306/study01
spring.druid.datasource.master.driver-class-name=com.mysql.jdbc.Driver spring.druid.datasource.migration.password=root spring.druid.datasource.migration.username=root #2.0版本多數據源必須是使用jdbc-url 不能使用url,否則報錯 jdbcUrl is required with driverClassName spring.druid.datasource.migration.jdbc-url=jdbc:mysql://localhost:3306/study02
spring.druid.datasource.migration.driver-class-name=com.mysql.jdbc.Driver

7、數據源配置類

/** * @author yehui * 數據源配置類 */ @Configuration public class DataSourceConfig { /** * 主數據 * * @return data source */ @Bean("master") @Primary @ConfigurationProperties(prefix = "spring.druid.datasource.master") public DataSource master() { return DataSourceBuilder.create().build(); } /** * 從數據庫 * * @return data source */ @Bean("slave") @ConfigurationProperties(prefix ="spring.druid.datasource.slave") public DataSource slave() { return DataSourceBuilder.create().build(); } /** * 從數據庫 * * @return data source */ @Bean("migration") @ConfigurationProperties(prefix ="spring.druid.datasource.migration") public DataSource migration() { return DataSourceBuilder.create().build(); } /** * 配置動態數據源 * * @return
     */ @Bean("dynamicDataSource") public DataSource dynamicDataSource() { DynamicDataSource dynamicRoutingDataSource = new DynamicDataSource(); Map<Object, Object> dataSourceMap = new HashMap<>(4); dataSourceMap.put(DataSourceKey.master.name(), master()); dataSourceMap.put(DataSourceKey.salve.name(), slave()); dataSourceMap.put(DataSourceKey.master.name(), slave()); //設置默認的數據源
 dynamicRoutingDataSource.setDefaultTargetDataSource(master()); // 多個slave數據源在此添加,自定義key,用於輪詢
        dataSourceMap.put(DataSourceKey.salve.name() + "1", slave()); //設置目標數據源
 dynamicRoutingDataSource.setTargetDataSources(dataSourceMap); //將數據源的key放在集合中判斷是否正常
 DynamicDataSourceContextHolder.slaveDataSourceKeys.addAll(dataSourceMap.keySet()); //實現負載均衡算法 將 Slave 數據源的 key 放在集合中,用於輪循
 DynamicDataSourceContextHolder.slaveDataSourceKeys.addAll(dataSourceMap.keySet()); DynamicDataSourceContextHolder.slaveDataSourceKeys.remove(DataSourceKey.migration.name()); return dynamicRoutingDataSource; } /** * 設置工廠類 */ @Bean public SqlSessionFactoryBean sqlSessionFactoryBean() { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dynamicDataSource()); //此處設置為了解決找不到mapper文件的問題
        try { sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*Mapper.xml")); } catch (IOException e) { e.printStackTrace(); } return sqlSessionFactoryBean; } /** * 事物管理器 */ @Bean("transactionManager") public DataSourceTransactionManager transactionManager() { return new DataSourceTransactionManager(dynamicDataSource()); } }

8、啟動類

/** * springboot入口類,此類需要在所有用到的package上層 exclude = * {DataSourceAutoConfiguration.class} * 禁用springboot默認加載的application.properties單數據源配置 */ @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) public class StartApp { public static void main(String[] args) { SpringApplication.run(StartApp.class); } }

9、測試

mapper接口

@Mapper public interface UserDataSourceMapper { public List<TbUser> findUser(); }

 

mapper文件

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yehui.mapper.UserDataSourceMapper">
    <select id="findUser" resultType="com.yehui.entity.TbUser"> select * from tb_user </select>
</mapper>

 service類

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDataSourceMapper userMapper;

    @Override
    @DBSource("slave")//使用數據源打上注解即可
    public List<TbUser> findUser() {
        return userMapper.findUser();
    }
}

controller類

@RestController public class UserController { @Autowired private UserService userService; @RequestMapping("/findUser") public List<TbUser> findUser(){ return userService.findUser(); } }

效果:

如圖所示,則動態數據源配置成功

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM