OptimisticLockException с Ebean/Play

У меня есть приложение Play 2.1.3 для Play Play, использующее Ebean. Я получаю исключение 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");
}

Ответ 1

Немного поздно, но для вашего случая @Version аннотация должна быть решением. Мы используем его в основном с java.util.Date, поэтому его также можно использовать и для определения даты последнего обновления записи в модели воспроизведения, которая просто:

@Version
public java.util.Date version; 

В этом случае инструкция обновления будет выполняться только с полями id и version - особенно полезно при использовании с большими моделями:

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

Примечание: вам не нужно/должно обновлять это поле вручную при каждом сохранении, Ebean делает это самостоятельно. version значение изменяется ТОЛЬКО при обновлении данных (поэтому использование obj.update(), где ничего не изменяется, не обновляет поле version)

Ответ 2

У меня есть альтернативный подход к этому, где я добавляю аннотацию

@EntityConcurrencyMode(ConcurrencyMode.NONE)

в класс Entity.

Это отключает проверку оптимистичной блокировки одновременной модификации, означающую, что SQL становится

update person set name=? where id=?

Это еще более оптимистично, поскольку он просто перезаписывает любые промежуточные изменения.

Ответ 3

Тайна решена.

Первое - это объявление публичной службы. "OptimisticLockException" - большое ведро. Если вы пытаетесь отследить один из них, будьте готовы к идее, что это действительно может быть что-то.

Я выяснил свою проблему, сбросив SQL в журнал и обнаружив следующее:

update person set name='Bob' 
where id=1 and email='[email protected]' 
and name='Robert' and password is null 
and created=2013-12-01 and deleted is null

Итак, я думаю, что происходит, когда вы делаете обновление, так это то, что он строит предложение WHERE со всеми известными объектами и их значениями, поскольку они были изначально готовы.

Это означает, что если какая-либо другая часть вашего кода или другого процесса что-то изменит за вашей спиной, этот запрос не удастся. Я ошибочно предположил, что проблема в том, что каким-то образом .setName( "Bob" ) изменило имя в БД или кеше объектов.

На самом деле происходило то, что предложение WHERE включает дату, в то время как моя база данных содержит целую метку времени с датой, временем и часовым поясом.

В настоящее время я исправил это, просто комментируя метку времени в модели, пока не смогу выяснить, может ли /Ebean управлять этим типом данных.

Ответ 4

У меня была та же проблема, после часов поиска я нашел причину.. Это было несогласованностью типа параметров в базе данных (в моей строке ввода) и объектом, который я создал, и попытался сохранить -java.util.Date.

после изменения базы данных для хранения объекта datetime проблема решена.