使用Ebean / Play的OptimisticLockException

时间:2013-12-02 22:15:26

标签: java jpa playframework playframework-2.1 ebean

我有一个使用Ebean的Play 2.1.3 Java应用程序。我在下面得到了OptimisticLockException。

[OptimisticLockException: Data has changed. updated [0] rows sql[update person 
set name=? where id=? and email=? and name=? and password is null and created=? 
and deleted is null] bind[null]]

据我所知,它试图告诉我,当我阅读它和尝试编写它时,记录已经发生了变化。但唯一的变化就是这种方法。

public void updateFromForm(Map<String, String[]> form) throws Exception {
    this.name = form.get("name")[0];

    String password = form.get("password")[0];
    if (password != null && password.length() != 0) {
        String hash = Password.getSaltedHash(password);
        this.password = hash;
    }

    this.update();
}

我这样做错了吗?我在zentasks中看到了类似的逻辑。另外,我应该能够看到绑定变量的值吗?

UPDATE:我从控制器内部调用updateFromForm():

@RequiresAuthentication(clientName = "FormClient")
public static Result updateProfile() throws Exception {

    final CommonProfile profile = getUserProfile();
    String email = getEmail(profile);           
    Person p = Person.find.where().eq("email", email).findList().get(0);

    Map<String, String[]> form = request().body().asFormUrlEncoded();

    if (p == null) {
        Person.createFromForm(form);
    } else {
        p.updateFromForm(form);
    }

    return ok("HI");
}

4 个答案:

答案 0 :(得分:15)

我有另一种方法,我在其中添加注释

@EntityConcurrencyMode(ConcurrencyMode.NONE)

到实体类。

这会禁用乐观锁定并发修改检查,这意味着SQL变为

update person set name=? where id=?

这更加乐观,因为它只是覆盖了任何中间变化。

答案 1 :(得分:8)

有点晚了,但对于你的案例@Version,注释应该是解决方案。我们主要使用它java.util.Date,所以它也可以用于确定上次记录更新的日期,在Play模型中只是:

@Version
public java.util.Date version; 

在这种情况下,更新语句将仅使用idversion字段完成 - 尤其适用于使用大型模型时:

update person set name='Bob' 
where id=1 and version='2014-03-03 22:07:35'; 

注意:您不需要/应该在每次保存时手动更新此字段,Ebean会自行完成。 version值仅在有更新数据时更改(因此使用obj.update(),其中没有任何更改不会更新version字段)

答案 2 :(得分:4)

神秘解决了。

首先是这个公共服务公告。 “OptimisticLockException”是一个很大的优势。如果你试图追踪其中一个,那就明白它可能真的是什么。

我通过将SQL转储到日志并找到它来解决我的问题:

update person set name='Bob' 
where id=1 and email='jj@test.com' 
and name='Robert' and password is null 
and created=2013-12-01 and deleted is null

所以我猜你做更新时会发生什么,它会构建一个WHERE子句,其中包含所有已知实体及其最初准备好的值。

这意味着,如果您的代码或其他进程的任何其他部分更改了背后的内容,则此查询将失败。我错误地认为问题是以某种方式.setName('Bob')更改了DB或某个对象缓存中的名称。

真正发生的事情是WHERE子句包含一个日期,而我的数据库包含一个包含日期,时间和时区的整个时间戳。

目前,我只是通过注释模型中的时间戳来修复它,直到我能够确定Ebean是否/如何处理这种数据类型。

答案 3 :(得分:0)

我有同样的问题, 经过几个小时的搜索我找到了原因.. 它是数据库中的参数类型(在我的情况下是字符串)和我创建并尝试保存-java.util.Date的对象的不一致。

更改数据库以保存datetime对象后问题得以解决