Spring Boot+MyBatis实现多数据源方法2-动态方式

这种方式实现了一个写库多个读库,使用的是同一套Mapper接口和XML文件,这样就有很好的拓展性,具体代码如下:

先是生成不同的数据源,其中多个读数据源合并:

@Configuration  
public class DataBaseConfiguration{  


    @Value("${spring.datasource.type}")  
    private Class<? extends DataSource> dataSourceType;  


    @Bean(name="writeDataSource", destroyMethod = "close", initMethod="init")  
    @Primary  
    @ConfigurationProperties(prefix = "spring.write.datasource")  
    public DataSource writeDataSource() {  
        return DataSourceBuilder.create().type(dataSourceType).build();  
    }  
    /** 
     * 有多少个从库就要配置多少个 
     * @return 
     */  
    @Bean(name = "readDataSourceOne")  
    @ConfigurationProperties(prefix = "spring.read.one")  
    public DataSource readDataSourceOne(){  
        return DataSourceBuilder.create().type(dataSourceType).build();  
    }  


    @Bean(name = "readDataSourceTwo")  
    @ConfigurationProperties(prefix = "spring.read.two")  
    public DataSource readDataSourceTwo() {  
        return DataSourceBuilder.create().type(dataSourceType).build();  
    }  


    @Bean("readDataSources")  
    public List<DataSource> readDataSources(){  
        List<DataSource> dataSources=new ArrayList<DataSource>();  
        dataSources.add(readDataSourceOne());  
        dataSources.add(readDataSourceTwo());  
        return dataSources;  
    }  
}  

生成一套SqlSessionFactory,进行动态切换

@Configuration  
@ConditionalOnClass({EnableTransactionManagement.class})  
@Import({ DataBaseConfiguration.class})  
@MapperScan(basePackages={"com.hui.readwrite.mapper.master1"})  
public class TxxsbatisConfiguration {  


    @Value("${spring.datasource.type}")  
    private Class<? extends DataSource> dataSourceType;  


    @Value("${datasource.readSize}")  
    private String dataSourceSize;  


    @Resource(name = "writeDataSource")  
    private DataSource dataSource;  


    @Resource(name = "readDataSources")  
    private List<DataSource> readDataSources;  


    @Bean  
    @ConditionalOnMissingBean  
    public SqlSessionFactory sqlSessionFactory() throws Exception {  
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();  
        sqlSessionFactoryBean.setDataSource(roundRobinDataSouceProxy());  
        sqlSessionFactoryBean.setTypeAliasesPackage("com.hui.readwrite.po");  
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()  
                .getResources("classpath:mapper/master1*//*.xml"));  
        sqlSessionFactoryBean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);  
        return sqlSessionFactoryBean.getObject();  
    }  
    /**  
     * 有多少个数据源就要配置多少个bean  
     * @return  
     */  
    @Bean  
    public AbstractRoutingDataSource roundRobinDataSouceProxy() {  
        int size = Integer.parseInt(dataSourceSize);  
        TxxsAbstractRoutingDataSource proxy = new TxxsAbstractRoutingDataSource(size);  
        Map<Object, Object> targetDataSources = new HashMap<Object, Object>();  
        targetDataSources.put(DataSourceType.write.getType(),dataSource);  
        for (int i = 0; i < size; i++) {  
            targetDataSources.put(i, readDataSources.get(i));  
        }  
        proxy.setDefaultTargetDataSource(dataSource);  
        proxy.setTargetDataSources(targetDataSources);  
        return proxy;  
    }  
}  

进行选择,和读库的简单负载。Spring boot提供了AbstractRoutingDataSource根据用户定义的规则选择当前的数据库,这样我们可以在执行查询之前,设置读取从库,在执行完成后,恢复到主库。实现可动态路由的数据源,在每次数据库查询操作前执行

public class TxxsAbstractRoutingDataSource extends AbstractRoutingDataSource {  


    private final int dataSourceNumber;  
    private AtomicInteger count = new AtomicInteger(0);  


    public TxxsAbstractRoutingDataSource(int dataSourceNumber) {  
        this.dataSourceNumber = dataSourceNumber;  
    }  


    @Override  
    protected Object determineCurrentLookupKey() {  
        String typeKey = DataSourceContextHolder.getJdbcType();  
        if (typeKey.equals(DataSourceType.write.getType()))  
            return DataSourceType.write.getType();  
        // 读 简单负载均衡  
        int number = count.getAndAdd(1);  
        int lookupKey = number % dataSourceNumber;  
        return new Integer(lookupKey);  
    }  
}  

利用AOP的方式实现,方法的控制

@Aspect  
@Component  
public class DataSourceAop {  


    public static final Logger logger = LoggerFactory.getLogger(DataSourceAop.class);  


    @Before("execution(* com.hui.readwrite.mapper..*.select*(..)) || execution(* com.hui.readwrite.mapper..*.get*(..))")  
    public void setReadDataSourceType() {  
        DataSourceContextHolder.read();  
        logger.info("dataSource切换到:Read");  
    }  


    @Before("execution(* com.hui.readwrite.mapper..*.insert*(..)) || execution(* com.hui.readwrite.mapper..*.update*(..))")  
    public void setWriteDataSourceType() {  
        DataSourceContextHolder.write();  
        logger.info("dataSource切换到:write");  
    }  
}  

DataSourceContextHolder类:

public class DataSourceContextHolder {

    private static final Logger logger = LoggerFactory.getLogger(DataSourceContextHolder.class);


    public static final String DATA_SOURCE_WRITE = "WRITE";
    public static final String DATA_SOURCE_READ = "READ";

    //线程本地环境
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();


    /**
     * 绑定当前线程数据源路由的key
     * 使用完成后必须调用removeRouteKey()方法删除
     */
    public static void read(){
        if(logger.isDebugEnabled())
            logger.debug("======切换数据源,类型:"+type+"=======");
        contextHolder.set(DataSourceContextHolder.DATA_SOURCE_READ);
    }

        /**
     * 绑定当前线程数据源路由的key
     * 使用完成后必须调用removeRouteKey()方法删除
     */
    public static void write(){
        if(logger.isDebugEnabled())
            logger.debug("======切换数据源,类型:"+type+"=======");
        contextHolder.set(DataSourceContextHolder.DATA_SOURCE_WRITE);
    }

    /**
     * 获取当前线程的数据源路由的key
     */
    public static String getType(){
       return  contextHolder.get();
    }

    /**
     * 删除与当前线程绑定的数据源路由的key
     */
    public static void removeType(){
        contextHolder.remove();
    }
}

配置文件:

#一些总的配置文件  
spring.aop.auto=true  
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource  
datasource.readSize=2  

# 主数据源,默认的  
spring.write.datasource.driverClassName=com.mysql.jdbc.Driver  
spring.write.datasource.url=jdbc:mysql://localhost:3306/learn?useUnicode=true&characterEncoding=utf8  
spring.write.datasource.username=root  
spring.write.datasource.password=root  

# 从数据源  
spring.read.one.driverClassName=com.mysql.jdbc.Driver  
spring.read.one.url=jdbc:mysql://localhost:3307/learn?useUnicode=true&characterEncoding=utf8  
spring.read.one.username=root  
spring.read.one.password=root  


spring.read.two.driverClassName=com.mysql.jdbc.Driver  
spring.read.two.url=jdbc:mysql://localhost:3308/learn?useUnicode=true&characterEncoding=utf8  
spring.read.two.username=root  
spring.read.two.password=root  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值