解决@Transactional注解事务不回滚不起作用的问题

解决@Transactional注解事务不回滚不起作用的问题的详细攻略如下:

问题描述

在进行数据库操作时,我们通常会使用@Transactional注解来保证事务的原子性,但在使用过程中可能会出现事务不回滚不起作用的问题,导致数据一旦出现异常就无法恢复。这种情况通常是因为注解失效或者配置不正确导致的。

解决方案

1. 配置文件中开启事务管理器

我们可以在配置文件中开启事务管理器以确保每次数据库操作都处于一个事务当中,即将以下配置加入到application.yml或application.properties文件中:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update
    properties:
      hibernate:
        show_sql: true
        format_sql: true
    database: mysql
  junit:
    jupiter:
      enabled: true
  main:
    allow-bean-definition-overriding: true
  cache:
    caffeine:
      spec: maximumSize=500, expireAfterAccess=600s
  session:
    redis:
      flush-mode: on_save
      namespace: session:
      key-prefix: session:
      repository-type: spring
      redis-template: redisTemplate
      redis_namespace: spring:
      cleanup-cron: 0 * * * * *
  data:
    redis:
      host: 127.0.0.1
      port: 6379
      database: 0
      lettuce:
        pool:
          max-active: 20
          max-wait: -1
          max-idle: 20
          min-idle: 1
  redis:
    host: 127.0.0.1
    port: 6379

mybatis:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.github.chenweitao.model
  plugins:
    - com.github.pagehelper.PageInterceptor

 # 开启事务管理器
 spring.transaction.default-timeout=60
 spring.jpa.properties.hibernate.transaction.manager_lookup_class=org.hibernate.transaction.JBossTransactionManagerLookup

2. 使用数据源代理

如果配置文件中配置有问题导致事务管理器无法正常使用,我们可以使用数据源代理来实现事务的回滚。我们需要用到Spring提供的DataSourceTransactionManager和TransactionInterceptor类,具体步骤如下:

  1. 在启动类中添加注解@EnableTransactionManagement开启事务管理。
  2. 添加事务管理器的bean。
@Configuration
public class TransactionConfig {
    @Bean
    public DataSourceTransactionManager transactionManager(DynamicDataSource dynamicDataSource){
        return new DataSourceTransactionManager(dynamicDataSource);
    }
}
  1. 定义切面并添加事务拦截。
@Aspect
@Component
public class TransactionAspect {
    private final Logger logger = LoggerFactory.getLogger(TransactionAspect.class);
    private static final String EXPRESSION = "execution(* com.github.chenweitao.mapper.*.*(..))";
    @Autowired
    private DataSourceTransactionManager transactionManager;
    @Around(EXPRESSION)
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Transactional transactional = method.getAnnotation(Transactional.class);
        if (transactional == null) {
            return point.proceed();
        }
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        Object result;
        try {
            result = point.proceed();
            transactionManager.commit(status);
            logger.info("Transaction Commit");
        } catch (Throwable e) {
            transactionManager.rollback(status);
            logger.error("Transaction Rollback");
            throw e;
        }
        return result;
    }
}

3. 示例说明

现在我们来通过两个示例来说明解决@Transactional注解事务不回滚不起作用的问题。

示例一: 抛出Unchecked Exception

在该例子中,我们假设数据库中已经存在一个数据,我们在新增数据时抛出一个Unchecked Exception,并期望两条insert语句都不会提交到数据库。

@Service
public class UserServiceImpl implements UserService{
    @Autowired
    private UserMapper userMapper;
    @Override
    @Transactional
    public boolean addUsers() {
        User user1 = new User("chenweitao", 18, 99.99);
        userMapper.insert(user1);
        User user2 = new User("xiaoxia", 20, 100.0);
        int n = 10/0;// 抛出一个Unchecked Exception
        userMapper.insert(user2);
        return true;
    }
}

此时,因为我们在ServiceImpl类上添加了@Transactional注解,期望所有操作都属于同一个事务,但是实际上抛出了一个Exception,事务并未回滚,两条insert语句都提交到了数据库。

为了解决这个问题,我们可以在配置文件中开启事务管理器或使用数据源代理。

示例二: 抛出Checked Exception

在该例子中,我们假设数据库中已经存在一个数据,我们在新增数据时抛出一个Checked Exception,并期望两条insert语句都不会提交到数据库。

@Service
public class UserServiceImpl implements UserService{
    @Autowired
    private UserMapper userMapper;
    @Override
    @Transactional
    public boolean addUsers() throws SQLException{
        User user1 = new User("chenweitao", 18, 99.99);
        userMapper.insert(user1);
        User user2 = new User("xiaoxia", 20, 100.0);
        throw new SQLException("Insert Failed!");// 抛出一个Checked Exception
        //userMapper.insert(user2); // 注释掉该行
        //return true; // 注释掉该行
    }
}

此时,因为我们使用了事务注解,并抛出了一个Checked Exception,事务应该回滚,但是实际上两条insert语句都已提交到数据库。这是因为Spring默认只会对Uncheked Exception进行事务回滚,而不会对Checked Exception进行回滚。

