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()); } }
6、资源文件配置
#datasource master datasource.type=com.alibaba.druid.pool.DruidDataSource #master master.datasource.url=jdbc:mysql://localhost:3306/haogonge_dev?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false master.datasource.username=root master.datasource.password=root master.datasource.driver-class-name=com.mysql.jdbc.Driver master.datasource.pool.initialSize=3 master.datasource.pool.maxActive=15 master.datasource.pool.minIdle=3 master.datasource.pool.maxWait=60000 master.datasource.pool.timeBetweenEvictionRunsMillis=60000 master.datasource.pool.minEvictableIdleTimeMillis=120000 #slave slave.datasource.url=jdbc:mysql://localhost:3306/haogonge_dev?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false slave.datasource.username=root slave.datasource.password=root slave.datasource.driver-class-name=com.mysql.jdbc.Driver slave.datasource.pool.initialSize=3 slave.datasource.pool.maxActive=15 slave.datasource.pool.minIdle=3 slave.datasource.pool.maxWait=60000 slave.datasource.pool.timeBetweenEvictionRunsMillis=60000 slave.datasource.pool.minEvictableIdleTimeMillis=120000