一、项目目录描述
java
- Application.java // 启动类
-config
--各种配置文件
-mapper
--master
--slave
resources
-mapper
--master
--slave
二、相关依赖添加pom
添加对应的依赖,因为要使用注解的方式,所以需要添加aop依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
三、配置yml文件
0、配置yml文件
spring:
datasource:
master:
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/cache?useSSL=FALSE&serverTimezone=UTC
username: root
password: root
slave:
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/test?useSSL=FALSE&serverTimezone=UTC
username: root
password: root
# 这里不用配置mybatis的xml位置,在mybatis多数据源配置类中进行配置
#mybatis:
# mapper-locations:
# - classpath:mapper/db1/*.xml
# - classpath:mapper/db2/*.xml
1、创建枚举类DataSourceType
public enum DataSourceType {
master,
slave
}
2、动态数据源
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<DataSourceType> contextHolder = new ThreadLocal<>();
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey() {
return getDataSource();
}
public static void setDataSource(DataSourceType dataSource) {
contextHolder.set(dataSource);
}
public static DataSourceType getDataSource() {
return contextHolder.get();
}
public static void clearDataSource() {
contextHolder.remove();
}
}
3、Mybatis多数据源配置
@Configuration
@MapperScan(basePackages = "*********.mapper")
public class DynamicDataSourceConfig {
@Bean(name = "masterDataSource")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "slaveDataSource")
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public DynamicDataSource dataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
@Qualifier("slaveDataSource") DataSource slaveDataSource) {
Map<Object, Object> map = new HashMap<>();
map.put(DataSourceType.master, masterDataSource);
map.put(DataSourceType.slave, slaveDataSource);
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setTargetDataSources(map);
dynamicDataSource.setDefaultTargetDataSource(masterDataSource);
return dynamicDataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dynamicDataSource);
factoryBean.setMapperLocations(
// 設置mybatisde xml所在的位置
new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*/*.xml"));
return factoryBean.getObject();
}
@Bean
public PlatformTransactionManager transactionManager(DynamicDataSource dynamicDataSource) {
return new DataSourceTransactionManager(dynamicDataSource);
}
}
4、自定义注解
/**
* @Description 自定义注解, 用于方法上
* @Date 2022年10月18日 10:56:00
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
DataSourceType value() default DataSourceType.master;
}
5、AOP切面设置数据源
@Order(-10) // 加上后会先切换数据源在切换事物
@Slf4j
@Aspect
@Component
public class DataSourceAspect {
@Pointcut(value = "@annotation(******.config.DataSource)")
public void logPointCut() {}
@Around("logPointCut()")
public Object beforeDataSource (ProceedingJoinPoint point) {
MethodSignature methodSignature = (MethodSignature)point.getSignature();
Method method = methodSignature.getMethod();
DataSource dataSource;
Object proceed;
try {
dataSource = method.getAnnotation(DataSource.class);
if(dataSource == null) {
DynamicDataSource.setDataSource(DataSourceType.master);
} else {
DynamicDataSource.setDataSource(dataSource.value());
}
proceed = point.proceed();
log.info("执行后的返回值: {}", proceed);
} catch (Throwable throwable) {
log.error("切换失败");
proceed = throwable.toString();
} finally {
DynamicDataSource.clearDataSource();
}
return proceed;
}
}
四、添加注解
在service层或者mapper层方法上添加注解即可
@Repository
public interface GroupMapper {
@DataSource(value = DataSourceType.slave)
Map<String, Object> selectGroup();
}
@Service
@RequiredArgsConstructor
public class UserService {
private final UserMapper userMapper;
private final GroupMapper groupMapper;
public Map<String, Object> getUser(int id) {
return userMapper.selectUser(id);
}
@DataSource(value = DataSourceType.slave)
//@Transactional(rollbackFor = Exception.class) // 如果需要事务,可添加
public Map<String, Object> getUser2() {
return groupMapper.selectGroup();
}
}