零基础尝试mybatis-plus读写分离
看了好几篇博友写的文章,关于spring-boot整合mybatis-plus实现读写分离,不过都是缺这少那的,跑不起来,所以自己实操了一次,做个记录实现方式为使用Aop切面
1、增加数据库枚举类
/**
* 数据库类型
*/
public enum DBTypeEnum {
/**
* 主节点
*/
MASTER,
/**
* 从
*/
SLAVE
}2、配置数据源
/**
* 多数据源配置
*/
@AutoConfigureBefore(DruidDataSourceAutoConfigure.class)
@Configuration
@ConfigurationProperties(prefix = "spring.datasource.druid")
@Data
public class DataSourceConfig {
private int initialSize;
private int maxActive;
private int minIdle;
private long maxWait;
private long minEvictableIdleTimeMillis;
private long timeBetweenEvictionRunsMillis;
private boolean testWhileIdle;
/**
* 配置主数据源
*
* @return 数据源
*/
@Bean(name = "masterDataSource")
@ConfigurationProperties(prefix = "spring.datasource.druid.master" )
public DataSource masterDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
parseDruidConfig(druidDataSource);
return druidDataSource;
}
/**
* 配置从数据源
*
* @return 数据源
*/
@Bean(name = "slaveDataSource")
@ConfigurationProperties(prefix = "spring.datasource.druid.slave")
public DataSource slaveDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
parseDruidConfig(druidDataSource);
return druidDataSource;
}
private void parseDruidConfig(DruidDataSource dataSource) {
dataSource.setInitialSize(initialSize);
dataSource.setMaxActive(maxActive);
dataSource.setMinIdle(minIdle);
dataSource.setMaxWait(maxWait);
dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
dataSource.setTestWhileIdle(testWhileIdle);
}
/**
* 配置路由数据源
*
* @param masterDataSource 主节点
* @param slaveDataSource 从节点
* @return 数据源
*/
@Bean(name = "myRoutingDataSource")
@DependsOn({"masterDataSource", "slaveDataSource"})
@Primary
public DataSource myRoutingDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
@Qualifier("slaveDataSource") DataSource slaveDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>(3);
targetDataSources.put(DBTypeEnum.MASTER, masterDataSource);
targetDataSources.put(DBTypeEnum.SLAVE, slaveDataSource);
MyRoutingDataSource myRoutingDataSource = new MyRoutingDataSource();
//设置默认数据源
myRoutingDataSource.setDefaultTargetDataSource(masterDataSource);
myRoutingDataSource.setTargetDataSources(targetDataSources);
return myRoutingDataSource;
}
}3、线程轮循切换主从数据库(多主多从的情况下适用)
/**
* 通过ThreadLocal将数据源设置到每个线程上下文中
*/
public class DataSourceContextHolder {
private static final ThreadLocal<DBTypeEnum> CONTEXT_HOLDER = new ThreadLocal<>();
private static final AtomicInteger COUNTER = new AtomicInteger(-1);
public static void set(DBTypeEnum dbType) {
CONTEXT_HOLDER.set(dbType);
}
public static DBTypeEnum get() {
return CONTEXT_HOLDER.get();
}
public static void clear(){
CONTEXT_HOLDER.remove();
}
public static void master() {
set(DBTypeEnum.MASTER);
System.out.println("切换到master");
}
public static void slave() {
//轮询
int index = COUNTER.getAndIncrement() % 2;
if (COUNTER.get() > 9999) {
COUNTER.set(-1);
}
// if (index == 0) {
// set(DBTypeEnum.SLAVE1);
// System.out.println("切换到slave1");
// } else {
// set(DBTypeEnum.SLAVE2);
// System.out.println("切换到slave2");
// }
set(DBTypeEnum.SLAVE);
System.out.println("切换到slave2");
}
}4、声明路由数据源key(多主多从的情况下适用)
/**
* 声明路由数据源key
*/
public class MyRoutingDataSource extends AbstractRoutingDataSource {
@Nullable
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.get();
}
}5、强制使用主/从数据库注解
/**
* 强制使用主数据库注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DataSourceMaster {
}/**
* 强制使用从数据库注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DataSourceSlave {
}6、切面类(从库注解会报错,不知道什么原因)
/**
* 使用aop实现数据源切换
*/
@Aspect
@Component
public class DataSourceAop {
// /**
// * 需要读的方法,切面
// */
// @Pointcut("!@annotation(com.readWriteSeparation.annotation.DataSourceSlave)" +
// "|| execution(* com.readWriteSeparation.service..*.select*(..)) " +
// "|| execution(* com.readWriteSeparation.service..*.get*(..))" +
// "|| execution(* com.readWriteSeparation.service..*.query*(..))" +
// "|| execution(* com.readWriteSeparation.service..*.find*(..)))")
// public void readPointcut() { }
/**
* 需要读的方法,切面
*/
@Pointcut("execution(* com.readWriteSeparation.service..*.select*(..)) " +
"|| execution(* com.readWriteSeparation.service..*.get*(..))" +
"|| execution(* com.readWriteSeparation.service..*.query*(..))" +
"|| execution(* com.readWriteSeparation.service..*.find*(..)))")
public void readPointcut() { }
/**
* 写切面
*/
@Pointcut("@annotation(com.readWriteSeparation.annotation.DataSourceMaster) " +
"|| execution(* com.readWriteSeparation.service..*.insert*(..))" +
"|| execution(* com.readWriteSeparation.service..*.save*(..))" +
"|| execution(* com.readWriteSeparation.service..*.add*(..))" +
"|| execution(* com.readWriteSeparation.service..*.update*(..))" +
"|| execution(* com.readWriteSeparation.service..*.edit*(..))" +
"|| execution(* com.readWriteSeparation.service..*.delete*(..))" +
"|| execution(* com.readWriteSeparation.service..*.remove*(..))")
public void writePointcut() { }
@Before("readPointcut()")
public void read() {
DataSourceContextHolder.slave();
}
@Before("writePointcut()")
public void write() {
DataSourceContextHolder.master();
}
@After("readPointcut()")
public void readAfter() {
DataSourceContextHolder.clear();
}
@After("writePointcut()")
public void writeAfter() {
DataSourceContextHolder.clear();
}
}7、.yml文件配置
spring:
datasource:
druid:
master:
username: root
password: 12345678
url: jdbc:mysql://192.168.10.15/zeroStart?characterEncoding=UTF-8&useSSL=true&requireSSL=false&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&allowMultiQueries=true
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.zaxxer.hikari.HikariDataSource
slave:
username: root
password: 12345678
url: jdbc:mysql://localhost:3306/zeroStart?characterEncoding=UTF-8&useSSL=true&requireSSL=false&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&allowMultiQueries=true
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.zaxxer.hikari.HikariDataSource8、引入的依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>2.4.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.7</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.16</version>
</dependency>配置到这就好了,业务代码就不展示了
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]