使用springboot aop来实现读写分离和事物配置

首先,我们需要了解一下Spring AOP是什么,以及它是如何实现的。Spring AOP是基于JDK动态代理(基于接口)和CGLIB(基于类)实现的面向切面编程的一种框架。通过将横切逻辑与业务逻辑分离,可以更加灵活和方便地对系统进行管理,提高系统的可维护性、可扩展性和代码质量。

接下来,我们将使用Spring Boot AOP来实现读写分离和事务配置:

第一步:引入相关依赖

在pom.xml中加入以下依赖:

<dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

第二步:配置读写分离

在application.yml中进行如下配置:

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&allowMultiQueries=true
    username: root
    password: root
    hikari:
      maximum-pool-size: 10
      minimum-idle: 5
      connection-timeout: 30000
      idle-timeout: 600000
      max-lifetime: 1800000
      pool-name: DataCenter-HikariCP
    test:
      query: SELECT 1
  jpa:
    show-sql: false
    hibernate:
      ddl-auto: create-drop
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL5Dialect
        id:
          new_generator_mappings: true
        jdbc:
          fetch_size: 100
          batch_size: 50
      javax:
        persistence:
          query:
            timeout: 10000
  aop:
    proxy-target-class: true

第三步:编写切面逻辑

在Spring Boot项目中,使用@Aspect注解表明该类是切面类,然后再使用@Pointcut来定义切点,@Before、@After等注解来定义切面逻辑。

比如在我们的业务代码中,读操作以“find”开头,写操作以“save”、“delete”、“update”开头,所以我们可以使用正则表达式来匹配方法名,判断该方法是读操作还是写操作。

@Aspect
@Component
public class DataSourceAspect {

    @Pointcut("execution(* com.example.demo.dao.*.find*(..))")
    public void readPointcut(){
    }

    @Pointcut("execution(* com.example.demo.dao.*.save*(..)) " +
            "|| execution(* com.example.demo.dao.*.delete*(..)) " +
            "|| execution(* com.example.demo.dao.*.update*(..))")
    public void writePointcut(){
    }

    @Before("readPointcut()")
    public void switchSlaveDataSource(JoinPoint point){
        //从库读取
        DBContextHolder.slave();
    }

    @Before("writePointcut()")
    public void switchMasterDataSource(JoinPoint point){
        //主库写入
        DBContextHolder.master();
    }
}

这里的DBContextHolder是根据线程来判断当前使用的是主库还是从库,我们也需要在代码中编写两个数据源的配置。

public class DBContextHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static void setDataSourceKey(String dataSource){
        contextHolder.set(dataSource);
    }

    public static String getDataSourceKey(){
        return contextHolder.get();
    }

    public static void master(){
        setDataSourceKey("master");
    }

    public static void slave(){
        setDataSourceKey("slave");
    }

    public static void clear(){
        contextHolder.remove();
    }
}

第四步:配置事务管理器

在事务管理中,我们需要使用@Transactional注解来定义需要进行事务管理的方法。需要注意的是,事务管理只能应用在public方法上,并且它们必须是由其他对象调用的。

对于事务的配置,我们可以将事务管理器作为一个Bean来注入到Spring容器中。

@Configuration
@EnableTransactionManagement
public class DataSourceConfig {

    @Bean(name = "master")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource getMasterDataSource(){
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    @Bean(name = "slave")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource getSlaveDataSource(){
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    @Bean(name = "routingDataSource")
    public DataSource getRoutingDataSource(@Qualifier("master") DataSource masterDataSource,
                                            @Qualifier("slave") DataSource slaveDataSource){
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("master", masterDataSource);
        targetDataSources.put("slave", slaveDataSource);
        RoutingDataSource routingDataSource = new RoutingDataSource();
        routingDataSource.setDefaultTargetDataSource(masterDataSource);
        routingDataSource.setTargetDataSources(targetDataSources);
        return routingDataSource;
    }

    @Bean(name = "transactionManager")
    public DataSourceTransactionManager transactionManager(@Qualifier("routingDataSource") DataSource
                                                         routingDataSource) {
        return new DataSourceTransactionManager(routingDataSource);
    }
}

第五步:编写业务逻辑

最后,我们需要编写我们的业务逻辑代码。业务逻辑中的读操作以“find”开头,写操作以“save”、“delete”、“update”开头,方法名与切面逻辑中定义的正则表达式保持一致。

以简单的增删改查为例:

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    @Override
    public void save(User user) {
        userRepository.save(user);
    }

    @Transactional
    @Override
    public void deleteById(Long id) {
        userRepository.deleteById(id);
    }

    @Transactional
    @Override
    public void update(User user) {
        userRepository.save(user);
    }

    @Override
    public User findById(Long id) {
        return userRepository.findById(id).orElseThrow(() -> new RuntimeException("Not found!"));
    }
}

综上所述,我们通过引入相关依赖、配置读写分离和事务管理器、编写切面逻辑和业务逻辑,就可以实现Spring Boot AOP对读写分离和事务管理的配置。

示例一:实现读写分离

假设我们有一个User实体类和一个UserRepository,如下:

@Entity
@Table(name = "users")
@Data
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;

    private String password;
}

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findByUsername(String username);
}

我们可以使用以下测试类来测试读写分离是否配置成功:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
public class RoutingDataSourceTest {

    @Autowired
    private UserService userService;

