1、数据源信息配置
spring:
datasource:
master:
url: jdbc:mysql://127.0.0.1:3306/master?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai
username: demo2_admin
password: xxxxxxxx
driver-class-name: com.mysql.cj.jdbc.Driver
slaver:
url: jdbc:mysql://127.0.0.1:3306/slaver?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai
username: demo1_admin
password: yyyyyyyy
driver-class-name: com.mysql.cj.jdbc.Driver
2、数据源切换注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DB{
EmDSType value() default EmDSType.MASTER;
}
3、数据源枚举
public enum EmDSType {
//生产环境
MASTER("master"),
//预发环境
SLAVER("slaver");
private String name;
EmDSType(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
4、数据库持有
public class DataSourceHolder {
private static final ThreadLocal<String> DS_HOLDER = new ThreadLocal<>();
/**
* 设置数据源
* @param dsType
*/
public static void setDataSource(EmDSType dsType) {
DS_HOLDER.set(dsType.getName());
}
/**
* 获取数据源
* @return
*/
public static String getDataSource() {
return DS_HOLDER.get();
}
/**
* 清理数据源
*/
public static void clearDataSource() {
DS_HOLDER.remove();
}
}
5、数据源路由:AbstractRoutingDataSource
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSource.class);
@Override
protected Object determineCurrentLookupKey() {
LOGGER.info("切换数据源:{}", DataSourceHolder.getDataSource());
return DataSourceHolder.getDataSource();
}
}
6、动态数据源配置
@Configuration
public class DataSourceConfig {
/**
* 主数据源
* @return
*/master")
@ConfigurationProperties("spring.datasource.master")
public DataSource master() {
return new DruidDataSource();
}
/**
* 从数据源
* @return
*/
@Bean("slaver")
@ConfigurationProperties("spring.datasource.slaver")
public DataSource slaver() {
return new DruidDataSource();
}
/**
* 优先加载数据源
* @return
*/
@Bean("dynamicDataSource")
@Primary
public DataSource dynamicDataSource() {
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setDefaultTargetDataSource(master());
Map<Object, Object> dataSourceMap = Maps.newHashMap();
dataSourceMap.put(EmDSType.MASTER.getName(), master());
dataSourceMap.put(EmDSType.SLAVER.getName(), slaver());
dataSource.setTargetDataSources(dataSourceMap);
return dataSource;
}
/**
* 事务管理
* @return
*/
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dynamicDataSource());
}
}
7、JDBC配置
@Configuration
public class JdbcTemplateConfig {
@Bean
@Primary
JdbcTemplate jdbcTemplateInner(@Qualifier("inner")DataSource inner) {
return new JdbcTemplate(inner);
}
@Bean
JdbcTemplate jdbcTemplateOuter(@Qualifier("outer")DataSource outer) {
return new JdbcTemplate(outer);
}
}
8、AOP切面
@Aspect
@Component
@Order(1)
public class DataSourceAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(DataSourceAspect.class);
@Pointcut("@annotation(com.jd.jx.lowcode.datasource.annotation.DB)")
public void pointCut() {}
@Before("pointCut()")
public void before(JoinPoint joinPoint) {
Object target = joinPoint.getTarget();
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
EmDSType dataSource = EmDSType.PROD;
try {
Class<?> aClass = target.getClass();
if (aClass.isAnnotationPresent(DB.class)) {
DB annotation = aClass.getAnnotation(DB.class);
dataSource = annotation.value();
}else {
Method method = signature.getMethod();
if (method.isAnnotationPresent(DB.class)) {
DB annotation = method.getAnnotation(DB.class);
dataSource = annotation.value();
}
}
} catch (Exception e) {
e.printStackTrace();
}
LOGGER.info("aop切换数据源:{}", dataSource.getName());
DataSourceHolder.setDataSource(dataSource);
}
@After("pointCut()")
public void after() {
DataSourceHolder.clearDataSource();
}
}
9、问题
在同一个事物中,数据源切换失败