文章目录
-
- [1. 引入依赖](#1. 引入依赖)
- [2. 配置数据源](#2. 配置数据源)
- [3. 配置MyBatis-Plus](#3. 配置MyBatis-Plus)
- [4. 实现动态数据源](#4. 实现动态数据源)
- [5. 实现动态数据源拦截器](#5. 实现动态数据源拦截器)
- [6. 实现自定义注解](#6. 实现自定义注解)
- [7. 使用注解标记只读操作](#7. 使用注解标记只读操作)
🎈个人主页:程序员 小侯
🎐CSDN新晋作者
🎉欢迎 👍点赞✍评论⭐收藏
✨收录专栏:Java框架
✨文章内容:Spring Boot + MyBatis-Plus
🤝希望作者的文章能对你有所帮助,有不足的地方请在评论区留言指正,大家一起学习交流!🤗
在现代应用程序的开发中,数据库读写分离是一种常见的优化手段,能够提升系统的性能和可扩展性。本文将介绍如何使用Spring Boot和MyBatis-Plus实现数据库读写分离,并提供详细的代码示例。
1. 引入依赖
首先,在pom.xml
文件中添加Spring Boot和MyBatis-Plus的依赖:
xml
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MyBatis-Plus Starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<!-- 数据库驱动,以MySQL为例 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
</dependencies>
2. 配置数据源
在application.properties
或application.yml
中配置主从数据源:
properties
# 主数据源配置
spring.datasource.master.url=jdbc:mysql://master-host:3306/master_db
spring.datasource.master.username=master_user
spring.datasource.master.password=master_password
spring.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver
# 从数据源配置
spring.datasource.slave.url=jdbc:mysql://slave-host:3306/slave_db
spring.datasource.slave.username=slave_user
spring.datasource.slave.password=slave_password
spring.datasource.slave.driver-class-name=com.mysql.cj.jdbc.Driver
3. 配置MyBatis-Plus
创建MyBatisPlusConfig
类,配置MyBatis-Plus的分页插件和多数据源:
java
@Configuration
@MapperScan("com.example.mapper")
public class MyBatisPlusConfig {
@Primary
@Bean("masterDataSource")
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean("slaveDataSource")
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean(
@Qualifier("masterDataSource") DataSource masterDataSource,
@Qualifier("slaveDataSource") DataSource slaveDataSource) throws Exception {
MybatisSqlSessionFactoryBean sessionFactoryBean = new MybatisSqlSessionFactoryBean();
sessionFactoryBean.setDataSource(masterDataSource);
sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:mapper/*.xml"));
MybatisConfiguration configuration = new MybatisConfiguration();
configuration.setMapUnderscoreToCamelCase(true);
configuration.setCacheEnabled(false);
Interceptor[] interceptors = new Interceptor[]{
new PaginationInterceptor(),
new DynamicDataSourceInterceptor()
};
configuration.setInterceptors(interceptors);
sessionFactoryBean.setConfiguration(configuration);
return sessionFactoryBean;
}
@Bean
public PlatformTransactionManager platformTransactionManager(
@Qualifier("masterDataSource") DataSource masterDataSource,
@Qualifier("slaveDataSource") DataSource slaveDataSource) {
return new DataSourceTransactionManager(masterDataSource, slaveDataSource);
}
}
在上述配置中,我们使用了DynamicDataSourceInterceptor
拦截器,它实现了MyBatis的Interceptor
接口,用于动态切换数据源。
4. 实现动态数据源
创建DynamicDataSource
类,用于动态获取当前线程的数据源:
java
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceKey();
}
}
创建DynamicDataSourceContextHolder
类,用于设置和获取当前线程的数据源:
java
public class DynamicDataSourceContextHolder {
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();
}
}
5. 实现动态数据源拦截器
创建DynamicDataSourceInterceptor
拦截器,用于在执行SQL前动态切换数据源:
java
public class DynamicDataSourceInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
boolean clearFlag = false;
Signature signature = invocation.getSignature();
if (signature instanceof MethodSignature) {
MethodSignature methodSignature = (MethodSignature) signature;
if (methodSignature.getMethod().isAnnotationPresent(ReadOnly.class)) {
DynamicDataSourceContextHolder.setDataSourceKey("slaveDataSource");
clearFlag = true;
}
}
try {
return invocation.proceed();
} finally {
if (clearFlag) {
DynamicDataSourceContextHolder.clearDataSourceKey();
}
}
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 暂时不需要配置
}
}
在上述代码中,我们通过@ReadOnly
注解标记只读操作,从而触发数据源切换。
6. 实现自定义注解
创建ReadOnly
注解,用于标记只读操作:
java
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ReadOnly {
}
7. 使用注解标记只读操作
在Service层的方法上使用@ReadOnly
注解标记只读操作:
java
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@ReadOnly
public List<User> getAllUsers() {
return userMapper.selectList(null);
}
// 其他方法
}
通过以上步骤,我们成功地实现了Spring Boot与MyBatis-Plus的数据库读写分离。在只读操作上使用@ReadOnly
注解,拦截器会动态切换到从数据源,从而提升了系统的查询性能。
这种方式的优点在于配置简单,只需要在只读操作上添加注解即可。同时,该方案也具备一定的灵活性,可以根据实际情况进行调整和
扩展。在高并发的系统中,数据库读写分离是提升性能的有效手段之一,通过本文的介绍,相信读者能够在实际项目中成功应用这一技术。
后记 👉👉💕💕美好的一天,到此结束,下次继续努力!欲知后续,请看下回分解,写作不易,感谢大家的支持!! 🌹🌹🌹