Spring Boot优雅实现注解切换多数据源
请帮我完善这篇文档,并且里面的代码添加注释
实现注解切换多数据源需要以下步骤:
1.首先添加了AOP、MyBatis、Druid和MySQL的依赖。
<!-- Aop-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- Mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!-- Druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
2.配置两个数据源,一个是master
,一个是slave
,同时设置了Druid
连接池的参数,以及MyBatis
的相关配置。
spring:
datasource:
druid:
defaultAutoCommit: true
initialSize: 5
maxActive: 20
maxWait: 60000
minIdle: 5
testOnBorrow: false
testOnReturn: false
testWhileIdle: true
timeBetweenEvictionRunsMillis: 60000
validationQuery: SELECT 1 FROM DUAL
filters: stat,wall,log4j2
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
master:
url: jdbc:mysql://localhost:3306/master?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
slave:
url: jdbc:mysql://localhost:3306/slave?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.demo.entity
configuration:
map-underscore-to-camel-case: true
3.创建一个枚举类,用于区分不同的数据源。
/**
* <p>
* 枚举类,用于区分不同的数据源
* </p>
*
* @author snogzixian
* @Url 宋子宪博客 www.songzixian.com
* @since 2023-03-15
*/
public enum DataSourceType {
/**
* 主数据源
*/
MASTER("master"),
/**
* 从数据源
*/
SLAVE("slave");
private final String value;
DataSourceType(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
4.创建一个注解类,用于标识需要使用哪个数据源。
import com.songzixian.enums.DataSourceType;
import java.lang.annotation.*;
/**
* <p>
* 注解类,用于标识需要使用哪个数据源
* </p>
*
* @author snogzixian
* @Url 宋子宪博客 www.songzixian.com
* @since 2023-03-15
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
DataSourceType value() default DataSourceType.MASTER;
}
5.创建一个AOP切面类,用于在方法执行前将数据源切换到指定的数据源上,方法执行后再切回原来的数据源。
import com.songzixian.annotation.DataSource;
import com.songzixian.enums.DataSourceType;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
/**
* <p>
* 数据源切面类,用于在方法执行前将数据源切换到指定的数据源上,方法执行后再切回原来的数据源
* </p>
*
* @author snogzixian
* @Url 宋子宪博客 www.songzixian.com
* @since 2023-03-15
*/
@Aspect
@Component
public class DataSourceAspect {
@Around("@annotation(com.songzixian.annotation.DataSource) || @within(com.songzixian.annotation.DataSource)")
public Object switchDataSource(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
DataSource dataSource = signature.getMethod().getAnnotation(DataSource.class);
if (dataSource == null) {
Class<?> declaringType = signature.getDeclaringType();
dataSource = declaringType.getAnnotation(DataSource.class);
}
if (dataSource == null) {
DynamicDataSource.setDataSource(DataSourceType.MASTER.getValue());
} else {
DynamicDataSource.setDataSource(dataSource.value().getValue());
}
try {
return point.proceed();
} finally {
DynamicDataSource.clearDataSource();
}
}
}
6.创建一个配置类,用于配置多数据源和动态数据源。配置多数据源后,将其放入动态数据源中,通过设置默认数据源和目标数据源来实现动态切换数据源的功能。
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* <p>
* 数据源配置类,用于配置多数据源和动态数据源
* </p>
*
* @author snogzixian
* @Url 宋子宪博客 www.songzixian.com
* @since 2023-03-15
*/
@Configuration
public class DataSourceConfig {
/**
* 配置多数据源
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource.druid.master")
public DataSource masterDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.druid.slave")
public DataSource slave1DataSource() {
return DruidDataSourceBuilder.create().build();
}
/**
* 配置动态数据源
*/
@Bean
public DynamicDataSource dynamicDataSource() {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master", masterDataSource());
targetDataSources.put("slave", slave1DataSource());
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources);
dataSource.setDefaultTargetDataSource(masterDataSource());
return dataSource;
}
}
7.创建一个动态数据源类,继承了AbstractRoutingDataSource
类,并实现了determineCurrentLookupKey()
方法,用于获取当前的数据源。同时,这里还提供了setDataSource()
和clearDataSource()
方法,用于设置和清除当前数据源。
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* <p>
* 动态数据源类,继承了AbstractRoutingDataSource类,并实现了determineCurrentLookupKey()方法,用于获取当前的数据源
* </p>
*
* @author snogzixian
* @Url 宋子宪博客 www.songzixian.com
* @since 2023-03-15
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> dataSourceHolder = new InheritableThreadLocal<>();
/**
* 设置数据源
*/
public static void setDataSource(String dataSource) {
dataSourceHolder.set(dataSource);
}
/**
* 清除数据源
*/
public static void clearDataSource() {
dataSourceHolder.remove();
}
/**
* 获取当前数据源
*/
@Override
protected Object determineCurrentLookupKey() {
return dataSourceHolder.get();
}
}
8.创建一个MyBatis
配置类,用于配置SqlSessionFactory
和SqlSessionTemplate
。配置SqlSessionFactory
时,需要注入DynamicDataSource
类,同时设置mapper文件的位置。配置SqlSessionTemplate
时,需要注入SqlSessionFactory
。
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
/**
* <p>
* MyBatis配置类,用于配置SqlSessionFactory和SqlSessionTemplate
* </p>
*
* @author snogzixian
* @Url 宋子宪博客 www.songzixian.com
* @since 2023-03-15
*/
@Configuration
public class MyBatisConfig {
@Autowired
private DataSourceConfig dataSourceConfig;
/**
* 配置MyBatis
*/
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSourceConfig.dynamicDataSource());
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
factoryBean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
return factoryBean.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate() throws Exception {
return new SqlSessionTemplate(sqlSessionFactory());
}
}
9.最后直接在显示类中使用注解方式即可自由切换不同数据源
注解使用示例:@DataSource(枚举值)
,不填写注解则表示使用MASTER
库
@Service
public class BlogUserServiceImpl implements BlogUserService{
@Resource
private BlogUserMapper blogUserMapper;
/**
* 数据源一用法 不用注解默认MASTER,可以省略
* @return
*/
//@DataSource(DataSourceType.MASTER)
@Override
public List<BlogUser> getAll(){
return blogUserMapper.selectAll();
}
/**
* 数据源二用法
* @return
*/
@DataSource(DataSourceType.SLAVE)
@Override
public List<BlogUser> getUserSlave(){
return blogUserMapper.selectAll();
}
}
10.启动Spring Boot项目.然后进行调用2个不同数据源的接口就可以测试了,本篇文字由博主编写和测试,完全能用
当前页面是本站的「Google AMP」版。查看和发表评论请点击:完整版 »