基于Spring Boot 动态主从数据库路由
本文简单的介绍一下基于Spring boot框架动态连接多数据源的实现,在配置文件中添加多个数据源,采用主从配置的方式,配置zjsz、cloud两个数据库。
项目的目录结构:
application.properties
核心主要是dynamic和aop目录
1.首先配置数据源信息,在spring5.x之后开始默认使用HikariCP来做数据源
@Configuration
public class DataSourceConfig {
@Bean(name = "masterDataSource")
@ConfigurationProperties(prefix = "spirng.master.datasource")
public HikariDataSource masterDataSource() {
return new HikariDataSource();
}
@Bean(name = "slaveDataSource")
@ConfigurationProperties(prefix = "spirng.slave.datasource")
public HikariDataSource slaveDataSource() {
return new HikariDataSource();
}
//动态数据源
@Bean(name = "dynamicDataSource")
//解决互相依赖关系
@DependsOn({ "masterDataSource", "slaveDataSource"})
@Primary
public DataSource getDataSource() {
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources());
return dataSource;
}
private Map<Object, Object> targetDataSources() {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.MASTER.getType(), masterDataSource());
targetDataSources.put(DataSourceType.SLAVE.getType(), slaveDataSource());
return targetDataSources;
}
}
public enum DataSourceType {
SLAVE("slave", "从库"),
MASTER("master", "主库");
private String type;
private String name;
DataSourceType(String type, String name) {
this.type = type;
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2.自定义Datasource注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@Inherited
public @interface DataSource {
/**
* 数据库路由
*/
DataSourceType value() default DataSourceType.MASTER;
}
3.使用ThreadLocal来保证从@DataSource获取的路由key是线程安全的
public class DynamicDataSourceHolder {
private static final ThreadLocal<String> holder = new ThreadLocal<>();
public static void putDataSource(DataSourceType dataSourceType) {
holder.set(dataSourceType.getType());
}
public static String getDataSouce() {
return holder.get();
}
}
4.继承AbstractRoutingDataSource并重写determineCurrentLookupKey法法
使用AbstractRoutingDataSource 的实现类,进行灵活的切换,可以通过AOP或者手动编程设置当前的DataSource,这样的编写方式比较好。
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
if (DynamicDataSourceHolder.getDataSouce() != null) {
return DynamicDataSourceHolder.getDataSouce();
}
return DataSourceType.MASTER.getType();
}
}
5.使用AOP进行拦截带有@DataSource注解的方法,并获取注解的值,进行动态数据源操作
@Aspect
@Component
public class DataSourceAspect implements Ordered {
// @Pointcut("execution(public * com.aop.jdbc.impl..*.*(..))")
// public void dynamic() {
//
// }
@Before(value = "@annotation(dataSource)")
public void dataSourcePoint(JoinPoint joinPoint, DataSource dataSource) {
DynamicDataSourceHolder.putDataSource(dataSource.value());
}
@Override
public int getOrder() {
return -1;
}
}
6.使用方法
@Service("testService")
public class TestServiceImpl implements ITestService {
@Autowired
private OwnCodeMapper ownCodeMapper;
@DataSource(DataSourceType.MASTER)
@Override
public String save() {
System.out.println("=======save======进入主库操作========save======");
System.out.println("删除影响行数:" + this.ownCodeMapper.deleteByPrimaryKey(2));
return null;
}
@DataSource(DataSourceType.SLAVE)
@Override
public String query() {
System.out.println("=======query======进入从库操作=========query=====");
System.out.println("查询结果:" + this.ownCodeMapper.selectByPrimaryKey(36));
return null;
}
}
到此就完成了动态主从路由的代码开发。
源码地址,大家帮忙star哦!!