配置多数据源,可以自己实现,也可以使用别人已有的轮子。
1. 自定义注解配置多数据源
自己配置多数据源用到了自定义注解,参考文章:
Java注解 - 简书 (jianshu.com)
1.1 需要的基本依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
1.2 配置多数据源
因为引入了druid,是在druid的基础上配置的。
- 1.2.1
application.yml
文件中数据库信息:
spring:
# datasource
datasource:
druid:
# 数据源1
master:
url: jdbc:mysql://192.168.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8&allowMultiQueries=true&autoReconnect=true
password: root
username: root
driver-class-name: com.mysql.cj.jdbc.Driver
initial-size: 5
max-active: 20
min-idle: 5
max-wait: 6000
# 数据源2
slave:
# 多数据源开关,配合@ConditionalOnProperty使用
enabled : true
url: jdbc:mysql://192.168.0.2:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8&allowMultiQueries=true&autoReconnect=true
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
# 连接池配置
# 配置初始化大小、最小、最大
initial-size: 5
max-active: 20
min-idle: 5
# 连接超时时间
max-wait: 6000
- 1.2.2 配置动态数据源
动态数据源-继承AbstractRoutingDataSource
类,重写方法:方法内容不重要,如果有需要,可以自己打印一些日志,
package com.test.dynamic.datasource.test.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
配置多个datasource,添加到动态数据源中
package com.test.dynamic.datasource.test.datasource;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
/**
* @date 2021年5月20日 上午10:16:09
* @Description:多数据源配置
*/
@Configuration
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class })
public class DynamicDataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.druid.master")
public DataSource masterDataSource() {
return DruidDataSourceBuilder.create().build();
}
/**
* @ConditionalOnProperty一般加在@Configurarion、@Component配置的类上或@Bean配置的方法上,
* 表示满足获取到某些配置文件信息后才会配置或加载。
* havingValue可与name组合使用:比较name获取到的属性值与havingValue给定的值是否相同,相同才加载配置
*/
@Bean
@ConfigurationProperties("spring.datasource.druid.slave")
@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
public DataSource slaveDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@Primary
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 添加数据源
Map<Object, Object> targetDataSources = new HashMap<>();
DataSource masterDataSource = masterDataSource();
DataSource slaveDataSource = slaveDataSource();
targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
targetDataSources.put(DataSourceType.SLAVE.name(), slaveDataSource);
dynamicDataSource.setTargetDataSources(targetDataSources);
// 设置默认数据源
dynamicDataSource.setDefaultTargetDataSource(masterDataSource);
return dynamicDataSource;
}
}
注意:因为是自己装配数据源,要排除SpringBoot自动装配数据源,所以类上添加注解@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class }),否则报错
1.3 数据源手动切换使用
前面将数据源添加到了动态数据源(DynamicDataSource
)中,并且设置了主数据源为默认数据源。但我们想要的是在需要的地方,想切换就切换。因此,需要有一个动态获切换数据源的地方(我们称为上下文),对于 web 应用,访问以线程为单位,使用 ThreadLocal 就比较合适,如下:
package com.test.dynamic.datasource.test.datasource;
public class DynamicDataSourceContextHolder {
/**
* 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 设置数据源的变量
*/
public static void setDataSourceType(String dsType) {
CONTEXT_HOLDER.set(dsType);
}
/**
* 获得数据源的变量
*/
public static String getDataSourceType() {
return CONTEXT_HOLDER.get();
}
/**
* 清空数据源变量
*/
public static void clearDataSourceType() {
CONTEXT_HOLDER.remove();
}
}
使用时,在DAO
前面先切换到目标数据源:
package com.test.dynamic.datasource.test.service.impl;
import java.util.Date;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import com.test.dynamic.datasource.test.dao.UserDao;
import com.test.dynamic.datasource.test.entity.Result;
import com.test.dynamic.datasource.test.entity.User;
import com.test.dynamic.datasource.test.service.TestService;
@Service
public class TestServiceImpl implements TestService {
@Resource
private UserDao userDao;
@Override
public Result<String> addUser(String name) {
User u = new User();
u.setCreateTime(new Date());
u.setIdCard("1234567891011");
u.setName(name);
u.setPhone("123");
// 手动切换数据源
DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE.name());
userDao.addUser(u);
// 手动切换数据源
DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
List<User> list = userDao.listMyUser();
for (User user : list) {
System.out.println(user);
}
return new Result<String>().success(1);
}
}
注意:到这就能使用了,但是每次sql时要手动切换数据源。下面讲用注解来切换数据源......
1.4 写数据源注解
数据源枚举类
package com.test.dynamic.datasource.test.datasource;
public enum DataSourceType {
/**
* 主库
*/
MASTER,
/**
* 从库
*/
SLAVE;
}
数据源注解类
package com.test.dynamic.datasource.test.datasource;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
public DataSourceType value() default DataSourceType.MASTER;
}
1.5 AOP+注解自动切换数据源
AOP类
package com.test.dynamic.datasource.test.datasource;
import java.lang.reflect.Method;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Aspect
@Order(1)
@Component
public class DynamicDataSourceAspect {
// 配置织入点
@Pointcut("@annotation(com.test.dynamic.datasource.test.datasource.DataSource)")
public void pointCut() {
}
/**
* @Description:在方法执行执行会先拦截注解@DataSource这个的方法
*/
@Before("pointCut()")
public void changeDataSource(JoinPoint point) {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
DataSource ds = method.getAnnotation(DataSource.class);
// 获取注解中的数据源
String dsType = ds.value().name();
if (!dsType.equals(DynamicDataSourceContextHolder.getDataSourceType())) {
DynamicDataSourceContextHolder.setDataSourceType(dsType);
}
}
/**
* @Description:执行完成后将数据源切换为主数据源,这样使用主数据源不用加注解
*/
@After("pointCut()")
public void releaseLocal(JoinPoint point) {
if (!DataSourceType.MASTER.name().equals(DynamicDataSourceContextHolder.getDataSourceType())) {
DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
}
}
}
DAO接口中添加@DataSource
注解使用
使用注解就不用在service实现类中手动切换了,即DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
可以去掉。
package com.test.dynamic.datasource.test.dao;
import java.util.List;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.test.dynamic.datasource.test.datasource.DataSource;
import com.test.dynamic.datasource.test.datasource.DataSourceType;
import com.test.dynamic.datasource.test.entity.User;
public interface UserDao extends BaseMapper<User> {
@DataSource(DataSourceType.SLAVE)
Integer addUser(User u);
@DataSource(DataSourceType.SLAVE)
List<User> listUser();
Integer addMyUser(User u);
List<User> listMyUser();
}
2. Mybatis-plus扩展插件配置多数据源
包米豆的插件dynamic-datasource-spring-boot-starter
提供了多数据源功能,官网使用方法比较详细,可以参考使用。
参考:Mybatis-plus多数据源插件
网友评论