1. 什么是动态数据源?
动态数据源是指在程序运行过程中,可以动态切换或配置的数据源。在多数据源环境中,应用程序可能需要根据不同的场景或条件从不同的数据源中读取或写入数据。为了实现这样的功能,需要在运行时动态地选择或切换数据源,这时就需要动态数据源管理。
在企业应用中,动态数据源有多种应用场景:
-
多租户应用:对于支持多租户的应用,每个租户可能有自己的数据库。当不同的租户登录应用时,需要动态地为其选择相应的数据源。
-
读写分离:为了提高数据库的性能和可用性,应用可能会使用主-从数据库架构,其中写操作发生在主数据库上,而读操作可以分发到一个或多个从数据库上。这需要应用在运行时根据操作类型动态地选择数据源。
-
数据库分片或分区:当单一数据库无法满足应用的容量或性能需求时,可能需要将数据分散到多个数据库实例上。应用需要根据数据的键值或其他条件动态地选择数据源。
在Java应用中,常用的动态数据源管理框架有Druid、MyBatis-Plus等。使用这些框架,开发者可以配置多个数据源并在运行时根据需要进行切换。
总体上,动态数据源提供了更大的灵活性,使应用能够适应多变的数据库环境和需求。但同时也带来了更复杂的配置和管理需求,需要开发者在设计和实现时进行充分考虑。
2. Druid动态数据源案例
使用Druid作为连接池并实现Spring应用中的动态数据源,通常涉及以下几个步骤:
- 添加依赖:
在
pom.xml中添加Druid和Spring Boot的相关依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version> <!-- 请检查最新版本 -->
</dependency>
- 配置多个数据源:
在
application.yml或application.properties中进行数据源的配置。
spring:
datasource:
dynamic:
primary: master # 主数据源
datasource:
master:
url: jdbc:mysql://localhost:3306/master_db
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
slave:
url: jdbc:mysql://localhost:3306/slave_db
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
- 创建动态数据源配置:
@Configuration
public class DynamicDataSourceConfig {
@Primary
@Bean(name = "masterDataSource")
@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.master")
public DataSource masterDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "slaveDataSource")
@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.slave")
public DataSource slaveDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "dynamicDataSource")
public DynamicDataSource dataSource(DataSource masterDataSource, DataSource slaveDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master", masterDataSource);
targetDataSources.put("slave", slaveDataSource);
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setTargetDataSources(targetDataSources); // 设置数据源
dynamicDataSource.setDefaultTargetDataSource(masterDataSource); // 设置默认数据源
return dynamicDataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dynamicDataSource);
return bean.getObject();
}
@Bean
public PlatformTransactionManager transactionManager(DynamicDataSource dynamicDataSource) {
return new DataSourceTransactionManager(dynamicDataSource);
}
}
- 实现动态数据源:
通过继承
AbstractRoutingDataSource实现数据源切换的逻辑:
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
@Override
protected Object determineCurrentLookupKey() {
return getContextKey();
}
public static void setContextKey(String key) {
CONTEXT_HOLDER.set(key);
}
public static String getContextKey() {
return CONTEXT_HOLDER.get();
}
public static void clearContextKey() {
CONTEXT_HOLDER.remove();
}
}
-
切换数据源: 根据需要,可以通过
DynamicDataSource.setContextKey("slave")来切换数据源,例如在服务方法、切面等地方。 -
清除数据源: 在使用完毕后,确保清除线程局部变量,防止内存泄漏。
以上只是一个简单的示例,根据实际需求和项目结构,可能需要进行相应的调整。
3. MyBatis-Plus动态数据源案例
使用MyBatis-Plus实现动态数据源切换的过程大体与使用原生MyBatis类似,但由于MyBatis-Plus提供了额外的功能和简化,我们可以更容易地进行集成。
以下是一个使用MyBatis-Plus的Spring Boot应用示例来实现动态数据源:
- 添加依赖:
在pom.xml中添加必要的依赖。
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version> <!-- 请根据实际情况选择版本 -->
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
- 配置数据源:
在application.yml中配置数据源。
spring:
datasource:
dynamic:
primary: master
datasource:
master:
url: jdbc:mysql://localhost:3306/master_db
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
slave:
url: jdbc:mysql://localhost:3306/slave_db
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
- 动态数据源配置类:
与上面的示例类似,你需要创建数据源配置类来配置动态数据源。
- 实现动态数据源:
同样,实现一个继承AbstractRoutingDataSource的DynamicDataSource类。
- 切换数据源:
使用DynamicDataSource.setContextKey("slave")来切换数据源。
- Mapper接口和实体类:
假设我们有一个User实体类和对应的UserMapper接口。
@Data
@TableName("user")
public class User {
private Long id;
private String name;
private Integer age;
}
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
- 使用MyBatis-Plus功能:
在你的服务或控制器中,你可以使用MyBatis-Plus提供的CRUD功能。
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public List<User> getAllUsers() {
// 这里假设我们想要从slave数据源读取数据
DynamicDataSource.setContextKey("slave");
List<User> users = userMapper.selectList(null);
DynamicDataSource.clearContextKey();
return users;
}
}
注意: 为了简洁起见,这个示例可能遗漏了某些细节,如异常处理、数据源切换的AOP实现等。在实际应用中,你可能需要根据具体需求进一步优化和扩展这些代码。