MySQL 主从复制是一种常见的数据库架构,它可以提高数据库的性能和可用性。动态数据源切换则可以根据业务需求,在不同场景下使用不同的数据源,比如在读多写少的场景下,可以通过切换到从库来分担主库的压力。
在本文中,我们将介绍如何在 Spring Boot 中实现 MySQL 主从复制和动态数据源切换,使用 MyBatis-Plus 进行数据库操作
#代码地址https://github.com/bangbangzhou/spring-boot-dynamic-master-slave.git
今日内容介绍,大约花费19分钟
图片
那么接下来我们开始项目实现,项目结构如下
图片
在项目的的pom.xml文件中引入Spring Boot和MyBatis-Plus的相关依赖
<?xml versinotallow="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <artifactId>spring-boot-starter-parent</artifactId> <groupId>org.springframework.boot</groupId> <version>2.7.15</version> </parent> <groupId>com.zbbmeta</groupId> <artifactId>spring-boot-dynamic-master-slave</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.20</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build></project>
在application.yml文件中配置主从数据源信息。注意这里我们要搭建主从数据库,只是在一个mysql实例中创建两个库,里面存在相同表
server: port: 8082spring: datasource: master: username: root password: root url: jdbc:mysql://localhost:3306/shiro_db?useUnicode=true&characterEncoding=utf8 driver-class-name: com.mysql.cj.jdbc.Driver slave: username: root password: root url: jdbc:mysql://localhost:3306/backend_db?useUnicode=true&characterEncoding=utf8 driver-class-name: com.mysql.cj.jdbc.Drivermybatis-plus: mapper-locations: classpath*:/mapper/**/*.xml
创建DatabaseType 枚举类型,用于切换数据源时,确定连接的是哪个数据源
在com.zbbmeta.config包下创建DatabaseType枚举类型
// 定义一个枚举类型 DatabaseType,表示系统中的数据库类型public enum DatabaseType { MASTER, // 主数据库类型 SLAVE // 从数据库类型}
在com.zbbmeta.holder包下创建一个DataSourceContextHolder类用于保存和获取当前线程使用的数据源类型
public class DatabaseContextHolder { private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<>(); public static void setDatabaseType(DatabaseType databaseType) { contextHolder.set(databaseType); } public static DatabaseType getDatabaseType() { return contextHolder.get(); } public static void clearDatabaseType() { contextHolder.remove(); }}
我们创建了一个 DynamicDataSource 类,继承 AbstractRoutingDataSource,用于实现动态数据源的切换。
AbstractRoutingDataSource 是 Spring Framework 提供的一个抽象数据源类,用于实现动态数据源切换。它允许应用程序在运行时动态地切换到不同的数据源,从而支持多数据源的场景,比如数据库读写分离、主从复制等
AbstractRoutingDataSource介绍:
在com.zbbmeta.config包下创建DynamicDataSource类
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSourceType(); }}
DynamicDataSource类中重写determineCurrentLookupKey()方法:在这个方法中,我们通过调用 DataSourceContextHolder.getDataSourceType() 来获取当前线程持有的数据源类型。这个方法的返回值将被用作数据源的 lookup key,从而实现动态切换。
在·com.zbbmeta.annotation包下创建DataSource注解类,这是一个自定义注解,用于标记在类或方法上,以指定数据源的类型。下面是对这段代码的注解说明
@Target({ ElementType.METHOD, ElementType.TYPE })@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface DataSource { DatabaseType type() default DatabaseType.SLAVE;}
注解说明:
在com.zbbmeta.aspect包下创建一个切面类DataSourceAspect,用于在执行数据库操作前动态切换数据源。
@Aspect@Component@EnableAspectJAutoProxypublic class DataSourceAspect {// 定义切点,匹配使用了 @DataSource 注解的方法 @Pointcut("@annotation(com.zbbmeta.annotation.DataSource)") public void dataSourcePointCut() {} // 环绕通知,在方法执行前后切换数据源 @Around("dataSourcePointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); // 获取方法上的 @DataSource 注解 DataSource dataSource = method.getAnnotation(DataSource.class); if (dataSource != null) { // 切换数据源类型 DatabaseContextHolder.setDatabaseType(dataSource.type()); } try { // 执行目标方法 return point.proceed(); } finally { // 清除数据源类型,确保线程安全 DatabaseContextHolder.clearDatabaseType(); } }}
在com.zbbmeta.config包下创建DataSourceConfig,用于配置主从两个数据源
@Configuration@Datapublic class DataSourceConfig { @Value("${spring.datasource.master.url}") private String dbUrl; @Value("${spring.datasource.master.username}") private String username; @Value("${spring.datasource.master.password}") private String password; @Value("${spring.datasource.master.driver-class-name}") private String driverClassName; @Value("${spring.datasource.slave.url}") private String slaveDbUrl; @Value("${spring.datasource.slave.username}") private String slaveUsername; @Value("${spring.datasource.slave.password}") private String slavePassword; @Value("${spring.datasource.slave.driver-class-name}") private String slaveDriverClassName; @Bean @ConfigurationProperties(prefix = "spring.datasource.master") public DataSource masterDataSource() { return DataSourceBuilder.create() .driverClassName(driverClassName) .url(dbUrl) .username(username) .password(password) .build(); } @Bean @ConfigurationProperties(prefix = "spring.datasource.slave") public DataSource slaveDataSource() { return DataSourceBuilder.create() .driverClassName(slaveDriverClassName) .url(slaveDbUrl) .username(slaveUsername) .password(slavePassword) .build(); }}
在com.zbbmeta.config包下创建DynamicDataSourceConfig类中配置MyBatis-Plus的相关内容。
@Configuration@MapperScan("com.zbbmeta.mapper")public class DynamicDataSourceConfig { @Autowired private DataSource masterDataSource; @Autowired private DataSource slaveDataSource; // 配置动态数据源 @Bean @Primary public DataSource dynamicDataSource() { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(DatabaseType.MASTER, masterDataSource); targetDataSources.put(DatabaseType.SLAVE, slaveDataSource); DynamicRoutingDataSource dynamicDataSource = new DynamicRoutingDataSource(); dynamicDataSource.setTargetDataSources(targetDataSources); dynamicDataSource.setDefaultTargetDataSource(masterDataSource); // 设置默认数据源 return dynamicDataSource; } // 配置 MyBatis 的 SqlSessionFactory @Bean public SqlSessionFactory sqlSessionFactory(DataSource dynamicDataSource) throws Exception { MybatisSqlSessionFactoryBean sessionFactoryBean = new MybatisSqlSessionFactoryBean(); sessionFactoryBean.setDataSource(dynamicDataSource); // 设置要扫描的 mapper 接口和 XML 文件路径 sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml")); sessionFactoryBean.setTypeAliasesPackage("com.zbbmeta.entity"); // 设置实体类包路径 return sessionFactoryBean.getObject(); } // 配置 MyBatis 的 SqlSessionTemplate @Bean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); }}
使用MybatisX生成代码,并且创建com.zbbmeta.controller包下创建TutorialController类,并且在需要切换数据源的方法上使用 @DataSource 注解,切面将根据该注解的配置在方法执行前后进行数据源切换。
图片
图片
@RestControllerpublic class TutorialController { @Autowired private TutorialService tutorialService; @DataSource @GetMapping("/list") public List<Tutorial> list(){ return tutorialService.list(); } @DataSource(type = DatabaseType.MASTER) @GetMapping("/create") public Boolean create(){ Tutorial tutorial = new Tutorial(); tutorial.setTitle("master"); tutorial.setDescription("master"); return tutorialService.save(tutorial); }}
使用POSTMAN发送请求
http://localhost:8082/listhttp://localhost:8082/create
#代码地址https://github.com/bangbangzhou/spring-boot-dynamic-master-slave.git
本文链接:http://www.28at.com/showinfo-26-44394-0.htmlSpring Boot + MyBatis-Plus 实现 MySQL 主从复制动态数据源切换
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com
上一篇: 原生 CSS 中类似 Sass 的嵌套
下一篇: C++之函数模板