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