OptimisticLockException with Ebean/Play

11

我有一个使用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中看到了类似的逻辑。另外,我应该能够看到绑定变量的值吗?

更新: 我从控制器中调用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");
}

你能否包含调用 updateFromForm 方法的代码? - mantithetical
好的,我已经更新了。我在控制器中调用updateFromForm()。updateFromForm本身是在模型中定义的。你知道吗,我的错误消息中应该看到绑定变量的值吗?感谢任何帮助。 - latj
4个回答

16

我有一种替代方案,其中我添加注释

@EntityConcurrencyMode(ConcurrencyMode.NONE)

到实体类。

这将禁用乐观锁并发修改检查,这意味着 SQL 语句变为

update person set name=? where id=?

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


@EntityConcurrencyMode注释位于哪个包中? - Oreoluwa
包 com.avaje.ebean.annotation; - Sindri Traustason

9

有点晚,但是对于你的情况,@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 字段)。

永远不会太晚。事实上,我现在在另一个项目中遇到了同样的问题。当我尝试将(at)Version添加到一个属性(一个填充有uuid的字符串)时,它会显示“读取models.MyModel注释时出错”。(at)Version是一个新的还是已弃用的功能?感谢您的任何见解。 - latj
1
不,它并不旧或过时,也许尝试使用完全限定路径@javax.persistence.Version,否则请创建新问题并展示您的模型。 - biesior

5

谜团解开了。

首先,这是一个公共服务通告。 "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如何处理这种数据类型为止。


谢谢!我的问题与此类似,是由于使用Postgres的now()函数手动插入日期所导致的。结果发现Postgres日期与Ebean日期具有不同的精度。 - duxx0r

0

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

将数据库更改为保存日期时间对象后,问题得到解决。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接