文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Spring Boot + MyBatis-Plus 实现 MySQL 主从复制动态数据源切换

2024-11-30 03:31

关注

在本文中,我们将介绍如何在 Spring Boot 中实现 MySQL 主从复制和动态数据源切换,使用 MyBatis-Plus 进行数据库操作

#代码地址
https://github.com/bangbangzhou/spring-boot-dynamic-master-slave.git

今日内容介绍,大约花费19分钟

图片

那么接下来我们开始项目实现,项目结构如下

图片

1.引入依赖

在项目的的pom.xml文件中引入Spring Boot和MyBatis-Plus的相关依赖



    4.0.0

    
        spring-boot-starter-parent
        org.springframework.boot
        2.7.15
    

    com.zbbmeta
    spring-boot-dynamic-master-slave
    1.0-SNAPSHOT

    
        11
        11
    

    
        
            org.springframework.boot
            spring-boot-starter-web
        

        
            mysql
            mysql-connector-java
            8.0.30
        

        
            com.baomidou
            mybatis-plus-boot-starter
            3.5.3
        

        
            cn.hutool
            hutool-all
            5.8.20
        

        
            org.projectlombok
            lombok
        


        
            org.springframework.boot
            spring-boot-starter-aop
        

    
    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
                
                    
                        
                            org.projectlombok
                            lombok
                        
                    
                
            
        
    

2. 配置数据源

在application.yml文件中配置主从数据源信息。注意这里我们要搭建主从数据库,只是在一个mysql实例中创建两个库,里面存在相同表

server:
  port: 8082

spring:
  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.Driver


mybatis-plus:
  mapper-locations: classpath*:/mapper*.xml

3. 创建DatabaseType 枚举类型

创建DatabaseType 枚举类型,用于切换数据源时,确定连接的是哪个数据源

在com.zbbmeta.config包下创建DatabaseType枚举类型

// 定义一个枚举类型 DatabaseType,表示系统中的数据库类型
public enum DatabaseType {
    MASTER,  // 主数据库类型
    SLAVE    // 从数据库类型
}

4. 配置数据源上下文

在com.zbbmeta.holder包下创建一个DataSourceContextHolder类用于保存和获取当前线程使用的数据源类型

public class DatabaseContextHolder {

    private static final ThreadLocal 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();
    }
}

5. 配置动态数据源

我们创建了一个 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,从而实现动态切换。

6. 添加DataSource注解类

在·com.zbbmeta.annotation包下创建DataSource注解类,这是一个自定义注解,用于标记在类或方法上,以指定数据源的类型。下面是对这段代码的注解说明

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {

    DatabaseType type() default DatabaseType.SLAVE;

}

注解说明:

7. 配置数据源切换切面

在com.zbbmeta.aspect包下创建一个切面类DataSourceAspect,用于在执行数据库操作前动态切换数据源。

@Aspect
@Component
@EnableAspectJAutoProxy
public 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();
        }
    }
}

8. 创建DataSourceConfig

在com.zbbmeta.config包下创建DataSourceConfig,用于配置主从两个数据源

@Configuration
@Data
public 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();

    }
}

9 创建DataSourceConfig

在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 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);
    }
}

10. 测试

使用MybatisX生成代码,并且创建com.zbbmeta.controller包下创建TutorialController类,并且在需要切换数据源的方法上使用 @DataSource 注解,切面将根据该注解的配置在方法执行前后进行数据源切换。

图片

图片

@RestController
public class TutorialController {


    @Autowired
    private TutorialService tutorialService;


    @DataSource
    @GetMapping("/list")
    public List 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/list

http://localhost:8082/create
#代码地址
https://github.com/bangbangzhou/spring-boot-dynamic-master-slave.git
来源:springboot葵花宝典内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