基于spring实现多数据源通过注解切换

 

许多项目都有主库与从库,主库用来写,从库用来读。

那么在开发中,为了方便切库,我们通过可以用注解来标志,标志这个方法是使用主库还是从库

如何实现多数据源切库呢?

主要使用spring的AbstractRoutingDataSource实现,先简单了解下AbstractRoutingDataSource

我们配置的多个数据源会放在AbstractRoutingDataSource的 targetDataSources和defaultTargetDataSource中

AbstractRoutingDataSourcegetConnection方法会先调用determineTargetDataSource()方法返回lookupkey键,根据lookupkey键对不同目标数据源调用。

所以,我们主要通过自己重写determineCurrentLookupKey()方法返回lookupKey即可。

实现多数据源切换的大致思路是:

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

第一步,配置数据源

<bean id="masterDataSource" class="com.alibaba.druid.pool.DruidDataSource">
   <property name="username" value="root" />
   <property name="password" value="spring" />
   <property name="url" value="jdbc:mysql://localhost:3306/taotao?characterEncoding=utf-8" />
   <!-- 最大并发连接数 -->
   <property name="maxActive" value="30" />
   <!-- 最小空闲连接数 -->
   <property name="minIdle" value="5" />
   <!-- 用于显示数据源监控中的sql语句监控 -->
   <property name="filters" value="stat" />
</bean>

<bean id="slaveDataSource" class="com.alibaba.druid.pool.DruidDataSource">
   <property name="username" value="root" />
   <property name="password" value="spring" />
   <property name="url" value="jdbc:mysql://localhost:3306/taobao?characterEncoding=utf-8" />
   <!-- 最大并发连接数 -->
   <property name="maxActive" value="30" />
   <!-- 最小空闲连接数 -->
   <property name="minIdle" value="5" />
   <!-- 用于显示数据源监控中的sql语句监控 -->
   <property name="filters" value="stat" />
</bean>

第二步,定义用来切库的注解,和枚举类

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Dataswitch {
    Datatype value() default Datatype.master;
}
public enum Datatype {
    master("masterDataSource"),slave("slaveDataSource");
    private String value;
    Datatype(String name){
        this.value = name;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

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

public  class DynamicDataSourceHolder {
    private static final ThreadLocal<String> threadLocal = new ThreadLocal<String>();

    public static String getThreadLocal() {
        return threadLocal.get();
    }

    public static void setThreadLocal(String name) {
        threadLocal.set(name);
    }
    public static void clear(){
        threadLocal.remove();
    }
}

第四步,创建AbstactRoutingDataSource的子类,重写determineCurrentLockupKey方法

public class DynamicDataSource extends AbstractRoutingDataSource{
    protected Object determineCurrentLookupKey() {
        System.out.println(DynamicDataSourceHolder.getThreadLocal());
        return DynamicDataSourceHolder.getThreadLocal();
    }
}

第五步,将多数据源配置到用我们创建的DynamicDataSource

<bean id="dynamicDataSource" class="xin.youhuila.sorceswitch.process.DynamicDataSource">
   <property name="targetDataSources">
      <map key-type="java.lang.String">
         <entry key="masterDataSource" value-ref="masterDataSource"></entry>
         <entry key="slaveDataSource" value-ref="slaveDataSource"></entry>
      </map>
   </property>
   <property name="defaultTargetDataSource" ref="masterDataSource"></property>
</bean>

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

@Component
@Aspect
@Order(0)
public class DataSourceAspect {
    @Pointcut("execution (* xin.youhuila.sorceswitch.service..*(..))")
    public void aspect(){

    }
    @Before("aspect()")
    public void before(JoinPoint joinPoint){
        Class<?> clazz = joinPoint.getTarget().getClass();
        Method[] method = clazz.getMethods();
        Dataswitch dataswitch = null;
        boolean is = false;
        for(Method m: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()")
    public void after(){
        DynamicDataSourceHolder.clear();
    }
}

第七步,使用

@Service
public class DemoService {
    @Autowired
    DemoMapper demoMapper;
    @Dataswitch(Datatype.master)
    public void select(){
        List<Demo> d = demoMapper.select();
        for(Demo demo:d){
            System.out.println(demo);
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值