2014-08-27 89 views
7

控制器逻辑:如何解决StaleObjectStateException使用JPA和Hibernate

def updateObject() { 

    Object o = Object.get(params.id as Long) 

    o.otherObjects.clear() 

    objectDataService.saveObject(o.id) 

    OtherObject newObject = new OtherObject; 

    o.addToOtherObjects(newObject) 

    objectDataService.saveObject(o.id) 

} 

ServiceLogic

def saveObject(long profileId) { 
    o.save(flush:true) 
} 

什么的情况下,这只是工作的90%发生

问题

ERROR errors.GrailsExceptionResolver - StaleObjectStateException occurred when processing request: [GET] /controller/updateObject - parameters: 
stuff[]: data 
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.path.Object#1]. 
Stacktrace follows: 
Message: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.path.Object#1] 

我已经通过相关的问题阅读,发现merge叫你在上面看到。它解决了大约50%的案件,但不是全部。

+0

如果你将这个逻辑转移到一个服务方法中,那么你会不会更好? – 2014-09-03 15:29:46

+0

我不想超载该服务。你认为这有意义吗? – 2014-09-03 15:36:44

+0

将业务逻辑转移到服务并使您的控制器尽可能瘦(通过瘦我的意思是用更少的代码行)总是更好。这将有助于您在任何需要的地方重复使用代码,并在测试中节省大量时间。 – 2014-09-03 19:41:57

回答

2

对于我们几个不同的方法终于从经常存在的解决StaleObjectException异常:

object = object.refresh() 

检索它们解决了我们大部分的StaleObjectExceptions后刷新对象。特别是在有人有可能从别处工作过同一个对象并改变了其中一些成员(大部分问题都是由收集成员提供)的情况下。

总体项目的稳定性:

wrongly linked resources 

我们对我们并不真正需要一个特定的资源文件了404,因此忽略了它一段时间。事实证明,缺少的文件会导致会话保持打开 - 从而左右制作StaleObjects。

因此作为一个暗示,任何人面临着比平常以上(一些StaleObjects可能总是发生 - 参见上面的答案)StaleObjectExceptions:确保所有资源是否正确链接,你的开发工具(铬F12)不报告任何丢失的文件。

4

StaleObjectStateException可以自然发生在任何其他项目上。每次有两个并发事务加载相同的实体版本,并且由于乐观锁定冲突故障,每个事务都会更改该实体,导致最终执行线程失败。

你的情况有一个额外的呼叫,JPA边界以外,这可能有利于这一问题:

Object o = PObject.lock(profileId) 

每个事务都是线装,它发生在一个会话里面,这样两个相互竞争的线程将继续两个对象引用为同一个实体ID。乐观锁定目标在没有任何其他显式锁定机制的情况下可以有效工作

如果发送修改后的实体参考saveObject版本,你仍然可以得到这个例外,它意味着你有一个高的机会为两个相互竞争的线程修改同一实体。

在这种情况下,pessimistic lock会产生更好的结果,因为它涉及等待而不是optimistic fail-fast approach

+0

我更新了我的代码。 .lock()调用不再存在。我仍然有20%左右的电话出现。它不是并发的(我正在单独制作它作为单个用户,所以没有并发的编辑) – 2014-09-03 15:09:11

5

StaleObjectStateException:

正如已经注释掉了有关此异常。 在刷新(显式或隐式)期间,当会话中存在无效的版本化域对象时,版本号会发生碰撞,但不会持久保存到数据库。然后,当它再次生效并保存并刷新时,hibernate认为它已过时,因为版本号与数据库中的版本不匹配,并且抛出StaleObjectStateException。

这可以解决像。

  1. 你有做更新之前找出版本值,如果是1,那么你必须通过程序更新。在更新的特定操作。
  2. 通过使用@version注释。

关于@version:

版本号机制乐观锁通过@版本注解提​​供。 示例:@版本注解

@Entity 
public class Flight implements Serializable { 
... 
    @Version 
    @Column(name="OPTLOCK") 
    public Integer getVersion() { ... } 
} 

这里,版本属性映射到OPTLOCK列,实体管理器使用它来检测更新冲突,并防止因被覆盖更新的损失last-commit-wins策略@version

有关Grails的更多详细信息,请参阅下面的链接,它具有Git中心代码。
test for GRAILS-8937: HibernateOptimisticLockingFailureException

+0

你能否详细说明你的解决方案?我将如何使用@version注释以及我将在第一个解决方案中更新哪些内容? – 2014-09-03 15:09:36

+1

@SebastianFlückiger:那些是解决这个异常的方法,查看关于hibernate框架的更多细节以了解版本的用法。我更新了一些关于版本的细节。其他给出的链接告诉Grails相关的解决方案。从表中更新,然后通过编写代码来更新该版本。 – Rudra 2014-09-03 16:08:57

相关问题