本文以MybatisPlus的多数据源切换注解@DS为例
- 项目背景介绍:
- SpringMVC项目迁移到SpirngCloud架构
- 老项目按mapper.java目录进行多数据源切换,新项目以@DS("指定数据源")注解进行切换
- 切面中逻辑需要同时兼容原因目录切换和注解切换
- 实现重点:获取注解值
- 代码:
<!--多数据源依赖,DB依赖请自行添加-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
类介绍:
DataBaseAspect.java(切面)
DataSourceContextHolder.java(DS指定)
DBTypeEnum.java(DS枚举值)
DynamicDataSource.java(SpringBoot切换DS)
MybatisPlusConfig.java(配置DS对象)
package com.xxl.config;
import com.baomidou.dynamic.datasource.annotation.DS;
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.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Aspect
@Component
@Order(-1)
public class DataBaseAspect {
/**
* 使用第一个数据源的路径
*/
@Pointcut("execution(* com.xxl.mapper.master.*.*(..))")
public void dbPointCut() {}
/**
* 使用第二个数据源的路径
*/
@Pointcut("execution(* com.xxl.mapper.read.*.*(..))")
public void dbPointCut2() {}
@Before("dbPointCut()")
public void beforeSwitchDS(JoinPoint point) {
DataSourceContextHolder.setDbType(DBTypeEnum.MASTER);
}
@Before("dbPointCut2()")
public void change2(JoinPoint point) {
DataSourceContextHolder.setDbType(DBTypeEnum.READ);
}
@Before("@annotation(com.baomidou.dynamic.datasource.annotation.DS)&&@annotation(ds)")
public void mybatisPlusDS(JoinPoint point,DS ds) {
DataSourceContextHolder.setDbType(ds.value());
}
@After("dbPointCut()")
public void afterSwitchDS(JoinPoint point) {
DataSourceContextHolder.clearDbType();
}
}
package com.xxl.config;
public class DataSourceContextHolder {
private final static ThreadLocal contextHolder = new ThreadLocal<>(); //实际上就是开启多个线程,每个线程进行初始化一个数据源
/**
* 设置数据源
* @param dbTypeEnum
*/
public static void setDbType(DBTypeEnum dbTypeEnum) {
contextHolder.set(dbTypeEnum.getValue());
}
public static void setDbType(String dbType) {
contextHolder.set(dbType);
}
/**
* 取得当前数据源
* @return
*/
public static String getDbType() {
return (String) contextHolder.get();
}
/**
* 清除上下文数据
*/
public static void clearDbType() {
contextHolder.remove();
}
}
package com.xxl.config;
import lombok.Getter;
@Getter
public enum DBTypeEnum {
MASTER("master"), READ("read");
private String value;
DBTypeEnum(String value) {
this.value = value;
}
}
package com.xxl.config;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDbType();
}
}
package com.xxl.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.extension.MybatisMapWrapperFactory;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
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 org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@EnableTransactionManagement
@Configuration
@MapperScan(value = {"com.xxl.mapper.read.*,com.xxl.mapper.write.*,com.xxl.mapper.mp.*"})//指定对应的mapper路径
public class MybatisPlusConfig {
private static ApplicationContext applicationContext;
//配置信息可以在被调用的时候写死
@Autowired
MybatisPlusNacosConfiguration mybatisPlusNacosConfiguration;
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
@Bean(name = "db1")
@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.master")
public DataSource db1() {
return new DruidDataSource();
}
@Bean(name = "db2")
@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.read")
public DataSource db2() {
return new DruidDataSource();
}
/**
* 动态数据源配置
*
* @return
*/
@Bean(name = "multipleDataSource")
@Primary
public DataSource multipleDataSource(@Qualifier("db1") DataSource db1,
@Qualifier("db2") DataSource db2) {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DBTypeEnum.MASTER.getValue(), db1);
targetDataSources.put(DBTypeEnum.READ.getValue(), db2);
dynamicDataSource.setTargetDataSources(targetDataSources);
// 程序默认数据源,根据程序调用数据源频次,把常调用的数据源作为默认
dynamicDataSource.setDefaultTargetDataSource(db1);
return dynamicDataSource;
}
@Bean("sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory() throws Exception {
MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
sqlSessionFactory.setDataSource(multipleDataSource(db1(), db2()));
sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/*/*.xml"));
sqlSessionFactory.setPlugins(new Interceptor[]{paginationInterceptor()});
/**
* Mybatis配置
*/
MybatisConfiguration configuration = new MybatisConfiguration();
configuration.setMapUnderscoreToCamelCase(mybatisPlusNacosConfiguration.getMapUnderscore2CamelCase());
configuration.setCallSettersOnNulls(mybatisPlusNacosConfiguration.getCallSettersOnNulls());
configuration.setCacheEnabled(false);
// 数据库查询结果驼峰式返回
sqlSessionFactory.setObjectWrapperFactory(new MybatisMapWrapperFactory());
// 添加分页功能
sqlSessionFactory.setPlugins(new Interceptor[]{paginationInterceptor()});
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setBanner(mybatisPlusNacosConfiguration.getBanner());
GlobalConfig.DbConfig dbConfig = new GlobalConfig.DbConfig();
dbConfig.setIdType(IdType.valueOf(mybatisPlusNacosConfiguration.getIdType()));
globalConfig.setDbConfig(dbConfig);
configuration.setGlobalConfig(globalConfig);
sqlSessionFactory.setConfiguration(configuration);
sqlSessionFactory.setGlobalConfig(globalConfig);
return sqlSessionFactory.getObject();
}
@Bean(name = "multipleTransactionManager")
@Primary
public DataSourceTransactionManager multipleTransactionManager(@Qualifier("multipleDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}