SpringBoot+MyBatis+AOP实现读写分离的示例代码

这里就详细讲解一下"SpringBoot+MyBatis+AOP实现读写分离"的完整攻略。本文会介绍什么是读写分离,如何使用SpringBoot、Mybatis和AOP实现读写分离,以及两个示例说明。

什么是读写分离

首先,我们需要了解一下什么是读写分离。在高并发的系统中,读取数据库的操作通常是多余写入的操作的。因此,将查询请求分发到只读数据库,减少了对主数据库的压力,提高了系统的可扩展性,稳定性和性能。这就是所谓的读写分离。通常一个复杂的系统,前端的请求经过负载均衡后,将查询请求转发到从数据库,写操作转发到主数据库。

实现读写分离

接下来,我们来介绍一下如何使用SpringBoot、Mybatis和AOP实现读写分离。

我们先定义两个数据库,一个主数据库master,一个从数据库slave。在SpringBoot的配置文件中,我们需要配置主数据库和从数据库的数据源。在这里我们使用Druid连接池,配置如下:

spring:
  datasource:
    master:
      url: jdbc:mysql://localhost:3306/db?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&useSSL=false
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
    slave:
      url: jdbc:mysql://localhost:3307/db?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&useSSL=false
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver

接下来,我们使用Mybatis作为数据源,并添加读写分离的AOP切面。

首先,我们需要定义两个数据源。在此,我们可以利用Spring Boot提供的一个注解:@Primary来标识默认数据源,如下:

@Configuration
@MapperScan(basePackages= {"com.example.test.mapper"}, sqlSessionFactoryRef = "testSqlSessionFactory")
public class DataSourceConfig {
    @Bean(name = "masterDataSource")
        @Primary
        @ConfigurationProperties(prefix = "spring.datasource.master")
        public DataSource masterDataSource() {
            return DruidDataSourceBuilder.create().build();
        }

        @Bean(name = "slaveDataSource")
        @ConfigurationProperties(prefix = "spring.datasource.slave")
        public DataSource slaveDataSource() {
            return DruidDataSourceBuilder.create().build();
        }
}

注:需要先在pom.xml中引入druid依赖

定义完两个数据源后,在此基础上定义SqlSessionFactory用于执行SQL语句。我们需要创建两个SqlSessionFactory,分别使用主从两个数据源。如下:

@Bean(name = "testSqlSessionFactory")
    @Primary
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("masterDataSource") DataSource masterDataSource,@Qualifier("slaveDataSource") DataSource slaveDataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        MultipleDataSource multipleDataSource = new MultipleDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>(2);
        targetDataSources.put(DbType.MASTER.name(), masterDataSource);
        targetDataSources.put(DbType.SLAVE.name(), slaveDataSource);
        //设置数据源
        multipleDataSource.setTargetDataSources(targetDataSources);
        //设置默认数据源
        multipleDataSource.setDefaultTargetDataSource(masterDataSource);
        bean.setDataSource(multipleDataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml"));//因为同时使用了多个sqlSessionFactory,所以不加这行会出现找不到mapper文件夹的错误
        return bean.getObject();
    }

最后一步是定义AOP切面,用于动态决定到哪个数据源中进行读/写操作。我们定义一个DataSourceAspect类,利用@Around注解定义具体的切面函数,代码如下:

@Aspect
@Component
public class DataSourceAspect {
    @Around("execution(* com.example.test.mapper..*.*(..))")
    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) point.getSignature();

        //获取目标函数上的注解
        if (methodSignature.getMethod().isAnnotationPresent(DataSource.class)
                && methodSignature.getMethod().getDeclaringClass().isAnnotationPresent(DataSource.class)) {
            DataSource methodAnno = methodSignature.getMethod().getAnnotation(DataSource.class);
            DataSource classAnno = methodSignature.getMethod().getDeclaringClass().getAnnotation(DataSource.class);
            if (methodAnno != null)
                DynamicDataSourceContextHolder.setDataSource(methodAnno.value().name(), null);
            else if (classAnno != null)
                DynamicDataSourceContextHolder.setDataSource(classAnno.value().name(), null);
            //当前线程执行目标方法
            Object result = point.proceed();
            //情况本地数据源设置,将数据源设置为默认
            DynamicDataSourceContextHolder.clearDataSource();
            return result;
        }

        Class<?> clazz = point.getTarget().getClass();
        DataSource ds = clazz.getAnnotation(DataSource.class);
        if (ds != null) {
            DynamicDataSourceContextHolder.setDataSource(ds.value().name(), null);
        }
        Object result = point.proceed();
        DynamicDataSourceContextHolder.clearDataSource();

        return result;
    }
}

这个切面函数是基于方法上的@DataSource注解来选择数据源的。如果方法注解存在,则使用方法注解指定的数据源,否则,使用类注解指定的数据源。如果一个方法既没有注解也没有在类上注解,则使用默认数据源。

两个示例

下面,本文以添加数据和获取数据两个示例来演示读写分离。

示例1:添加数据

首先我们在mapper中定义一个添加数据的函数和一个查询数据的函数,代码如下:

public interface DemoMapper {
    int insert(Demo record);
    @DataSource(DbType.SLAVE)
    List<Demo> listDemosSelective(Demo record);
}

在上述代码中,我们为listDemosSelective函数添加了@DataSource(DbType.SLAVE)注解,用于向从库中执行查询操作。如下:

