• 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();
        }
    }

    效果:

    如图所示,则动态数据源配置成功

  • 相关阅读:
    java基础(一)
    java概述
    七大查找十大排序之二排序
    bat批处理脚本语言(一)
    photoshop安装与破解
    office——excel常用函数
    arcgis engine开发环境搭建
    七大查找十大排序算法(一)
    华为路由交换常用命令
    cisco路由交换常用命令
  • 原文地址:https://www.cnblogs.com/cxyyh/p/10630544.html
Copyright © 2020-2023  润新知