    @Test
    public void test() {
        User user = new User();
        user.setUsername("test");
        user.setPassword("test");
        userService.save(user);
        for (int i = 0; i < 100; i++) {
            userService.findById(Long.valueOf(i+1));
        }
    }
}

在这个测试类中,我们通过在循环中调用findById方法,来检查读操作是否都是在从库上进行。

示例二:实现事务管理

我们可以通过在Service层加上@Transactional注解,来实现事务管理。比如:

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    @Override
    public void save(User user) {
        userRepository.save(user);
    }
}

在这个示例中,我们对save方法加上了@Transactional注解,使其具有事务管理的功能。具体来说,当我们在执行该方法时,如果发现有任何一步操作失败,那么整个事务都会回滚。

再例如删除用户的操作:

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    @Override
    public void deleteById(Long id) {
        userRepository.deleteById(id);
    }
}

在这个示例中,我们同样使用了@Transactional注解来表明该方法是需要进行事务管理的。如果在执行删除操作时出现了任何异常,那么整个事务都会进行回滚,防止数据不一致的问题出现。

综上所述,我们通过在Service层中引入@Transactional注解,就可以将业务逻辑代码与事务管理逻辑代码分离开来,提高代码的可维护性和可扩展性。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:使用springboot aop来实现读写分离和事物配置 - Python技术站

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

相关文章

  • Windows命令行bat批处理延迟sleep方法(批处理延时)

    下面是详细讲解“Windows命令行bat批处理延迟sleep方法(批处理延时)”的完整攻略。 1. 简介 在Windows下,命令行批处理文件(.bat)是一种非常常用的脚本文件。批处理文件可以自动执行一系列命令或者程序,并且可以通过编写简单的脚本来实现自动化的操作。在批处理脚本中,有时候需要延迟一段时间再执行某些操作,这时候就需要使用延迟(sleep)功…

    database 2023年5月22日
    00
  • MySQL数据备份之mysqldump的使用详解

    MySQL数据备份之mysqldump的使用详解 简介 在MySQL数据库管理中,备份和恢复数据是非常重要的操作。其中,使用mysqldump工具进行备份是最常见的方式之一。本文将详细介绍mysqldump工具的使用及其参数说明,以帮助用户更好地进行备份操作。 安装 在CentOS或Ubuntu系统中,mysqldump一般会随着MySQL数据库一起被安装。…

    database 2023年5月22日
    00
  • Couchbase 和 MS SQL Server 的区别

    Couchbase是一个NoSQL数据库,而MS SQL Server则是一个关系型数据库。他们之间的区别在以下几个方面: 数据模型 Couchbase的数据模型是一个非关系模型。它将数据存储在一个(或多个)JSON文档中。这些文档以集群节点之间的方式进行复制和分区。这意味着数据无需先进行规范化,文档可以包含不同数量的字段,这样Couchbase允许开发人员…

    database 2023年3月27日
    00
  • MSSQL经典语句

    MSSQL是一种关系型数据库管理系统,广泛应用于企业级应用程序和网站中。掌握MSSQL经典语句对于数据管理和开发是非常重要的。以下是MSSQL经典语句的完整攻略: 1. 创建和使用数据库 创建数据库 要创建MSSQL数据库,可以使用CREATE DATABASE语句。例如,以下语句将创建一个名为“mydatabase”的数据库: CREATE DATABAS…

    database 2023年5月21日
    00
  • mysql一键安装教程 mysql5.1.45全自动安装(编译安装)

    MySQL一键安装教程(MySQL5.1.45全自动安装 – 编译安装) 前言 MySQL 是一款开源的关系型数据库管理系统,被广泛应用于互联网行业和企业级应用中。本文主要介绍 MySQL 在 Linux 操作系统中的一键安装教程。我们将通过编译安装的方式来完成全自动安装。 准备工作 在进行 MySQL 安装前,我们需要做如下准备工作: 安装必要的编译工具和…

    database 2023年5月22日
    00
  • oracle中exp,imp的使用详解

    Oracle中exp,imp的使用详解 在Oracle数据库中,exp和imp是常用的数据导入导出工具。下面将详细讲解它们的使用方法。 exp的使用 exp用于将Oracle数据库中的数据导出至文件,通常称为Oracle数据库的备份功能。 命令格式 exp username/password[@connect-string] file=exportfile.…

    database 2023年5月21日
    00
  • 详解Redis hash哈希散列的5种使用方法

    Redis中的哈希散列被用来表示具有名称-值对的对象。在哈希散列中,我们可以存储任何类型的数据,包括数字、字符串、列表、集合和其他哈希散列。 在Redis中,哈希散列可以执行一些有趣的操作,例如添加、删除、更新、获取、迭代和扫描。 在本教程中,我们将对Redis中哈希散列的一些常见操作进行详细讲解。 创建哈希散列 在Redis中,创建哈希散列需要使用命令&q…

    Redis 2023年3月18日
    00
  • linux环境部署及docker安装redis的方法

    Linux环境部署及Docker安装Redis的方法 环境准备 一台Linux服务器:本文以Ubuntu 18.04为例 已安装Docker的服务器 步骤一:拉取Redis镜像 在Linux服务器上执行以下命令拉取Redis镜像: docker pull redis 步骤二:启动Redis容器 在Linux服务器上执行以下命令启动Redis容器: docke…

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