@Service
public class DemoService {
    @Autowired
    DemoMapper demoMapper;

    @DataSource(DbType.MASTER)
    public boolean insert(Demo demo){
        int result = demoMapper.insert(demo);
        if (result > 0)
            return true;
        return false;
    }
    @DataSource(DbType.SLAVE)
    public List<Demo> listDemosSelective(Demo record){
        return demoMapper.listDemosSelective(record);
    }
}

DemoService中,为了向主数据源中添加数据,我们为insert函数指定了@DataSource(DbType.MASTER)注解。同时使用了@DataSource(DbType.SLAVE)注解来向从库查询数据。

示例2:获取数据

我们还可以添加一个简单的API,用于获取数据。代码如下:

@SpringBootApplication
@MapperScan(basePackages = "com.example.test.mapper")
@RestController
public class TestApplication {
    @Autowired
    DemoService demoService;
    @GetMapping("/listDemo")
    public List<Demo> listDemo(){
        Demo demo = new Demo();
        demo.setId(1);
        return demoService.listDemosSelective(demo);
    }

    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }
}

在上述代码中,API是使用GET方法实现的,调用了DemoService中的listDemosSelective函数,用于从从库中查询数据。

以上就是一个简单的例子,演示了Spring Boot,Mybatis和AOP实现读写分离的原理和实现方法。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot+MyBatis+AOP实现读写分离的示例代码 - Python技术站

(0)
上一篇 2023年5月19日
下一篇 2023年5月19日

相关文章

  • Java 如何优雅的抛出业务异常

    Java 抛出业务异常是我们在日常开发中难免会遇到的问题,如何优雅的抛出业务异常已成为现代开发者必须掌握的技能之一。接下来,我将详细讲解 Java 如何优雅的抛出业务异常的完整攻略。 1. 异常的定义 在 Java 中,可以通过继承 Exception 或者 RuntimeException 来定义自己的业务异常。一般来说,Exception 异常适用于业务…

    Java 2023年5月28日
    00
  • Java程序单实例运行的简单实现

    Java程序单实例运行的简单实现 在某些情况下,我们需要保证Java程序只能运行一个实例,这就需要实现Java程序单实例运行的功能。下面是实现Java程序单实例运行的简单攻略: 1. 使用文件锁机制 使用文件锁机制实现Java程序单实例运行的方法是:在程序启动时,创建一个文件并加锁,如果文件已经被锁住,就说明已经有一个实例在运行,程序就要直接退出。下面是示例…

    Java 2023年5月19日
    00
  • spring boot项目打包成war在tomcat运行的全步骤

    下面是详细的步骤。 1.创建Spring Boot项目 首先,需要使用Spring Initializr创建一个Spring Boot项目。这里我们以创建一个简单的Spring Boot RESTful应用为例。 可以使用如下命令创建: curl https://start.spring.io/starter.zip -o myproject.zip unz…

    Java 2023年5月19日
    00
  • JSP在Linux下的安装

    以下是JSP在Linux下的安装攻略,基于Ubuntu 18.04系统,其他Linux系统可能存在细微差异。 安装Java 前往Oracle官网下载Java SE Development Kit(JDK),下载地址为:https://www.oracle.com/java/technologies/javase-downloads.html 下载完成后,将下…

    Java 2023年6月15日
    00
  • 使用Java获取Json中的数据简单示例

    下面是使用Java获取Json中的数据简单示例的完整攻略: 什么是Json? Json(全称JavaScript Object Notation)是一种轻量级的数据交换格式。Json格式数据可以使用在不同的编程语言中进行数据传递,包括Java。 Json数据可以被编码为一个字符串,并在各种网络上传输或存储。在Java中可以使用Json库来解析和生成Json数…

    Java 2023年5月28日
    00
  • Spring Security结合JWT的方法教程

    我来详细讲解一下“Spring Security结合JWT的方法教程”的完整攻略。 1. 什么是Spring Security和JWT Spring Security是一种基于框架的安全性解决方案,它为Java应用程序提供了身份验证和身份验证授权功能。 JWT(JSON Web Token)是一种身份验证和授权的标准,它将声明和签名打包在一个安全令牌中。JW…

    Java 2023年5月20日
    00
  • 教你如何写springboot接口 

    教你如何写Spring Boot接口的完整攻略 Spring Boot是一个基于Spring框架的快速开发应用程序的工具。它提供了一种快速、便捷的方式来创建基于Spring的应用程序,同时也提供了一些默认的和约定,使得开发人员可以更加专注于业务逻辑的实现。本文将详细讲解如何使用Spring Boot编写接口,并提供两个示例。 1. 创建Spring Boot…

    Java 2023年5月15日
    00
  • 整理Javascript基础语法学习笔记

    下面是“整理Javascript基础语法学习笔记”的完整攻略: 第一步:细心阅读学习笔记 首先,要认真阅读你的Javascript基础语法学习笔记,将各部分的知识点和代码实例整理出来,并归类到一个个章节中。可以采取在每个标题下面加上摘要或者关键词的方式来进行梳理,帮助自己更好的理解和记忆。 比如下面这个关于变量声明的小节,可以采用这样的方式来整理笔记: 变量…

    Java 2023年5月26日
    00
合作推广
合作推广
分享本页
返回顶部