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 проблема решена.