个人主页:程序员 小侯
CSDN新晋作者
欢迎 点赞✍评论⭐收藏
✨收录专栏:Java框架
✨文章内容:Spring Boot + MyBatis-Plus
希望作者的文章能对你有所帮助,有不足的地方请在评论区留言指正,大家一起学习交流!
在现代应用程序的开发中,数据库读写分离是一种常见的优化手段,能够提升系统的性能和可扩展性。本文将介绍如何使用Spring Boot和MyBatis-Plus实现数据库读写分离,并提供详细的代码示例。
首先,在pom.xml
文件中添加Spring Boot和MyBatis-Plus的依赖:
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.4.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.23version>
dependency>
dependencies>
在application.properties
或application.yml
中配置主从数据源:
# 主数据源配置
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
创建MyBatisPlusConfig
类,配置MyBatis-Plus的分页插件和多数据源:
@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
接口,用于动态切换数据源。
创建DynamicDataSource
类,用于动态获取当前线程的数据源:
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceKey();
}
}
创建DynamicDataSourceContextHolder
类,用于设置和获取当前线程的数据源:
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();
}
}
创建DynamicDataSourceInterceptor
拦截器,用于在执行SQL前动态切换数据源:
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
注解标记只读操作,从而触发数据源切换。
创建ReadOnly
注解,用于标记只读操作:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ReadOnly {
}
在Service层的方法上使用@ReadOnly
注解标记只读操作:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@ReadOnly
public List<User> getAllUsers() {
return userMapper.selectList(null);
}
// 其他方法
}
通过以上步骤,我们成功地实现了Spring Boot与MyBatis-Plus的数据库读写分离。在只读操作上使用@ReadOnly
注解,拦截器会动态切换到从数据源,从而提升了系统的查询性能。
这种方式的优点在于配置简单,只需要在只读操作上添加注解即可。同时,该方案也具备一定的灵活性,可以根据实际情况进行调整和
扩展。在高并发的系统中,数据库读写分离是提升性能的有效手段之一,通过本文的介绍,相信读者能够在实际项目中成功应用这一技术。
后记 美好的一天,到此结束,下次继续努力!欲知后续,请看下回分解,写作不易,感谢大家的支持!!