Java配置,mysql双数据源 读写分离


大致思路

创建用于标志数据源的自定义注解
通过配置切面,在操作数据库的方法之前,扫描该方法的注解所配置的数据源名称,将名称存储在一个代表当前线程变量工具类
创建AbstarctRoutingDataSource子类DynamicDataSourc,重写determineCurrentLookupKey()方法,把当前线程变量的工具类存储的数据源名称返回即可。
在spring配置文件中,将多个数据源配置到我们创建的DynamicDataSourc

一、配置双数据源 xml配置文件

 master.driver=com.mysql.cj.jdbc.Driver
 master.url=jdbc:mysql://10.70.20.81:3306/test
 master.username=root
 master.password=root
 ​
 slave.driver=com.mysql.cj.jdbc.Driver
 slave.url=jdbc:mysql://10.70.20.155:3306/test
 slave.username=root
 slave.password=root

 <!-- master数据源 -->
     <beanid="masterDataSource"class="com.alibaba.druid.pool.DruidDataSource">
         <propertyname="driverClassName"value="${master.driver}"></property>
         <propertyname="url"value="${master.url}"></property>
         <propertyname="username"value="${master.username}"></property>
         <propertyname="password"value="${master.password}"></property>
     </bean>
     <!-- slave数据源 -->
     <beanid="slaveDataSource"class="com.alibaba.druid.pool.DruidDataSource">
         <propertyname="driverClassName"value="${slave.driver}"></property>
         <propertyname="url"value="${slave.url}"></property>
         <propertyname="username"value="${slave.username}"></property>
         <propertyname="password"value="${slave.password}"></property>
     </bean>

二、定义用来切库的注解和枚举类

注解定义

 importjava.lang.annotation.ElementType;
 importjava.lang.annotation.Retention;
 importjava.lang.annotation.RetentionPolicy;
 importjava.lang.annotation.Target;
 ​
 @Target({ElementType.METHOD})
 @Retention(RetentionPolicy.RUNTIME)
 public@interfaceDataSwitch {
     DataTypevalue() defaultDataType.master;
 }

枚举类

 publicenumDataType {
     master("masterDataSource"),slave("slaveDataSource");
     privateStringvalue;
     DataType(Stringname){
         this.value=name;
     }
 ​
     publicStringgetValue() {
         returnvalue;
     }
 ​
     publicvoidsetValue(Stringvalue) {
         this.value=value;
     }
 }

三、定义一个当前线程的变量的工具类,用于设置对应的数据源名称

 public  classDynamicDataSourceHolder {
     privatestaticfinalThreadLocal<String>threadLocal=newThreadLocal<String>();
 ​
     publicstaticStringgetThreadLocal() {
         returnthreadLocal.get();
     }
 ​
     publicstaticvoidsetThreadLocal(Stringname) {
         threadLocal.set(name);
     }
     publicstaticvoidclear(){
         threadLocal.remove();
     }
 }

四、创建AbstractRoutingDataSource的子类,并重写determineCurrentLockupKey方法

 importorg.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
 ​
 publicclassDynamicDataSourceextendsAbstractRoutingDataSource {
     protectedObjectdetermineCurrentLookupKey() {
 ​
         System.out.println("------------>"+DynamicDataSourceHolder.getThreadLocal());
         returnDynamicDataSourceHolder.getThreadLocal();
     }
 }

五、将多数据源配置到我们创建的DynamicDataSource下

 <beanid="dynamicDataSource"class="com.shen.datasource.DynamicDataSource">
     <propertyname="targetDataSources">
         <mapkey-type="java.lang.String">
             <entrykey="masterDataSource"value-ref="masterDataSource"></entry>
             <entrykey="slaveDataSource"value-ref="slaveDataSource"></entry>
         </map>
     </property>
     <propertyname="defaultTargetDataSource"ref="masterDataSource"></property>
 </bean>

