springboot 多源数据库切换

利用aop注解实现多源数据库切换,具体步骤如下:

1、数据源切换工具类 ThreadLocal

public class DynamicDataSourceContextHolder {


    /**
     * 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
     * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
     */
    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();
    }

}

2、动态数据源类

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * @author 
 * 动态数据源
 **/
public class DynamicDataSource extends AbstractRoutingDataSource {

    /**
     * 代码中的determineCurrentLookupKey方法取得一个字符串,
     * 该字符串将与配置文件中的相应字符串进行匹配以定位数据源,配置文件,
     * 即applicationContext.xml文件中需要要如下代码:(non-Javadoc)
     *
     * @see AbstractRoutingDataSource#determineCurrentLookupKey()
     *
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        /*
        * DynamicDataSourceContextHolder代码中使用setDataSourceType
        * 设置当前的数据源,在路由类中使用getDataSourceType进行获取,
        *  交给AbstractRoutingDataSource进行注入使用。
        */
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}

3、切换数据源的注解

import java.lang.annotation.*;

/**
 * @author 
 **/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {

    String value();

}

4、处理数据源切换的AOP切面

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @author 
 * 切换数据源Advice
 **/
@Aspect
@Order(-10) //保证该aop在@Transaction之前执行
@Component
public class DynamicDataSourceAspect {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * * @Before("@annotation(ds)")
     * 的意思是:@Before:在方法执行之前进行执行; @annotation(targetDataSource):会拦截注解targetDataSource的方法,否则不拦截;
     * @param point
     * @param targetDataSource
     */
    @Before("@annotation(targetDataSource)")
    public void changeDataSource(JoinPoint point, TargetDataSource targetDataSource){
        //获取当前的指定数据源
        String dsID = targetDataSource.value();
        DynamicDataSourceContextHolder.setDataSourceType(dsID);

    }


    @After("@annotation(targetDataSource)")
    public void restoreDataSource(JoinPoint point, TargetDataSource targetDataSource){
        //方法执行完毕后,销毁当前数据源信息,进行垃圾回收
        DynamicDataSourceContextHolder.clearDataSourceType();
    }
}

5、数据源配置

import com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean;
import io.shardingsphere.api.config.rule.ShardingRuleConfiguration;
import io.shardingsphere.api.config.rule.TableRuleConfiguration;
import io.shardingsphere.api.config.strategy.InlineShardingStrategyConfiguration;
import io.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
import org.apache.ibatis.datasource.pooled.PooledDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import sf.myboot.dynamicdata.DynamicDataSource;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

@Configuration
public class ShardingJDBCConfiguration  {

    //数据源1
    @Bean(name ="ds_0")
    public DataSource dataSource0(){
        PooledDataSource dataSource = new PooledDataSource();
        try{
            dataSource.setUsername("root");
            dataSource.setPassword("");
            dataSource.setDriver("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://localhost:3306/mytest?serverTimezone=UTC");
        } catch (Exception e){

        }
        return dataSource;
    }

    //数据源2
    @Bean(name ="ds_1")
    public DataSource dataSource1(){
        PooledDataSource dataSource = new PooledDataSource();
        try{
            dataSource.setUsername("root");
            dataSource.setPassword("");
            dataSource.setDriver("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://localhost:3306/mytest2?serverTimezone=UTC");
        } catch (Exception e){

        }
        return dataSource;
    }


    //对数据源1实现分表设置
    @Bean(name="shardingDataSource")
    public DataSource getDataSource(@Qualifier("ds_0") DataSource ds_0) throws SQLException {
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        //分表策略
        shardingRuleConfig.getTableRuleConfigs().add(remarkRuleConfig());
//        shardingRuleConfig.getTableRuleConfigs().add(taskRuleConfig());
        //分库设置
        Map<String, DataSource> dataSourceMap = new HashMap<>();
        dataSourceMap.put("ds_0", ds_0);
        //设置默认的数据源
//        shardingRuleConfig.setDefaultDataSourceName("ds_0");
        Properties properties = new Properties();
        properties.setProperty("sql.show", Boolean.TRUE.toString());
        return ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig, new ConcurrentHashMap<>(), properties);
    }

    private TableRuleConfiguration remarkRuleConfig() {
        // 配置表规则
        TableRuleConfiguration tableRuleConfig = new TableRuleConfiguration();
        tableRuleConfig.setLogicTable("logtable");
        tableRuleConfig.setActualDataNodes("ds_0.logtable_${1..2}");
//        tableRuleConfig.setKeyGeneratorColumnName("id");
        // 配置分表策略
        tableRuleConfig.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("remark", "logtable_${remark % 2}"));
        return tableRuleConfig;
    }

    //加载所有数据源
    @Bean
    @Primary
    public DynamicDataSource dataSource(
            @Qualifier("shardingDataSource")DataSource shardingDataSource,
            @Qualifier("ds_1")DataSource ds1) {
        Map<Object, Object> targetDataSource=new HashMap<Object, Object>();
        targetDataSource.put("sharding",shardingDataSource);
        targetDataSource.put("ds1",ds1);

        DynamicDataSource dynamicDataSource=new DynamicDataSource();
        dynamicDataSource.setTargetDataSources(targetDataSource);
        dynamicDataSource.setDefaultTargetDataSource(shardingDataSource);

        return dynamicDataSource;
    }
    
    //多源数据与Mybatis plus配置
    @Bean(name = "shardSqlSessionFactory")
    public MybatisSqlSessionFactoryBean shardSqlSessionFactory(@Qualifier("shardingDataSource") DataSource dataSource,@Qualifier("ds_1") DataSource ds1) throws Exception {
        MybatisSqlSessionFactoryBean  bean = new MybatisSqlSessionFactoryBean ();//MybatisSqlSessionFactoryBean为Mybatis Plus的bean
        bean.setDataSource(this.dataSource(dataSource,ds1));
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml"));
        return bean;
    }

}

6、使用注解动态切换数据源

@Service
public class StudentServiceImpl extends ServiceImpl<StudentMapper,Student> implements StudentService {


    @Override
    @TargetDataSource("ds1")
    public Student getStudent(Long id) {
        Wrapper<Student> wrapper=new EntityWrapper<>();
        wrapper.eq("id",id);
        return  selectOne(wrapper);
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值