OptimisticLockException с Ebean / Play


У меня есть приложение Play 2.1.3 Java, использующее 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");
}
4 10

4 ответа:

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

@Version
public java.util.Date version; 

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

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

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

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

@EntityConcurrencyMode(ConcurrencyMode.NONE)

К классу сущностей.

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

update person set name=? where id=?
Это еще более оптимистично, так как он просто перезаписывает любые промежуточные изменения.

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

Первое-это публичное служебное объявление. "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') изменил имя в БД или каком-то кэше объектов.

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

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

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

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