六、配置切面类,在操作数据库方法之前,获取注解配置的数据源名称并返回

 importcom.shen.datasource.DataSwitch;
 importcom.shen.datasource.DataType;
 importcom.shen.datasource.DynamicDataSourceHolder;
 importorg.aspectj.lang.JoinPoint;
 importorg.aspectj.lang.annotation.After;
 importorg.aspectj.lang.annotation.Aspect;
 importorg.aspectj.lang.annotation.Before;
 importorg.aspectj.lang.annotation.Pointcut;
 importorg.springframework.core.annotation.Order;
 importorg.springframework.stereotype.Component;
 ​
 importjava.lang.reflect.Method;
 ​
 @Component
 @Aspect
 @Order(0)
 publicclassDataSourceAspect {
     @Pointcut("execution (* com.shen.service..*(..))")
     publicvoidaspect(){
 ​
     }
     @Before("aspect()")
     publicvoidbefore(JoinPointjoinPoint){
         Class<?>clazz=joinPoint.getTarget().getClass();
         Method[] method=clazz.getMethods();
         DataSwitchdataSwitch=null;
         booleanis=false;
         for(Methodm:method){
             if(m.isAnnotationPresent(DataSwitch.class)){
                 dataSwitch=m.getAnnotation(DataSwitch.class);
                 DynamicDataSourceHolder.setThreadLocal(dataSwitch.value().getValue());
                 is=true;
             }
         }
         if(!is){
             DynamicDataSourceHolder.setThreadLocal(DataType.master.getValue());
         }
     }
     @After("aspect()")
     publicvoidafter(){
         DynamicDataSourceHolder.clear();
     }
 }

七、测试

 @Service
 publicclassUserService {
 ​
     @Autowired
     privateUserMapperuserMapper;
 ​
     @DataSwitch(DataType.master)
     publicIntegerinsertUser(Useruser){
         System.out.println("service--------------------");
         returnuserMapper.insertSelective(user);
     }
 ​
     @DataSwitch(DataType.slave)
     publicList<User>queryAllUsers(){
         returnuserMapper.selectByExample(null);
     }
 }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Spring Boot中实现读写分离配置可以通过使用多数据源和动态数据源的方式来实现。下面是一种常见的配置方式: 1. 配置多个数据源:在`application.properties`或`application.yml`文件中配置多个数据源的连接信息,例如: ``` spring.datasource.master.url=jdbc:mysql://localhost:3306/masterdb spring.datasource.master.username=root spring.datasource.master.password=123456 spring.datasource.slave.url=jdbc:mysql://localhost:3306/slavedb spring.datasource.slave.username=root spring.datasource.slave.password=123456 ``` 2. 创建数据源配置类:创建一个配置类,用于配置多个数据源,并将其注入到Spring容器中,例如: ```java @Configuration public class DataSourceConfig { @Bean(name = "masterDataSource") @ConfigurationProperties(prefix = "spring.datasource.master") public DataSource masterDataSource() { return DataSourceBuilder.create().build(); } @Bean(name = "slaveDataSource") @ConfigurationProperties(prefix = "spring.datasource.slave") public DataSource slaveDataSource() { return DataSourceBuilder.create().build(); } } ``` 3. 创建动态数据源:创建一个动态数据源类,用于根据需要选择使用哪个数据源,例如: ```java public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSourceType(); } } ``` 4. 配置动态数据源:在配置类中配置动态数据源,并将多个数据源添加到动态数据源中,例如: ```java @Configuration public class DynamicDataSourceConfig { @Autowired @Qualifier("masterDataSource") private DataSource masterDataSource; @Autowired @Qualifier("slaveDataSource") private DataSource slaveDataSource; @Bean public DynamicDataSource dynamicDataSource() { DynamicDataSource dynamicDataSource = new DynamicDataSource(); Map<Object, Object> dataSourceMap = new HashMap<>(); dataSourceMap.put("master", masterDataSource); dataSourceMap.put("slave", slaveDataSource); dynamicDataSource.setTargetDataSources(dataSourceMap); dynamicDataSource.setDefaultTargetDataSource(masterDataSource); return dynamicDataSource; } @Bean public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource) throws Exception { SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean(); sessionFactoryBean.setDataSource(dynamicDataSource); return sessionFactoryBean.getObject(); } } ``` 5. 配置数据源切换:创建一个数据源上下文类,用于在不同的线程中切换数据源,例如: ```java public class DataSourceContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); public static void setDataSourceType(String dataSourceType) { contextHolder.set(dataSourceType); } public static String getDataSourceType() { return contextHolder.get(); } public static void clearDataSourceType() { contextHolder.remove(); } } ``` 6. 使用注解切换数据源:在需要切换数据源的方法上使用`@DataSource`注解,例如: ```java @Service public class UserService { @Autowired private UserMapper userMapper; @DataSource("master") public User getMasterUserById(Long id) { return userMapper.getById(id); } @DataSource("slave") public User getSlaveUserById(Long id) { return userMapper.getById(id); } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值