`

两个与spring事务相关的问题

 
阅读更多

有些spring相关的知识点之前一直没有仔细研究:比如spring的事务,并不是没有使用,也曾经简单的在某些需要事务处理的方法上通过增加事务注解来实现事务功能,仅仅是跟随使用(甚至并未测试过事务的正确性),至于如何在项目中配置事务,如何才能将事务写正确,事务的其它的一些原理性的东西从未花时间研究。最近同事正好抛出了一个问题,借此机会学习了一遍。


  
问题一:增加了readOnly=true的事务中包含写操作,为什么线上运行这段代码是正常的呢?
 

@Transactional(readOnly = true)
    public Integer getUID(String key, String type) {
        keyGeneratorDao.insert(key, type);
        keyGeneratorDao.update(key, type);
        return keyGeneratorDao.select(key, type);
    }

    
我为什么对这个问题感兴趣?

  • 不懂这个readOnly参数的含义,之前写@Transactional的注解,那都是使用的默认值,不带显示参数。
  • 提出配置了readOnly参数后,理论上应该程序报错而实际上没有报错,想搞清楚为什么。


开始写单元测试:

  • 在单元测试类中写事务函数以及测试方法
复制代码
    @Autowired
    private IkeyGeneratorDao keyGeneratorDao;

    @Transactional(readOnly=true)
    public Integer getId(String key, String type){
        return keyGeneratorDao.select(key, type);
    }
    @Transactional
    public Integer getUID(String key, String type) {
        keyGeneratorDao.insert(key, type);
        keyGeneratorDao.update(key, type);
        return this.getId(key,type);
    }
    @Test
    public void testCreateGuid(){
        int guid=this.getUID("12345", "jim");
        System.out.println(guid);
    }
复制代码


        测试结果显示正常,与上面提到的不允许进行写操作的观点相反,于是想起典型的事务生效问题。

        挖的第一个坑:如果事务采用的是cglib动态代理,调用的方法与事务方法处在同一个类中事务不生效。

  • 将两个事务事务转移到单独的类中,然后测试,类代码省略,只是将上面两个标记了@Transactional的方法封装在一个单独的类中。      
复制代码
    @Autowired
    private KeyGeneratorService keyService;

    @Test
    public void testCreateGuid2(){
        int guid=this.keyService.getUID("12345", "jim");
        System.out.println(guid);
    }
复制代码


       测试结果显示也是正常,于是想确认下事务到底是否生效,加入异常以测试数据是否回滚,修改代码如下:

    @Transactional
    public Integer getUID3(String key, String type) {
        keyGeneratorDao.insert(key, type);
        Integer.parseInt("aaa");//throw exception
        keyGeneratorDao.update(key, type);
}

        测试结果显示事务回滚正常,可以排除事务环境配置问题。

        挖的第二个坑:做测试一定要与原问题代码尽量保持一致,否则会产生其它的不明原因影响判断。通过对比原问题的代码发现我写的测试代码与问题代码有区别,readOnly是加在包含有写操作的方法上,而我的是两个方法,只有在读的方法上增加了readOnly,于时再次修改代码:
       

    @Transactional(readOnly = true)
    public Integer getUID4(String key, String type) {
        keyGeneratorDao.insert(key, type);
        keyGeneratorDao.update(key, type);
        return keyGeneratorDao.select(key, type);
    }


        测试结果显示运行不正常,提示如下错误:Cause: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed,到这的确说明在在加了readOnly=true的事务内是不允许写入操作的。为什么这段代码在线上运行是成功的呢,于时查看前端的调用,发现前端调用的并不是直接标识了Transactional的方法,而是根据不同的具体业务重新包装的方法,比如我们需要生成订单的编号,前端只调用genOrderCode而不调用getUID。

 

  • 非事务方法在内部调用了本类事务方法,然后非事务方法被外部调用
    • Service
      • genOrderCode,是一个非事务方法,内部调用了getUID
      • getUID,是一个事务方法
复制代码
    @Autowired
    private IkeyGeneratorDao keyGeneratorDao;

    @Transactional(readOnly = true)
    public Integer getUID(String key, String type) {
        keyGeneratorDao.insert(key, type);
        keyGeneratorDao.update(key, type);
        return keyGeneratorDao.select(key, type);
    }

    public String genOrderCode(Date orderDate)
    {
        SimpleDateFormat df = new SimpleDateFormat("yyMMddHH");
        String ticketDate = df.format(orderDate);
        Integer uid = getUID(ticketDate, ORDER_CODE);        

        return ticketDate + genRandom2(uid.toString(), 3, 3) ;
    }
复制代码
    • Test,外部类调用非事务方法
    @Test
    public void testCreateGuid3(){
        String guid=this.keyService.genOrderCode(new Date());
        System.out.println(guid);
    }

         
       测试结果居然是正常的,这与线上运行的结果相同,后面经同事提醒,这又是一个不正确使用事务的案例。

       挖的第三个坑:当调用一个类的非事务方法且这个非事务方法内部调用了本类自身的事务方法,那么事务也不会生效。

问题二:下面的代码可以实现事务回滚吗?

  • Service
    • genOrderCode方法调用getUID2
    • 两个方法都是具备相同的事务参数
    • getUID2抛出异常
    • genOrderCode捕获这个异常
复制代码
    @Transactional
    public Integer getUID2(String key, String type) {
        keyGeneratorDao.insert(key, type);
        Integer.parseInt("aaa");//throw exception
        keyGeneratorDao.update(key, type);
        return keyGeneratorDao.select(key, type);
    }

    @Transactional
    public String genOrderCode(Date orderDate)
    {
        try{
            SimpleDateFormat df = new SimpleDateFormat("yyMMddHH");
            String ticketDate = df.format(orderDate);
            Integer uid = getUID2(ticketDate, ORDER_CODE);        

            return ticketDate + genRandom2(uid.toString(), 3, 3) ;
        }catch(Exception ex){
            System.out.println(ex);
        }
        return null;
    }
复制代码
  • Test
    @Test
    public void testCreateGuid3(){
        String guid=this.keyService.genOrderCode(new Date());
        System.out.println(guid);
    }


      执行测试代码,发现可以成功提交,但数据是不完整的,因为更新操作没有完成。为什么会是这样的呢?因为默认的Propagation.REQUIRED指明多个操作处于一个事务中,由于genOrderCode有异常处理,所以即使getUID2中发生异常,系统也会认定提交是合法的,因此会出现插入操作正常更新不正常但事务正常提交并不回滚的情况。

    如果显示指定Propagation.REQUIRES_NEW呢?

复制代码
    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public Integer getUID2(String key, String type) {
        keyGeneratorDao.insert(key, type);
        Integer.parseInt("aaa");//throw exception
        keyGeneratorDao.update(key, type);
        return keyGeneratorDao.select(key, type);
    }
复制代码


    再执行相同的测试,数据正常回滚,这里提供两张图,可以看的清楚些(因为常用的就这两种,其它的有兴趣可以多多研究)

  • REQUIRED

       

  • REQUIRES_NEW

       


   通过事务的两个小问题,总结出解决问题的一些小技巧或者叫经验:发现问题之后,不要局限于某个点,最好根据上下文来结合分析,比如问题一的readonly可写入,单看那段代码很难找出合理的解释,只有结合前后端调用才能找出根本原因。写单元测试尽量写相同的代码,否则有可能会出现一些干扰项影响判断。学习呢,有时间尽量学的全点,比如@Transactional这个注解,除了readOnly还有Propagation等等。

 

转自:http://www.cnblogs.com/ASPNET2008/p/5570237.html

分享到:
评论

相关推荐

    spring事务与数据库操作

    ### Spring事务与数据库操作 #### 一、Spring的声明式事务管理 在现代软件开发中,事务处理是非常关键的一部分,特别是在涉及多个数据操作时。Spring框架提供了强大的事务管理能力,可以方便地集成到应用程序中。...

    Spring事务流程图

    Spring事务管理是Spring框架的核心特性之一,主要用于处理应用程序中的数据一致性问题。在Spring中,事务管理分为编程式和声明式两种方式。本篇文章将详细解释Spring事务管理的流程,以及如何通过时序图来理解这一...

    Spring事务管理Demo

    在压缩包中的Spring事务管理练习,你可以尝试创建一个简单的示例,例如模拟两个银行账户转账的过程,通过开启事务确保转账的原子性,即转账操作要么全部成功,要么全部失败。这样可以帮助你更好地理解Spring事务管理...

    Spring事务原理、Spring事务配置的五种方式

    Spring事务原理围绕着两个核心:PlatformTransactionManager和TransactionStatus。 PlatformTransactionManager是Spring提供的用于管理事务的基础接口,其下有一个实现的抽象类AbstractPlatformTransactionManager...

    Hibernate缓存与spring事务详解

    **标题:“Hibernate缓存与Spring事务详解”** 在IT领域,尤其是Java开发中,Hibernate作为一款流行的ORM(对象关系映射)框架,极大地简化了数据库操作。而Spring框架则以其全面的功能,包括依赖注入、AOP(面向切...

    Spring自定义切面事务问题

    - **Spring 事务管理**:Spring 提供了两种事务管理方式:编程式事务管理和声明式事务管理。声明式事务管理通常更受欢迎,因为它可以通过简单的配置实现,而无需编写额外的代码。 2. **Spring 自定义切面事务失效...

    aop与spring事务处理

    ### AOP与Spring事务处理详解 #### 一、引言:为什么使用框架和设计模式? 在软件开发领域,设计模式和框架是两个重要的概念。设计模式作为一种指导思想,能够帮助开发者更好地解决常见的软件设计问题,确保系统...

    Spring事务管理开发必备jar包

    本资源包提供了进行Spring事务管理开发所需的所有关键库,包括框架基础、核心组件、AOP(面向切面编程)支持、日志处理、编译工具以及与数据库交互的相关jar包。下面将对这些知识点进行详细解释: 1. **Spring框架*...

    spring事务操作试验

    本文将深入探讨在"spring事务操作试验"中涉及的关键知识点,并结合提供的资源进行详细阐述。 首先,Spring事务管理的核心概念是ACID(原子性、一致性、隔离性和持久性),这是所有事务系统的基础。在Spring中,事务...

    spring3.0两种事务管理配置

    Spring 3.0 提供了两种事务管理配置方法:基于 XML 的事务管理和基于 @Transactional 的事务管理,这两种方法都是为了实现事务管理的目标,分别具有不同的配置方式和优缺点。 基于 XML 的事务管理 这种方法不需要...

    Spring事务与Java事务比较

    Spring 框架中的事务管理分为两种主要方式:编程式事务管理和声明式事务管理。 1. 编程式事务管理:在这种方式下,开发者需要在代码中显式调用开始事务、提交事务、回滚事务等方法。这种方式灵活性高,但可能导致...

    spring学习事务源码

    首先,Spring事务管理有两种主要模式:编程式事务管理和声明式事务管理。编程式事务管理通过调用`PlatformTransactionManager`接口提供的方法进行显式控制,如`beginTransaction()`, `commit()`, 和`rollback()`。...

    Spring事务管理失效原因汇总

    标题“Spring事务管理失效原因汇总”指出了本文的核心内容是分析在使用Spring框架进行事务管理时可能遇到的问题及其原因。描述部分进一步说明了事务失效的后果往往不明显,容易在测试环节被忽略,但在生产环境中出现...

    Spring中事务的传播属性详解

    假设我们有两个服务类`ServiceA`和`ServiceB`,其中`ServiceA`调用了`ServiceB`的方法。 ```java @Service public class ServiceA { @Autowired private ServiceB serviceB; @Transactional(propagation = ...

    实验 spring 声明事务

    实验 "Spring 声明事务" 是 Java 高级编程中的一个重要环节,旨在让学生掌握 Spring 框架中声明式事务管理的配置和使用。...这将有助于他们在未来开发中更好地处理事务相关的复杂问题,确保应用程序的数据一致性。

    Spring 事务简单完整例子

    你可以根据这个文件进一步了解和学习Spring事务管理的实践。 总结来说,Spring的事务管理提供了强大且灵活的工具,使得开发者能够在不关心底层实现的情况下,轻松地管理数据库事务。无论是编程式还是声明式,都能...

    Spring事务管理的jar包

    Spring事务管理分为编程式事务管理和声明式事务管理两种方式。编程式事务管理通过使用PlatformTransactionManager接口的begin、commit、rollback等方法直接控制事务的生命周期,这种方式灵活但容易导致代码过于繁琐...

    SPRING事务机制DEMO

    Spring事务机制是Java开发中非常重要的一个概念,它在企业级应用中扮演着核心角色,确保数据的一致性和完整性。Spring提供了多种事务管理方式,包括编程式事务管理和声明式事务管理。在这篇DEMO中,我们将重点探讨...

    spring 事务传播与隔离级别DEMO

    本DEMO主要探讨的是Spring事务的传播行为和隔离级别,这些概念对于理解和优化数据库操作至关重要。让我们深入理解这些概念及其实际应用。 首先,我们来谈谈事务的传播行为。在Spring中,当一个方法被另一个具有事务...

Global site tag (gtag.js) - Google Analytics