1、引入数据库相关依赖
<!--数据库相关-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.2.0</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
2、配置多数据源
例如下面yml 中我配置了 ali 和 local 两个数据源,名称自己随便取
spring:
datasource:
ali:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://139.9.168.24:3306/ctools?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource # 指定使用Druid数据源
# Druid连接池配置
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
validation-query: SELECT 1
test-while-idle: true
test-on-borrow: false
test-on-return: false
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
filters: stat,wall
connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
local:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/cl_test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource # 指定使用Druid数据源
# Druid连接池配置
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
validation-query: SELECT 1
test-while-idle: true
test-on-borrow: false
test-on-return: false
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
filters: stat,wall
connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# useUnicode=true:表示使用Unicode字符集。
# characterEncoding=UTF-8:表示使用UTF-8字符编码。
# autoReconnect=true:表示在连接断开时自动重新连接。
# useSSL=false:表示不使用SSL加密连接。
3、数据库配置
- 多数据源配置
配置自定义 DataSourceRouting 数据库路由的Bean
@Primary 数据表示使用那个作为默认数据源
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.ali")
public DataSource aliDB() {
return DataSourceBuilder.create().type(DruidDataSource.class).build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.local")
public DataSource localDB() {
return DataSourceBuilder.create().type(DruidDataSource.class).build();
}
@Bean
@Primary
@DependsOn(value = {"aliDB", "localDB"})
public DataSource getDB() {
DataSourceRouting routing = new DataSourceRouting();
Map<Object, Object> dbMap = new HashMap<>();
dbMap.put("aliDB", aliDB());
dbMap.put("localDB", localDB());
routing.setTargetDataSources(dbMap);
routing.setDefaultTargetDataSource(localDB());
return routing;
}
}
- 路由配置
要实现 AbstractRoutingDataSource 抽象类
需要用到自定义的DataSourceHelper 类的 ThreadLocal 来切换数据源
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DataSourceRouting extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceHelper.get();
}
}
- 自定义 DataSourceHelper 类,ThreadLocal 用来指定当前线程的数据源
public class DataSourceHelper {
private static final ThreadLocal<String> dbname = new ThreadLocal<String>();
public static String get() {
return dbname.get();
}
public static void set(String name) {
dbname.set(name);
}
public static void remove() {
dbname.remove();
}
}
4、测试
import com.springboot.config.DataSourceHelper;
import com.springboot.mapper.UserMapper;
import com.springboot.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class DemoApplicationTests {
@Resource
private UserMapper userMapper; // ali
@Resource
private UserInfoMapper userInfoMapper; // 本地
@Test
void contextLoads() {
// 本地
List<UserInfo> list = userInfoMapper.list();
list.forEach(System.out::println);
// 切换到 ali 数据源
DataSourceHelper.set("aliDB");
List<User> list02 = userMapper.list();
list02.forEach(System.out::println);
}
}
补充:使用注解方式来切换数据源
1、添加 AOP 依赖
<!--aop 相关依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2、启动类上配置启用AOP
@EnableAspectJAutoProxy 开启AOP
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@SpringBootApplication
@MapperScan(value = "com.springboot.mapper")
@EnableAspectJAutoProxy
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
3、自定义数据源切换注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(value = {ElementType.TYPE, ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface DB {
String value();
}
4、定义切面使注解生效
package com.springboot.aop;
import com.springboot.config.DataSourceHelper;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
public class DBAop {
// @annotation 表示注解用到方法上,@within 则是类上
@Pointcut(value = "@annotation(com.springboot.aop.DB) || @within(com.springboot.aop.DB)")
public void pointcut() {}
@Around(value = "pointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
// 获取方法上的自定义数据源注解
DB annotation = method.getAnnotation(DB.class);
if (annotation == null) {
annotation = method.getDeclaringClass().getAnnotation(DB.class);
}
try {
if (annotation != null) {
DataSourceHelper.set(annotation.value());
}
return pjp.proceed();
} finally {
DataSourceHelper.remove();
}
}
}
5、使用注解
// 阿里数据库用户信息
@Service
@Slf4j
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
@Resource
private UserInfoService userInfoService;
@Override
@DB("aliDB")
public List<User> list() {
List<User> users = userMapper.list();
List<User> userInfo = getUserInfo();
users.addAll(userInfo);
return users;
}
@DB("localDB")
public List<User> getUserInfo() {
List<UserInfo> list = userInfoService.list();
return list.stream().map(em -> {
User user = new User();
BeanUtils.copyProperties(em, user);
return user;
}).collect(Collectors.toList());
}
}
// 本地用户数据库信息
@Service
@DB("localDB")
public class UserInfoServiceImpl implements UserInfoService {
@Resource
private UserInfoMapper userInfoMapper;
@Override
public List<UserInfo> list() {
return userInfoMapper.list();
}
}