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实现读写分离的原理和实现方法。

阅读剩余 73%

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

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

相关文章

  • idea 与 maven 使用过程中遇到的问题及解决方案

    问题描述 在使用IntelliJ IDEA和Maven进行Java开发时,可能会遇到以下问题: IDEA无法加载Maven项目 Maven依赖无法正常导入 Maven仓库无法更新 IDEA无法保存Maven的配置信息 解决方案 1. IDEA无法加载Maven项目 如果IDEA无法加载Maven项目,首先需要确保Maven已经正确安装并配置了环境变量。然后可…

    Java 2023年5月20日
    00
  • Mybatis Plus 代码生成器的实现

    MyBatis Plus 是 MyBatis 的增强工具,在 MyBatis 的基础上新增了很多实用的功能,其中的代码生成器可以帮助我们快速生成基础的代码,提升开发效率。 MyBatis Plus 代码生成器是通过配置数据库连接信息、配置表、配置输出路径等,生成对应的 Java 代码,包括实体类、DAO 接口、以及对应的 XML 映射文件,同时也支持生成 C…

    Java 2023年5月20日
    00
  • 10分钟带你徒手做个Java线程池

    摘要:花10分钟开发一个极简版的Java线程池,让小伙伴们更好的理解线程池的核心原理。 本文分享自华为云社区《放大招了,冰河带你10分钟手撸Java线程池,yyds,赶快收藏吧》,作者:冰 河。 Java线程池核心原理 看过Java线程池源码的小伙伴都知道,在Java线程池中最核心的类就是ThreadPoolExecutor,而在ThreadPoolExec…

    Java 2023年4月19日
    00
  • 详解springboot集成mybatis xml方式

    接下来我将详细讲解关于“详解Spring Boot集成MyBatis XML方式”的攻略。 1. 添加相关依赖 在pom.xml中添加以下依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-st…

    Java 2023年5月20日
    00
  • Springboot+AOP实现时间参数格式转换

    下面是”Springboot+AOP实现时间参数格式转换”的完整攻略。 1、背景 在web开发过程中,我们经常需要将前端传过来的时间参数转换成我们需要的格式。如果每次方法处理前都要手动转换,这无疑会增加代码的复杂度和开发时间。因此,我们可以利用Springboot框架提供的AOP功能,在方法执行前进行参数格式转换,从而减少开发时间和复杂度。 2、实现步骤 2…

    Java 2023年6月1日
    00
  • Java实现监听文件变化的三种方案详解

    Java实现监听文件变化的三种方案详解 在Java编程中,经常需要对文件进行监听,以便在文件发生更新时及时做出相应的处理。下面将介绍三种实现文件监听的方案,分别是Java 7的WatchService、commons-io库、以及第三方库jnotify。 1. Java 7的WatchService Java 7引入了WatchService API,它可以…

    Java 2023年5月20日
    00
  • 自定义类加载器的作用是什么?

    自定义类加载器的作用: Java类在运行时是需要被加载的。默认情况下,Java虚拟机会使用以下三种类加载器来加载类: Bootstrap ClassLoader:负责加载Java的核心类,如java.lang.Object等。 Extension ClassLoader:负责加载Java扩展库,如javax.*等。 Application(Class) Cl…

    Java 2023年5月10日
    00
  • 史上最通俗理解的Java死锁代码演示

    让我们来详细讲解一下“史上最通俗理解的Java死锁代码演示”的完整攻略。 什么是死锁 在介绍代码演示之前,我们先来了解一下什么是死锁。简单来说,死锁是指两个或多个线程互相持有对方所需要的资源,导致这些线程都在等待被对方释放占用的资源,从而陷入无限等待的状态,程序不再继续执行。 示例代码及分析 下面我们用一份简单的代码来进行演示。 public class D…

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