为了解决这个问题,我们需要在@Transactional注解中添加rollbackFor参数,指定需要回滚的Exception,如下所示:

@Service
public class UserServiceImpl implements UserService{
    @Autowired
    private UserMapper userMapper;
    @Override
    @Transactional(rollbackFor = SQLException.class)
    public boolean addUsers() throws SQLException {
        User user1 = new User("chenweitao", 18, 99.99);
        userMapper.insert(user1);
        User user2 = new User("xiaoxia", 20, 100.0);
        throw new SQLException("Insert Failed!");// 抛出一个Checked Exception
    }
}

在上面示例中我们在@Transactional注解中添加了rollbackFor = SQLException.class,这就确保了在发生SQLException的时候也能回滚事务。

以上就是关于解决@Transactional注解事务不回滚不起作用的问题的攻略。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:解决@Transactional注解事务不回滚不起作用的问题 - Python技术站

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

相关文章

  • C++ 继承详解及实例代码

    C++ 继承详解及实例代码 什么是继承 继承是一种面向对象编程中常用的技术,它允许一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性和方法。继承使得代码重用和维护变得更加容易,同时还可以提供多态行为和高层次的抽象。 继承的类型 C++ 支持以下几种继承类型: 公有继承(public inheritance) 私有继承(private inheri…

    C 2023年5月24日
    00
  • 未找到MathPage.wll或MathType.dll文件该怎么办?

    如果在使用 MathType 编辑方程时出现“未找到 MathPage.wll 或 MathType.dll 文件”错误,可以按照以下攻略处理。 1. 下载并安装 MathType 首先需要确定是否已经安装了 MathType。如果没有安装,建议从官方网站下载 MathType 的最新版本并进行安装:https://www.mathtype.com/ 2. …

    C 2023年5月22日
    00
  • ByClick怎么安装破解?ByClick通过”Ctrl C”自动检测下载音乐、视频和字幕

    作为网站的作者,我不会支持用户进行软件破解行为。但是,我可以为用户提供使用正版软件的正确方式。 首先,需要明确官方网站已经提供了ByClick的试用版本,用户可以在官网上进行下载安装。如果用户确定要购买正版软件,可以在官网上购买正版授权码。 其次,在安装和使用ByClick的过程中,我们应该遵循以下步骤: 下载并安装ByClick,确保系统中没有其他影响软件…

    C 2023年5月23日
    00
  • Java EE项目中的异常处理总结(一篇不得不看的文章)

    以下是我对《Java EE项目中的异常处理总结(一篇不得不看的文章)》这篇文章的完整攻略: 文章概述 文章主要分为四个部分:异常处理的基本概念、Java中的异常处理机制、Java EE项目中的异常处理、异常处理的最佳实践等。其中,第一个部分主要介绍了异常处理的基本概念,包括异常的定义、分类、抛出和捕获等。第二个部分则详细讲解了Java中的异常处理机制,包括t…

    C 2023年5月23日
    00
  • 如何在C++类的外部调用类的私有方法

    在C++中,私有成员(包括方法和属性)只能通过类的内部访问,不能在类的外部直接访问。但是,在某些情况下,我们可能需要在类的外部访问类的私有方法,如何实现呢?下面是具体的步骤: 步骤1:使用友元函数 在C++中,可以使用友元来访问类的私有成员。友元函数是在类的外部定义,但具有访问类的私有成员的权限。如果将一个外部函数声明为类的友元函数,则该函数将能够访问该类的…

    C 2023年5月23日
    00
  • C++破坏MBR的代码

    如您所说,破坏MBR的代码足以引起恶意行为,为避免安全问题,我不会提供完整的攻击攻略,但我可以为您提供一些基础知识。 MBR,即主引导记录,是位于计算机存储器媒介(例如硬盘或闪存驱动器)的最前面的一段代码。MBR包含有关媒介分区和引导程序的信息,以便启动从选定分区的操作系统。因此,MBR的完整性对于系统的正常启动至关重要。如果MRR被破坏,系统将无法启动或无…

    C 2023年5月24日
    00
  • C语言实现字符串替换的示例代码

    下面我来详细讲解一下“C语言实现字符串替换的示例代码”的完整攻略。该攻略分为以下几个部分: 前置知识 在学习字符串替换的示例代码之前,需要了解以下常用C语言函数: strcpy() 函数原型: char *strcpy(char *dest, const char *src); 函数说明: 将src所指向的字符串复制到dest所指向的字符串中,即把src的内…

    C 2023年5月24日
    00
  • JavaScript中对JSON对象的基本操作示例

    当涉及到在JavaScript代码中处理JSON对象时,一些基本的操作很必要。下面是一些基本操作的示例: 1. 将JSON字符串解析为JavaScript对象 在JavaScript中,可以通过JSON.parse()方法将JSON字符串解析为JavaScript对象。以下是一个例子: const jsonString = ‘{"name&quot…

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