下面我将详细讲解 Spring Boot 动态数据源示例(多数据源自动切换)的完整攻略。
什么是动态数据源
动态数据源是一种可以在程序运行时自动切换数据源的技术,它可以在不重启应用的情况下帮助我们实现多数据源的自动切换,非常便于开发和维护。在实践中,我们可以使用 Spring Boot 官方提供的 AbstractRoutingDataSource
类来实现动态数据源的功能。
实现动态数据源
下面,我们来看一下如何使用 Spring Boot 实现动态数据源。
第一步:引入依赖
在 pom 文件中引入以下依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
</dependencies>
第二步:实现数据源切换逻辑
首先,我们需要继承 AbstractRoutingDataSource
类,并重写它的 determineCurrentLookupKey()
方法,该方法返回的是当前线程的数据源 key。当程序需要切换数据源时,只需要调用 setTargetDataSource()
方法切换到指定的数据源即可。
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceKey();
}
@Override
public void setTargetDataSource(Object targetDataSource) {
super.setTargetDataSource(targetDataSource);
afterPropertiesSet();
}
}
第三步:实现数据源上下文
在动态数据源中,需要为每个线程分配一个数据源,我们可以使用 ThreadLocal 来实现。
public class DataSourceContextHolder {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
public static void setDataSourceKey(String dataSourceKey) {
CONTEXT_HOLDER.set(dataSourceKey);
}
public static String getDataSourceKey() {
return CONTEXT_HOLDER.get();
}
public static void clearDataSourceKey() {
CONTEXT_HOLDER.remove();
}
}
第四步:实现数据源切换注解
我们可以使用自定义注解来标注使用哪个数据源,这样可以方便地在代码中切换数据源。
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String value() default "";
}
第五步:配置数据源
在 application.yml
中配置数据源信息:
spring:
datasource:
master:
url: jdbc:mysql://localhost:3306/db_master?useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: password
driver-class-name: com.mysql.jdbc.Driver
slave:
url: jdbc:mysql://localhost:3306/db_slave?useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: password
driver-class-name: com.mysql.jdbc.Driver
type: com.zaxxer.hikari.HikariDataSource
第六步:配置数据源切换拦截器
我们需要配置一个切面来拦截 @DataSource
注解,并将数据源的 key 设置为当前线程的上下文中。
@Aspect
@Component
public class DataSourceAspect {
@Around("@annotation(dataSource)")
public Object around(ProceedingJoinPoint joinPoint, DataSource dataSource) throws Throwable {
try {
String dataSourceKey = dataSource.value();
if (StringUtils.isNotEmpty(dataSourceKey)) {
DataSourceContextHolder.setDataSourceKey(dataSourceKey);
}
return joinPoint.proceed();
} finally {
DataSourceContextHolder.clearDataSourceKey();
}
}
}
至此,我们就实现了动态数据源的功能。
示例
示例一:基础数据源
我们先定义一个简单的数据库表,然后通过基础数据源连接数据库,并进行查询操作。
首先,我们在 master
数据库中创建 user
表:
CREATE TABLE user (
id BIGINT(20) PRIMARY KEY NOT NULL,
username VARCHAR(50) NOT NULL,
password VARCHAR(50) NOT NULL
);
接下来,我们编写一个查询方法:
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public List<User> findAll() {
return jdbcTemplate.queryForList("SELECT * FROM user", User.class);
}
}
最后,我们在 Service
层中调用该查询方法:
@Service
public class UserService {
@Autowired
private UserDao userDao;
public List<User> findAll() {
return userDao.findAll();
}
}
示例二:多数据源
接下来,我们修改上面的示例,使用动态数据源来连接数据库。首先,我们在 slave
数据库中创建 user
表:
CREATE TABLE user (
id BIGINT(20) PRIMARY KEY NOT NULL,
username VARCHAR(50) NOT NULL,
password VARCHAR(50) NOT NULL
);
接下来,我们来定义两个 DataSource
,分别对应 master
和 slave
数据库:
@Configuration
public class DataSourceConfig {
@Bean(name = "masterDataSource")
@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();
}
}
然后,我们定义一个 DynamicDataSource
,这个类是动态数据源实现的关键。
@Configuration
public class DynamicDataSourceConfig {
@Bean
@Primary
public DataSource dynamicDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
@Qualifier("slaveDataSource") DataSource slaveDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>(2);
targetDataSources.put("masterDataSource", masterDataSource);
targetDataSources.put("slaveDataSource", slaveDataSource);
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setTargetDataSources(targetDataSources);
dynamicDataSource.setDefaultTargetDataSource(masterDataSource);
return dynamicDataSource;
}
}
然后,我们修改一下 UserDao
和 UserService
中的代码,使用 @DataSource
来指定数据源。
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@DataSource("masterDataSource")
public List<User> findAllInMaster() {
return jdbcTemplate.queryForList("SELECT * FROM user", User.class);
}
@DataSource("slaveDataSource")
public List<User> findAllInSlave() {
return jdbcTemplate.queryForList("SELECT * FROM user", User.class);
}
}
@Service
public class UserService {
@Autowired
private UserDao userDao;
public List<User> findAllInMaster() {
return userDao.findAllInMaster();
}
public List<User> findAllInSlave() {
return userDao.findAllInSlave();
}
}
在上面的代码中,我们在 UserDao
中定义了两个查询方法,分别使用了 @DataSource("masterDataSource")
和 @DataSource("slaveDataSource")
注解,用来指定数据源。然后,在 UserService
中调用这两个查询方法,以便在 Controller
中测试使用动态数据源的效果。
最后,我们来测试一下是否可以动态切换数据源。我们定义一个 UserController
:
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/users/master")
public List<User> getAllUsersInMaster() {
return userService.findAllInMaster();
}
@GetMapping("/users/slave")
public List<User> getAllUsersInSlave() {
return userService.findAllInSlave();
}
}
在上面的代码中,我们定义了两个 HTTP 接口,分别对应查询 master
和 slave
数据库中的数据。我们在浏览器中访问这两个接口,可以发现数据来源确实是 master 和 slave 数据库,动态数据源切换成功。
至此,我们就完成了 Spring Boot 动态数据源示例的演示。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring Boot 动态数据源示例(多数据源自动切换) - Python技术站