SpringBoot+MybatisPlus配置读写分离1.概述
继承AbstractRoutingDataSource接⼝实现读写分离配置。使⽤的主要技术如下:
SpringBoot 2.1.12.RELEASE
MybatisPlus
alibaba.druid数据库连接池
mysql数据库
SpringAop
2.配置⽂件
mybatis-plus:
# 如果是放在src/main/java⽬录下 classpath:/com/yourpackage/*/mapper/*l
# 如果是放在resource⽬录 classpath:/mapper/*l
mapper-locations: classpath:com/bbdog/dao/xml/*l
#实体扫描,多个package⽤逗号或者分号分隔
typeAliasesPackage: com.del
global-config:
#主键类型  0:"数据库ID⾃增", 1:"⽤户输⼊ID",2:"全局唯⼀ID (数字类型唯⼀ID)", 3:"全局唯⼀ID UUID";
id-type: 0
#字段策略 0:"忽略判断",1:"⾮ NULL 判断"),2:"⾮空判断"
field-strategy: 1
#刷新mapper 调试神器
refresh-mapper: true
configuration:
map-underscore-to-camel-case: true
cache-enabled: false
#配置JdbcTypeForNull
jdbc-type-for-null: 'null'
spring:
datasource:
master:
driver-class-name: sql.jdbc.Driver
url: jdbc:mysql://${spring.virtualIp}:3306/bbdog
username: master
password: ******
#----数据库连接池配置----------------------
# 下⾯为连接池的补充设置,应⽤到上⾯所有数据源中
# 初始化⼤⼩,最⼩,最⼤
initialSize: 5
minIdle: 1
maxActive: 50
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进⾏⼀次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置⼀个连接在池中最⼩⽣存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 打开PSCache,并且指定每个连接上PSCache的⼤⼩
poolPreparedStatements: false
#maxPoolPreparedStatementPerConnectionSize: 20
# 配置监控统计拦截的filters,去掉后监控界⾯sql⽆法统计,'wall'⽤于防⽕墙
filters: stat,wall,slf4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectionProperties:
druid:
stat:
mergeSql: true
slowSqlMillis: 5000
# 合并多个DruidDataSource的监控数据
#useGlobalDataSourceStat: true
slave:
driver-class-name: sql.jdbc.Driver
url: jdbc:mysql://${spring.virtualIp}:3306/bbdog
username: slave
password: ******
#----数据库连接池配置----------------------
# 下⾯为连接池的补充设置,应⽤到上⾯所有数据源中
# 初始化⼤⼩,最⼩,最⼤
initialSize: 5
minIdle: 1
maxActive: 50
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进⾏⼀次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置⼀个连接在池中最⼩⽣存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 打开PSCache,并且指定每个连接上PSCache的⼤⼩
poolPreparedStatements: false
#maxPoolPreparedStatementPerConnectionSize: 20
# 配置监控统计拦截的filters,去掉后监控界⾯sql⽆法统计,'wall'⽤于防⽕墙
filters: stat,wall,slf4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectionProperties:
druid:
stat:
mergeSql: true
slowSqlMillis: 5000
# 合并多个DruidDataSource的监控数据
#useGlobalDataSourceStat: true
3.SpringBoot启动类设置
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) // 设置动态数据源需要,禁⽤数据源⾃动配置@EnableTransactionManagement//开启springBoot事务
@MapperScan("com.bbdog.dao.mapper*")
@EnableCaching//开启基于注解的缓存
public class WebApplication {
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
}
4.创建数据源类型
public enum SourceName {
read("read"), write("write");
private String value;
SourceName(String value) {
this.value = value;
}
public String value() {
return this.value;
}
}
5.构建切换数据源类
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
/**
* 将 read 数据源的 key作为默认数据源的 key
*/
@Override
protected String initialValue() {
ad.value();
}
};
/**
* 数据源的 key集合,⽤于切换时判断数据源是否存在
*/
public static List<Object> dataSourceKeys = new ArrayList<>();
/**
* 切换数据源
*
* @param key
*/
public static void setDataSourceKey(String key) {
contextHolder.set(key);
}
/**
* 获取数据源
*
* @return
*/
public static String getDataSourceKey() {
();
}
/**
* 重置数据源
*/
public static void clearDataSourceKey() {
}
/**
* 判断是否包含数据源
*
* @param key 数据源key
* @return
*/
public static boolean containDataSourceKey(String key) {
ains(key);
}
/**
* 添加数据源keys
*
* @param keys
* @return
*/
public static boolean addDataSourceKeys(Collection<? extends Object> keys) {
return dataSourceKeys.addAll(keys);
}
}
6.继承AbstractRoutingDataSource接⼝实现动态数据源public class AutoChooseDataSource extends AbstractRoutingDataSource {
/**
* 如果不希望数据源在启动配置时就加载好,可以定制这个⽅法,从任何你希望的地⽅读取并返回数据源    * ⽐如从数据库、⽂件、外部接⼝等读取数据源信息,并最终返回⼀个DataSource实现类对象即
*/
@Override
protected DataSource determineTargetDataSource() {
return super.determineTargetDataSource();
}
/**
* 如果希望所有数据源在启动配置时就加载好,这⾥通过设置数据源Key值来切换数据,定制这个⽅法
*/
@Override
protected Object determineCurrentLookupKey() {
DataSourceKey();
}
/**
* 设置默认数据源
*
* @param defaultDataSource
*/
@Override
public void setDefaultTargetDataSource(Object defaultDataSource) {
super.setDefaultTargetDataSource(defaultDataSource);
}
/
**
* 设置数据源
*
* @param dataSources
*/
@Override
public void setTargetDataSources(Map<Object, Object> dataSources) {
super.setTargetDataSources(dataSources);
// 将数据源的 key 放到数据源上下⽂的 key 集合中,⽤于切换时判断数据源是否有效
DynamicDataSourceContextHolder.addDataSourceKeys(dataSources.keySet());
}
springboot aop}
7.数据源配置类设置
参照⾃动配置类MybatisPlusAutoConfiguration.java中的SqlSessionFactory配置来为添加⾃⼰的动态数据源@SuppressWarnings("ConstantConditions")
@t.annotation.Configuration
@ConditionalOnClass({SqlSessionFactory.class, MybatisSqlSessionFactoryBean.class})
@ConditionalOnBean(DataSource.class)//容器中有DataSource类就可以调⽤该配置类的⽅法了
@EnableConfigurationProperties(MybatisPlusProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisPlusAutoConfiguration {
...
/*
将⾃⼰配置的动态数据源放⼊容器中,容器会⾃动注⼊到该⽅法的⼊参。
由于容器中有多个DataSource类,所以要将⾃⼰的动态数据源设置为默认@Primary
*/
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
....
Object();
}
}
数据源配置类内容:
@Configuration
public class DruidConfiguration {
/**
* 动态数据源配置**********************************↓↓↓↓↓↓↓↓↓↓↓↓↓↓
***************************/
@Bean(name = "write", destroyMethod = "close", initMethod = "init")
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource master() {
return druidDataSource();
}
@Bean(name = "read", destroyMethod = "close", initMethod = "init")
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slave() {
return druidDataSource();
}
@Bean("dataSource")
@Primary//⾃动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为⾸选者,否则将抛出异常
public DataSource autoChooseDataSource() {
AutoChooseDataSource autoChooseDataSource = new AutoChooseDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>(2);
dataSourceMap.put(SourceName.write.value(), master());
dataSourceMap.ad.value(), slave());
// 将 read 数据源作为默认指定的数据源
autoChooseDataSource.setDefaultTargetDataSource(slave());
// 将 read 和 write 数据源作为指定的数据源
autoChooseDataSource.setTargetDataSources(dataSourceMap);
return autoChooseDataSource;
}
@Bean
public PlatformTransactionManager transactionManager() {
/
/ 配置事务管理, 使⽤事务时在⽅法头部添加@Transactional注解即可
return new DataSourceTransactionManager(autoChooseDataSource());
}
/**
* 动态数据源配置**********************************↑↑↑↑↑↑↑↑↑↑↑↑↑↑
***************************/
public DataSource druidDataSource() {
return new DruidDataSource();
}
}
8.创建数据源切换注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
/**
* 数据源key值
* @return
*/
SourceName value();
}
9.创建数据源切换切⾯
@Aspect
@Order(-1)  // 该切⾯应当先于 @Transactional 执⾏
@Component
public class DynamicDataSourceAspect {
private static Logger _log = Logger(DynamicDataSourceAspect.class);
/**
* 切换数据源
*
* @param point
* @param dataSource
*/
@Before("@annotation(dataSource))")
public void switchDataSource(JoinPoint point, DataSource dataSource) {
if (!ainDataSourceKey(dataSource.value().name())) {            _("DataSource [{}] 不存在,使⽤默认 DataSource [{}] ",
dataSource.value(),
} else {
// 切换数据源
DynamicDataSourceContextHolder.setDataSourceKey(dataSource.value().name());
_log.debug("切换 DataSource ⾄ [{}] ,引起切换⽅法是 [{}]",
}
}
/**
* 重置数据源
*
* @param point
* @param dataSource
*/
@After("@annotation(dataSource))")
public void restoreDataSource(JoinPoint point, DataSource dataSource) {
// 将数据源置为默认数据源
DynamicDataSourceContextHolder.clearDataSourceKey();
_log.debug("重置 DataSource ⾄ [{}] ,引起重置的⽅法是 [{}]",
}
}
10.⽰例
/**
* 删除⾓⾊
*
* @param role
* @return
*/
@Override
@DataSource(SourceName.write)
@Transactional
public R deleteRoleById(Role role) {
role.CurrentFormatDateStr());
role.setValid("0");
Integer delete = baseMapper.updateById(role);
this.RoleId());
return new R(1 == delete);
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。