Mybatis 读写分离简单实现
package com.box.batisplus.db.source;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
public class DataSourceAspect {
@Pointcut("!@annotation(com.box.batisplus.annotation.Master) " +
"&& (execution(* com.box.*.service..*.select*(..)) " +
"|| execution(* com.box.*.service..*.get*(..)))"+
"|| execution(* com.baomidou.mybatisplus.extension.service..*.get*(..)))"+
"|| execution(* com.baomidou.mybatisplus.extension.service..*.list*(..)))"+
"|| execution(* com.baomidou.mybatisplus.extension.service..*.count*(..)))"+
"|| execution(* com.baomidou.mybatisplus.extension.service..*.page*(..)))")
public void readPointcut() {
}
@Pointcut("@annotation(com.box.batisplus.annotation.Master) " +
"|| execution(* com.box.*.service..*.insert*(..)) " +
"|| execution(* com.box.*.service..*.add*(..)) " +
"|| execution(* com.box.*.service..*.save*(..)) " +
"|| execution(* com.box.*.service..*.update*(..)) " +
"|| execution(* com.box.*.service..*.edit*(..)) " +
"|| execution(* com.box.*.service..*.delete*(..)) " +
"|| execution(* com.box.*.service..*.remove*(..))" +
"|| execution(* com.baomidou.mybatisplus.extension.service..*.remove*(..))" +
"|| execution(* com.baomidou.mybatisplus.extension.service..*.update*(..))" +
"|| execution(* com.baomidou.mybatisplus.extension.service..*.save*(..))")
public void writePointcut() {
}
@Before("readPointcut()")
public void read() {
DBContextHolder.slave();
}
@Before("writePointcut()")
public void write() {
DBContextHolder.master();
}
@After("readPointcut()")
public void destroy() {
log.info("DataSourceAspect.destroy is db:{}",DBContextHolder.get());
if(DBContextHolder.get() != null){
DBContextHolder.remove();
log.info("DataSourceAspect.destroy is db remove:{}",DBContextHolder.get());
}
}
}
package com.box.batisplus.config;
/**
* 数据源配置
*
* @author yuan.dingwang
* @date 2021年03月30日 15:36
*/
import com.box.batisplus.db.source.MyRoutingDataSource;
import com.box.batisplus.enums.DBTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* 关于数据源配置,参考SpringBoot官方文档第79章《Data Access》
* 79. Data Access
* 79.1 Configure a Custom DataSource
* 79.2 Configure Two DataSources
*/
@Slf4j
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.slave1")
public DataSource slave1DataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.slave2")
public DataSource slave2DataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public DataSource myRoutingDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
@Qualifier("slave1DataSource") DataSource slave1DataSource,
@Qualifier("slave2DataSource") DataSource slave2DataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DBTypeEnum.MASTER, masterDataSource);
targetDataSources.put(DBTypeEnum.SLAVE1, slave1DataSource);
targetDataSources.put(DBTypeEnum.SLAVE2, slave2DataSource);
MyRoutingDataSource myRoutingDataSource = new MyRoutingDataSource();
myRoutingDataSource.setDefaultTargetDataSource(masterDataSource);
myRoutingDataSource.setTargetDataSources(targetDataSources);
log.info("==============DataSourceConfig =====================");
return myRoutingDataSource;
}
}
################### 数据配置 start ##########################
datasource:
master:
jdbc-url: jdbc:mysql://47.103.114.48:3306/box_mall?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=GMT%2B8&useSSL=false&autoReconnect=true&failOverReadOnly=false
username: boxlife
password: boxlife2019
driver-class-name: com.mysql.cj.jdbc.Driver
druid:
initial-size: 5
max-active: 25
min-idle: 5
max-wait: 120000
min-evictable-idle-time-millis: 30000
max-evictable-idle-time-millis: 30000
time-between-eviction-runs-millis: 0
validation-query: select 1
validation-query-timeout: -1
test-on-borrow: false
test-on-return: false
test-while-idle: true
pool-prepared-statements: true
max-open-prepared-statements: 100
filters: stat
share-prepared-statements: true
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
slave1:
jdbc-url: jdbc:mysql://47.103.114.48:3306/box_mall?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=GMT%2B8&useSSL=false&autoReconnect=true&failOverReadOnly=false
username: boxlife
password: boxlife2019
driver-class-name: com.mysql.cj.jdbc.Driver
druid:
initial-size: 5
max-active: 25
min-idle: 5
max-wait: 120000
min-evictable-idle-time-millis: 30000
max-evictable-idle-time-millis: 30000
time-between-eviction-runs-millis: 0
validation-query: select 1
validation-query-timeout: -1
test-on-borrow: false
test-on-return: false
test-while-idle: true
pool-prepared-statements: true
max-open-prepared-statements: 100
filters: stat
share-prepared-statements: true
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
slave2:
jdbc-url: jdbc:mysql://47.103.114.48:3306/box_mall?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=GMT%2B8&useSSL=false&autoReconnect=true&failOverReadOnly=false
username: boxlife
password: boxlife2019
driver-class-name: com.mysql.cj.jdbc.Driver
druid:
initial-size: 5
max-active: 25
min-idle: 5
max-wait: 120000
min-evictable-idle-time-millis: 30000
max-evictable-idle-time-millis: 30000
time-between-eviction-runs-millis: 0
validation-query: select 1
validation-query-timeout: -1
test-on-borrow: false
test-on-return: false
test-while-idle: true
pool-prepared-statements: true
max-open-prepared-statements: 100
filters: stat
share-prepared-statements: true
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
################### mybatis plus start ##########################
mybatis-plus:
mapper-locations: classpath:/mapper/*Mapper.xml
#实体扫描,多个package用逗号或者分号分隔
typeAliasesPackage: com.box.member.model.entity
#typeEnumsPackage: com.baomidou.springboot.entity.enums
global-config:
#刷新mapper 调试神器
db-config:
#主键类型 0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
id-type: INPUT
#字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
field-strategy: not_empty
#驼峰下划线转换
column-underline: true
#数据库大写下划线转换
#capital-mode: true
#逻辑删除配置 注解已配置
#logic-delete-value: 1
#logic-not-delete-value: 0
db-type: mysql
refresh: true
#自定义填充策略接口实现
#meta-object-handler: com.baomidou.springboot.xxx
#自定义SQL注入器
#sql-injector: com.baomidou.mybatisplus.extension.injector.LogicSqlInjector
configuration:
map-underscore-to-camel-case: true
cache-enabled: false
package com.box.batisplus.config;
/**
* 数据源配置
*
* @author yuan.dingwang
* @date 2021年03月30日 15:36
*/
import com.box.batisplus.db.source.MyRoutingDataSource;
import com.box.batisplus.enums.DBTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* 关于数据源配置,参考SpringBoot官方文档第79章《Data Access》
* 79. Data Access
* 79.1 Configure a Custom DataSource
* 79.2 Configure Two DataSources
*/
@Slf4j
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.slave1")
public DataSource slave1DataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.slave2")
public DataSource slave2DataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public DataSource myRoutingDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
@Qualifier("slave1DataSource") DataSource slave1DataSource,
@Qualifier("slave2DataSource") DataSource slave2DataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DBTypeEnum.MASTER, masterDataSource);
targetDataSources.put(DBTypeEnum.SLAVE1, slave1DataSource);
targetDataSources.put(DBTypeEnum.SLAVE2, slave2DataSource);
MyRoutingDataSource myRoutingDataSource = new MyRoutingDataSource();
myRoutingDataSource.setDefaultTargetDataSource(masterDataSource);
myRoutingDataSource.setTargetDataSources(targetDataSources);
log.info("==============DataSourceConfig =====================");
return myRoutingDataSource;
}
}