MyBatis SpringBoot2.0 数据库读写分离

1、自定义DataSource

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

/**
 * @Description 动态数据源
 * AbstractRoutingDataSource(每执行一次数据库,动态获取DataSource)
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}

2、数据源切换器

import java.util.ArrayList;
import java.util.List;

/**
 * @Description 动态数据源上下文管理
 */
public class DynamicDataSourceContextHolder {

    //存放当前线程使用的数据源类型信息
    private static final ThreadLocal<Object> contextHolder = new ThreadLocal<>();
    //存放数据源id
    public static List<Object> dataSourceIds = new ArrayList<>();
    
    
    //当从库数据源大于1个时,可以配置轮询方式
    public static List<Object> slaveDataSourceKeys = new ArrayList<>();


    //设置数据源
    public static void setDataSourceType(String dataSourceType) {
            if(dataSourceIds.contains(dataSourceType)) {
                contextHolder.set(dataSourceType);
            }
    }

    //获取数据源
    public static Object getDataSourceType() {
        return contextHolder.get();
    }

    //清除数据源
    public static void clearDataSourceType() {
        contextHolder.remove();
    }
}

3、代理类事物切换数据源

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.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

/**
 * @Description 动态数据源通知
 */
@Aspect
@Order(-1)//保证在@Transactional之前执行
@Component
public class DynamicDattaSourceAspect {

    //改变数据源,方法上存在事物的注解,则走主库
    @Before("@annotation(transactional)")
    public void changeDataSource(JoinPoint joinPoint, Transactional transactional) {
           DynamicDataSourceContextHolder.setDataSourceType("master");
    }

    @After("@annotation(transactional)")
    public void clearDataSource(JoinPoint joinPoint, Transactional transactional) {
        DynamicDataSourceContextHolder.clearDataSourceType();
    }
}

4、数据源Bean注册器

import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.flyway.FlywayDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import tk.mybatis.spring.annotation.MapperScan;

/**
 * @Description 注册动态数据源
 * 初始化数据源和提供了执行动态切换数据源的工具类
 */
@Configuration
@MapperScan(basePackages="com.xxxx.*.mapper")
public class DynamicDataSourceRegister{
    protected Logger logger = LoggerFactory.getLogger(getClass());


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


    @Bean
    @Primary
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dynamicDataSource());
        PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
        sessionFactory.setMapperLocations(pathMatchingResourcePatternResolver.getResources("classpath*:mapper/*Mapper.xml"));
        Resource resource = pathMatchingResourcePatternResolver.getResource("classpath:mybatis-setting.xml");
        sessionFactory.setConfigLocation(resource);
        return sessionFactory.getObject();
    }

    @FlywayDataSource//指定主库为flyway的数据源
    @Bean(name = "masterDataSource")
    @ConfigurationProperties(prefix = "master.datasource")
    public DataSource masterDataSource(){
        return DataSourceBuilder.create().type(dataSourceType).build();
    }

    @Bean(name = "slaveDataSource")
    @ConfigurationProperties(prefix = "slave.datasource")
    public DataSource slaveDataSource(){
        return DataSourceBuilder.create().type(dataSourceType).build();
    }

    @Bean("dynamicDataSource")
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicRoutingDataSource = new DynamicDataSource();
        Map<Object, Object> dataSourceMap = new HashMap<>(4);
        dataSourceMap.put("master", masterDataSource());
        dataSourceMap.put("slaveDataSource", slaveDataSource());

        // 将 slave 数据源作为默认指定的数据源
        dynamicRoutingDataSource.setDefaultTargetDataSource(slaveDataSource());
        // 将 master 和 slave 数据源作为指定的数据源
        dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);

        // 将数据源的 key 放到数据源上下文的 key 集合中,用于切换时判断数据源是否有效
        DynamicDataSourceContextHolder.dataSourceIds.addAll(dataSourceMap.keySet());

        // 将 Slave 数据源的 key 放在集合中,用于轮循
        DynamicDataSourceContextHolder.slaveDataSourceKeys.addAll(dataSourceMap.keySet());
        DynamicDataSourceContextHolder.slaveDataSourceKeys.remove("master");
        return dynamicRoutingDataSource;
    } 
    
    @Bean
    public PlatformTransactionManager transactionManager() {
        
        return new DataSourceTransactionManager(dynamicDataSource());
    }
    

}

猜你喜欢

转载自www.cnblogs.com/binz/p/10452288.html