业务中场景中,调用多个service,事务处理中要注意的问题

由于事务处理中,DAO层的对象状态跟hibernate紧密相关,先回顾一下hibernate对象的三种状态:

  • 临时状态(new出来的domain对象),持久状态(与数据的记录对应),游离状态(拥有identity,但是与session关联断开)
  • Hibernate按照insert,update,……,delete的顺序提交所有登记的操作,所以在进行对象操作时,要以此顺序着手代码,以免发生主键冲突等莫名异常。
  • sessionFactory.getCurrentSession().clear()方法可以解决以下场景:在循环调用中,某一次抛出了异常,但是异常调用(抛出异常)所产生临时对象和正常调用所产生的对象一起被尝试存入数据库,会抛出cannot save ***, because identity is null类似的异常。通过clear当前的session,当循环中下次正常的调用发生时,session会被重新构造,正常的调用产生的数据将被存入数据库。这是一个不优雅的解决方案,正在尝试其他更好的方案。可能是dao层代码不当使用所造成。
  • session的flush方法可以解决,update某持久对象时,事务不提交或者不执行的问题(在update方法之后,显式的调用session.flush())。但通常是由于不正当使用hibernate的原因,或者事务配置不正当的情况造成。
  • Session的不同操作对对象状态的影响:
    save()方法将一个临时对象转变为持久对象。
    update()方法 将一个游离对象转变为持久对象。
    调用lock()方法将对象同Session相关联而不强制更新。
    拷贝指定对象的状态到具有相同对象标识符的持久对象。
    saveOrUpdate() 方法对于临时对象,执行save()方法,对于游离对象,执行update()方法。

    load() 方法和get()方法都可以根据对象的标识符加载对象,这两个方法加载的对象都位于Session的缓存中,属于持久对象。
    delete()方法用于从数据库中删除与持久化对象对应的记录。如果传入的是一个持久化对象,Session就执行一条 delete语句。如果传入的参数是游离对象,先使分离对象与Session关联,使它变为持久化对象,然后才计划执行一个delete语句。
    evict()方法从Session的缓存中删除一个持久对象

在业务层:service中,经常会发生一个操作调用多个service的情况。此时,事务的定义和处理需要注意以下几个关键点:

场景描述: 在A类中循环调用B类的 methodB,B类中包含对其他C, D 两个service的调用。


1.  如果在A类中仅仅只try,catch B类的methoB方法,如果第二次循环中,methodB抛出异常(methodB没有异常处理),则A类中其他的调用,产生的数据,也不能正常保存。

尽管A try catch了B的异常,但是B方法配置了事务策略,所以Spring的事务管理拦截器(TransactionInterceptor中invoke会优先捕获此异常,然后标记有关这个connection的事务做回滚。最终,当循环完成,此次事务的发起者----A类领衔的B,C,D等等业务事务,都一起进行回顾,能看到的结果是,在循环中,正常的业务数据也被回滚了,没有执行成功。


解决办法1:在异常的根源处,catch方法抛出的异常,相当于在真正执行的方法内部有异常,但是自我捕获了,那么对于spring而言,这个方法并没有抛出异常。则事务intercepter就不会提前处理此异常。


2. 不管是调用多少个service,事务的控制仍然离不开:传播机制。如果统一个类的 两个方法,在事务中,看起来代码的运行,是直接融为一体的,如果跨类,可以在日志中看到“Participating in existing transaction”的关键字,表示事务被传递到另外一个类的方法中了,两者共同工作在一个事务当中。


3. 如果事务的传播设置成:REQUIRES_NEW,则目标方法对应的事务将总是以开启新事务的方式运行。在第一点中,为了解决一起回滚的问题,可以将methodB的事务设置成REQUIRES_NEW,则一次循环开一个新的事务,如果有异常只会回滚当前循环的数据。不影响正常数据。


4. 在第一点中,会抛出“Transaction rolled back because it has been marked as rollback-only”的异常。因为spring捕获methodB的异常之后,spring在更早一步就try-catch了,同时还设置了标志位,再把catch住的异常往外抛。同时,在A类中,catch之后,事务准备进行提交,但是在提交的时候,发现标志位已经被设置了,不能再执行提交,然后执行全面的回调,则,spring会提示事务已经被设置成rollback-only了。

展开阅读全文

没有更多推荐了,返回首页