北京赛车网站开发,网站开发策划方案知乎,ios网站开发教程,网站建设的教学视频1、简介
在Spring Boot中配置多个数据源并实现自动切换EntityManager#xff0c;这里我编写了一个RoutingEntityManagerFactory和AOP#xff08;面向切面编程#xff09;的方式来实现。
这里我配置了两个数据源#xff1a;primary和secondary#xff0c;其中primary主数…1、简介
在Spring Boot中配置多个数据源并实现自动切换EntityManager这里我编写了一个RoutingEntityManagerFactory和AOP面向切面编程的方式来实现。
这里我配置了两个数据源primary和secondary其中primary主数据源用来写入数据secondary从数据源用来读取数据。
注意1 使用Springboot3的读写分离首先要保证主库和从库已经配置好了 数据同步否则会导致数据不一致。 当然如果仅仅是测试的话不同步就不影响了
注意2 SpringBoot3的JDK不能低于17 我使用的JDK版本
openjdk version 20.0.2 2023-07-18
OpenJDK Runtime Environment (build 20.0.29-78)
OpenJDK 64-Bit Server VM (build 20.0.29-78, mixed mode, sharing)2、数据库说明
这里我使用了本机的同一个mysql上的两个不同的数据库在实际环境中这两个库应该是分别处于不同的服务器上同时应该已经配置好了主从复制或主备保证了数据的一致性不然读写分离就没有意义了。
数据库名称JDBC-URL说明primary_dbjdbc:mysql://localhost:3306/primary_db这个是主库设计为写入数据库secondary_dbjdbc:mysql://localhost:3306/secondary_db这个是从库设计为读取数据库 提示虽然这里我使用的是MySQL数据库但是在实际的开发过程中可以替换为Postgresql或oracle等其他关系型数据库只需要做很小的改动就可以使用了 3、准备工作
3.1、添加依赖
在你的项目里添加如下依赖
!-- Spring Web --
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId
/dependency!-- AOP --
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-aop/artifactId
/dependency!-- JPA --
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-jpa/artifactId
/dependency!-- MySQL --
dependencygroupIdcom.mysql/groupIdartifactIdmysql-connector-j/artifactIdversion8.3.0/version
/dependency!-- druid --
dependencygroupIdcom.alibaba/groupIdartifactIddruid/artifactIdversion1.2.22/version
/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope
/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId
/dependency完整的pom.xml文件
下面是我在编写代码时候的完整的pom.xml文件内容
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion3.2.3/versionrelativePath/ !-- lookup parent from repository --/parentgroupIdcom.ts/groupIdartifactIdspringboot3-jpa-read-write-separation-mysql2/artifactIdversion0.0.1-SNAPSHOT/versionnamespringboot3-jpa-read-write-separation-mysql2/namedescriptionspringBoot3 JPA MySQL 实现读写分离/descriptionpropertiesjava.version17/java.version/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency!-- Spring Boot Starter AOP for Transactional annotations --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-aop/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-jpa/artifactId/dependency!-- MySQL Database --dependencygroupIdcom.mysql/groupIdartifactIdmysql-connector-j/artifactIdversion8.3.0/version/dependency!-- druid --dependencygroupIdcom.alibaba/groupIdartifactIddruid/artifactIdversion1.2.22/version/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId/dependency/dependenciesbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build/project
3.2、准备的SQL
-- 用户表
CREATE TABLE userinfo (id int AUTO_INCREMENT PRIMARY KEY,name varchar(50),age smallint,gender varchar(3),entry_date timestamp
);-- 测试数据
INSERT INTO userinfo(name,age,gender,entry_date) VALUES
(刘峰,24,男,2024-02-28 12:01:01),
(舒航,25,女,2024-02-28 12:01:01),
(张明,26,男,2024-02-29 12:01:01),
(徐媛,28,女,2024-02-29 12:01:01),
(舒莱,29,女,2023-07-30 12:01:01),
(唐力,30,男,2023-08-05 12:01:01),
(唐莉,29,女,2023-06-05 12:01:01),
(王乐,27,男,2023-07-01 12:01:01),
(张萌,32,女,2023-07-02 12:01:01),
(程媛,25,女,2023-08-02 12:01:01),
(嫪玉,35,女,2023-08-01 10:00:00),
(贾茹,26,女,2023-10-03 12:01:01),
(胡安,25,男,2023-11-09 12:01:01),
(刘伟,27,男,2023-07-09 12:01:01);3.3、application.yml中定义多数据源配置
application.yml中定义每个数据源的连接信息。
spring:jpa:database: mysqlshow-sql: truedatasource:#主数据源 , 写入数据库primary:url: jdbc:mysql://localhost:3306/primary_db?allowMultiQueriestrueuseUnicodetruecharacterEncodingUTF-8driver-class-name: com.mysql.jdbc.Driverusername: rootpassword: root# 次数据源 , 读取数据库secondary:url: jdbc:mysql://localhost:3306/secondary_db?allowMultiQueriestrueuseUnicodetruecharacterEncodingUTF-8driver-class-name: com.mysql.jdbc.Driverusername: rootpassword: root
4、创建动态数据源
4.1、主数据源配置
package com.ts.config;import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;import javax.sql.DataSource;/*** 主数据源配置* author zhouq* since 15:26 2024/03/21**/
Configuration(proxyBeanMethods false)
EnableJpaRepositories(basePackages com.ts.service,entityManagerFactoryRef primaryEntityManagerFactory
)
public class PrimaryDataSourceConfig {Autowiredprivate JpaProperties jpaProperties;Bean(name primaryDataSource)ConfigurationProperties(prefix spring.datasource.primary)public DataSource primaryDataSource() {//return DataSourceBuilder.create().build();return new DruidDataSource();}//LocalContainerEntityManagerFactoryBeanBeanpublic Object primaryEntityManagerFactory(DataSource primaryDataSource,JpaProperties primaryJpaProperties) {EntityManagerFactoryBuilder builder createEntityManagerFactoryBuilder(primaryJpaProperties);return builder.dataSource(primaryDataSource).packages(com.ts.model).persistenceUnit(primaryDataSource).build();}private EntityManagerFactoryBuilder createEntityManagerFactoryBuilder(JpaProperties jpaProperties) {JpaVendorAdapter jpaVendorAdapter createJpaVendorAdapter(jpaProperties);return new EntityManagerFactoryBuilder(jpaVendorAdapter, jpaProperties.getProperties(), null);}private JpaVendorAdapter createJpaVendorAdapter(JpaProperties jpaProperties) {// ... map JPA properties as neededreturn new HibernateJpaVendorAdapter();}}
4.2、次要数据源配置
package com.ts.config;import com.alibaba.druid.pool.DruidDataSource;
import jakarta.persistence.EntityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;import javax.sql.DataSource;/*** 次要数据源配置* author zhouq* since 15:25 2024/03/21**/
Configuration
EnableJpaRepositories(basePackages com.ts.service,entityManagerFactoryRef secondaryEntityManagerFactory
)
public class SecondaryDataSourceConfig {Autowiredprivate JpaProperties jpaProperties;Bean(name secondaryDataSource)ConfigurationProperties(prefix spring.datasource.secondary)public DataSource secondaryDataSource() {//return DataSourceBuilder.create().build();return new DruidDataSource();}// LocalContainerEntityManagerFactoryBeanBeanpublic Object secondaryEntityManagerFactory(DataSource secondaryDataSource,JpaProperties secondaryJpaProperties) {EntityManagerFactoryBuilder builder createEntityManagerFactoryBuilder(secondaryJpaProperties);return builder.dataSource(secondaryDataSource).packages(com.ts.model).persistenceUnit(secondaryDataSource).build();}private EntityManagerFactoryBuilder createEntityManagerFactoryBuilder(JpaProperties jpaProperties) {JpaVendorAdapter jpaVendorAdapter createJpaVendorAdapter(jpaProperties);return new EntityManagerFactoryBuilder(jpaVendorAdapter, jpaProperties.getProperties(), null);}private JpaVendorAdapter createJpaVendorAdapter(JpaProperties jpaProperties) {// ... map JPA properties as neededreturn new HibernateJpaVendorAdapter();}}
5、全局EntityManager配置
5.1、创建EntityManager线程工具类
EntityManagerContextHolder是一个用来保存当前线程的EntityManager名称的工具类。
package com.ts.config;/*** 用来保存当前线程的EntityManagerFactory名称 的工具类* author zhouq* since 14:29 2024/03/20**/
public class EntityManagerContextHolder {private static final ThreadLocalString contextHolder new ThreadLocal();public static void setEntityManagerFactoryType(String entityManagerFactoryType) {contextHolder.set(entityManagerFactoryType);}public static String getEntityManagerFactoryType() {return contextHolder.get();}public static void clearEntityManagerFactoryType() {contextHolder.remove();}
}5.2、创建数RoutingEntityManagerFactory路由(管理多个EntityManager)
package com.ts.config;import jakarta.persistence.*;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.metamodel.Metamodel;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;import java.util.Map;/*** 动态切换EntityManagerFactory* EntityManagerFactory路由类* author zhouq* since 14:54 2024/03/21**/
public class RoutingEntityManagerFactory implements EntityManagerFactory{Nullableprivate MapString, Object targetEntityManagerFactorys;Nullableprivate Object defaultTargetEntityManagerFactory;Nullablepublic MapString, Object getTargetEntityManagerFactorys() {return targetEntityManagerFactorys;}public void setTargetEntityManagerFactorys(Nullable MapString, Object targetEntityManagerFactorys) {this.targetEntityManagerFactorys targetEntityManagerFactorys;}Nullablepublic Object getDefaultTargetEntityManagerFactory() {return defaultTargetEntityManagerFactory;}public void setDefaultTargetEntityManagerFactory(Nullable Object defaultTargetEntityManagerFactory) {this.defaultTargetEntityManagerFactory defaultTargetEntityManagerFactory;}public String determineCurrentLookupKey() {// 如果不使用读写分离,这里也可以做一个简单的负载均衡策略String entityManagerFactoryType EntityManagerContextHolder.getEntityManagerFactoryType();//System.out.println(当前使用的数据源是 entityManagerFactoryType);return entityManagerFactoryType;}public EntityManagerFactory determineTargetEntityManagerFactory() {Assert.notNull(this.targetEntityManagerFactorys, targetEntityManagerFactory router not initialized);Object lookupKey this.determineCurrentLookupKey();EntityManagerFactory entityManagerFactory (EntityManagerFactory)this.targetEntityManagerFactorys.get(lookupKey);if (entityManagerFactory null lookupKey null) {entityManagerFactory (EntityManagerFactory)this.defaultTargetEntityManagerFactory;}if (entityManagerFactory null) {throw new IllegalStateException(Cannot determine target EntityManagerFactory for lookup key [ lookupKey ]);} else {return entityManagerFactory;}}//---------------------下面的方法都是EntityManagerFactory接口的实现------------------------------------Overridepublic EntityManager createEntityManager() {return this.determineTargetEntityManagerFactory().createEntityManager();}Overridepublic EntityManager createEntityManager(Map map) {return this.determineTargetEntityManagerFactory().createEntityManager(map);}Overridepublic EntityManager createEntityManager(SynchronizationType synchronizationType) {return this.determineTargetEntityManagerFactory().createEntityManager(synchronizationType);}Overridepublic EntityManager createEntityManager(SynchronizationType synchronizationType, Map map) {return this.determineTargetEntityManagerFactory().createEntityManager(synchronizationType,map);}Overridepublic CriteriaBuilder getCriteriaBuilder() {return this.determineTargetEntityManagerFactory().getCriteriaBuilder();}Overridepublic Metamodel getMetamodel() {return this.determineTargetEntityManagerFactory().getMetamodel();}Overridepublic boolean isOpen() {return this.determineTargetEntityManagerFactory().isOpen();}Overridepublic void close() {this.determineTargetEntityManagerFactory().close();}Overridepublic MapString, Object getProperties() {return this.determineTargetEntityManagerFactory().getProperties();}Overridepublic Cache getCache() {return this.determineTargetEntityManagerFactory().getCache();}Overridepublic PersistenceUnitUtil getPersistenceUnitUtil() {return this.determineTargetEntityManagerFactory().getPersistenceUnitUtil();}Overridepublic void addNamedQuery(String s, Query query) {this.determineTargetEntityManagerFactory().addNamedQuery(s,query);}Overridepublic T T unwrap(ClassT aClass) {return this.determineTargetEntityManagerFactory().unwrap(aClass);}Overridepublic T void addNamedEntityGraph(String s, EntityGraphT entityGraph) {this.determineTargetEntityManagerFactory().addNamedEntityGraph(s,entityGraph);}
}
5.3、创建EntityManagerFactory全局配置类
package com.ts.config;import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;/*** 全局配置实体管理工厂 EntityManagerFactory* 配置EntityManagerFactory确定使用的EntityManagerFactory* author zhouq* since 10:59 2024/03/21**/
Configuration
public class EntityManagerFactoryConfig {AutowiredQualifier(primaryEntityManagerFactory)private Object primaryEntityManagerFactory;AutowiredQualifier(secondaryEntityManagerFactory)private Object secondaryEntityManagerFactory;Beanpublic EntityManagerFactory entityManager(){String dataSourceType EntityManagerContextHolder.getEntityManagerFactoryType();RoutingEntityManagerFactory routingEntityManagerFactory new RoutingEntityManagerFactory();MapString, Object targetEntityManagerFactorys new HashMapString, Object();targetEntityManagerFactorys.put(primary, primaryEntityManagerFactory); //主实体管理工厂targetEntityManagerFactorys.put(secondary, secondaryEntityManagerFactory); //次要实体管理工厂routingEntityManagerFactory.setTargetEntityManagerFactorys(targetEntityManagerFactorys);// 配置实体管理工厂routingEntityManagerFactory.setDefaultTargetEntityManagerFactory(primaryEntityManagerFactory);// 设置默认实体管理工厂return routingEntityManagerFactory;}Beanpublic PlatformTransactionManager transactionManager() {JpaTransactionManager tm new JpaTransactionManager();tm.setEntityManagerFactory(entityManager());return tm;}}
6、使用AOP实现EntityManager自动切换
6.1、创建AOP切面注入EntityManager类型
通过AOP在方法执行前设置EntityManagerFactory并在方法执行后清除。
package com.ts.config;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;/*** 使用AOP注入EntityManager类型* 通过AOP在方法执行前设置EntityManager并在方法执行后清除。* author zhouq* since 14:25 2024/03/20**/
Aspect
Component
public class EntityManagerFactoryAspect {Before(annotation(dataSource))public void switchDataSource(JoinPoint point, DataSourceSwitch dataSource) {//log.info(使用的数据源是: dataSource.value());System.out.println(使用的数据源是: dataSource.value());EntityManagerContextHolder.setEntityManagerFactoryType(dataSource.value());}After(annotation(dataSource))public void restoreDataSource(JoinPoint point, DataSourceSwitch dataSource) {EntityManagerContextHolder.clearEntityManagerFactoryType();}
}6.2、创建自定义注解用于标注所使用的的
DataSourceSwitch是一个自定义注解用来标识需要切换数据源的方法。
package com.ts.config;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 自定义注解用来标识需要切换数据源的方法* author zhouq* since 14:31 2024/03/20**/
Target({ElementType.METHOD})
Retention(RetentionPolicy.RUNTIME)
public interface DataSourceSwitch {String value() default primary;
}
7、创建实体类
package com.ts.model;import jakarta.persistence.*;
import lombok.*;
import lombok.experimental.Accessors;
import java.util.Date;Setter
Getter
Accessors(chain true)
AllArgsConstructor // 全参构造方法
NoArgsConstructor // 无参构造方法
//RequiredArgsConstructor
ToString
Entity
Table(name userinfo)
public class Userinfo implements java.io.Serializable{IdGeneratedValue(strategyGenerationType.IDENTITY)private int id;private String name;private int age;private String gender;private Date entry_date;}8、创建Service
创建UserService接口
package com.ts.service;import com.ts.model.Userinfo;
import java.util.List;public interface UserService {/*** 获取所有* author zhouq* since 13:33 2024/03/20* return java.util.Listcom.ts.model.Userinfo**/ListUserinfo getAll();/*** 查找包含姓名的用户* author zhouq* since 13:34 2024/03/20* param name 姓名* return java.util.Listcom.ts.model.Userinfo**/ListUserinfo queryWithName(String name);/*** 添加用户* author zhouq* since 13:39 2024/03/20* return java.util.Listcom.ts.model.Userinfo**/void addUser(Userinfo userinfo);/*** 按照id删除* author zhouq* since 13:55 2024/03/20**/void deleteUser(int id);}
创建UserService接口实现类
package com.ts.service.impl;import com.ts.config.DataSourceSwitch;
import com.ts.model.Userinfo;
import com.ts.service.UserService;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.List;/*** 基于EntityManager的实现* 这里使用 primary主数据源写入数据secondary从数据源读取数据* author zhouq* since 11:08 2024/03/21**/
Service
public class UserServiceEntityManagerImpl implements UserService {Autowiredprivate EntityManager entityManager;/*** 获取所有用户* 这里使用 DataSourceSwitch(secondary) 指定 读取的数据源* author zhouq* since 11:21 2024/03/21* return java.util.Listcom.ts.model.Userinfo**/OverrideDataSourceSwitch(secondary)public ListUserinfo getAll() {return entityManager.createQuery(select u from Userinfo u).getResultList();}/*** 查询用户模糊查询* author zhouq* since 11:20 2024/03/21* return java.util.Listcom.ts.model.Userinfo**/OverrideDataSourceSwitch(secondary)public ListUserinfo queryWithName(String name) {Query query entityManager.createQuery(select u from Userinfo u where u.name like :name);query.setParameter(name, % name %);return query.getResultList();}/*** 添加用户* 这里使用 DataSourceSwitch(primary) 指定写入的数据源* author zhouq* since 11:20 2024/03/21**/OverrideDataSourceSwitch(primary) //或者 DataSourceSwitchModifyingTransactionalpublic void addUser(Userinfo userinfo) {//使用本地原生SQL查询Query nativeQuery entityManager.createNativeQuery(insert into Userinfo(id,name,age,gender) values(:id,:name,:age,:gender));nativeQuery.setParameter(id,userinfo.getId());nativeQuery.setParameter(name,userinfo.getName());nativeQuery.setParameter(age,userinfo.getAge());nativeQuery.setParameter(gender,userinfo.getGender());//执行查询int i nativeQuery.executeUpdate();if ( i 0 ) {throw new RuntimeException(添加用户失败!);}}/*** 删除用户* 这里使用 DataSourceSwitch(primary) 指定写入的数据源* author zhouq* since 11:19 2024/03/21**/OverrideDataSourceSwitch(primary) //或者 DataSourceSwitchModifyingTransactionalpublic void deleteUser(int id) {//使用本地原生SQL查询Query nativeQuery entityManager.createNativeQuery(delete from Userinfo where id :id);nativeQuery.setParameter(id,id);//执行int i nativeQuery.executeUpdate();if ( i 0 ) {throw new RuntimeException(删除用户失败!);}}}
9、编写测试类
package com.ts;import com.ts.model.Userinfo;
import com.ts.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.Date;SpringBootTest
public class UserServiceTest {Autowiredprivate UserService service;// 测试使用从库读取 查询用户Testpublic void testGetAll() {service.getAll().forEach(System.out::println);//System.out.println(service);}// 测试使用从库读取 查询用户Testpublic void testQuery() {service.queryWithName(张).forEach(System.out::println);}// 测试使用主库写入 添加用户Testpublic void testAddUser() {service.addUser(new Userinfo(100,测试用户,25,男,new Date()));System.out.println(添加用户完成);}// 测试使用主库写入 删除用户Testpublic void testDelete() {service.deleteUser(100);System.out.println(删除用户完成);}}
小结
关于读写分离网上的示例很多但是都比较杂乱而且很多的方法都是基于SpringBoot2或者SpringBoot1的我这个是基于SpringBoot3.2.3版本实现了
上面的代码所有的都亲自测试通过有什么疑问可以留言评论。
几个截图 1、查询所有 可以看到 使用的数据源是:secondary 使用的是 读 数据源 2、模糊查询
3、添加用户 看到 使用的数据源是:primary 使用的是 写 数据源 4、删除用户