Я пытаюсь реализовать блокировку приложения JPA/Hibernate, чтобы определить, когда клиент пытается внести изменения в устаревшую версию объекта.
Я решил выставить dto (представляющий подмножество домена) через службы REST. На данный момент я могу легко обнаруживать обновления параллельных транзакций, но я не могу заставить его работать для обнаружения обновлений «старых» сущностей. Я объясню:
- #1 2 параллельные транзакции, управляющие одной и той же сущностью, каждая из которых подключена к своему диспетчеру сущностей, должным образом защищены от грязных чтений (последняя фиксация получает исключение OptimisticLockingException)
#2 2 одновременно работающих пользователя, манипулирующих одним и тем же объектом с помощью внешнего интерфейса и совершающих две разные непараллельные транзакции, НЕ получают никаких исключений блокировки. Это потому, что при использовании DTO часть кода обновления выглядит примерно так:
- start a transaction
- получить постоянный объект для обновления от менеджера
- скопируйте то, что имеет значение из dto в этот объект
- совершить
... но ничто (JPA, Hibernate или что-то еще) никогда не проверяет согласованность между версией dto и версией объекта... (ps: попытка установить поле @Version с версией, указанной в dto, как указано JPA , привести к странным результатам)
Основываясь на том, что я видел, и на большом количестве прочитанной документации и отладки, я написал такой код:
abstract class AbstractBusinessService {
private static final Logger LOG = LoggerFactory.getLogger(AbstractBusinessService.class);
protected final void checkEntityVersion(Long givenVersion, VersionedEntity<?> entity) {
if (givenVersion == null) {
LOG.warn("no version have been provided. unable to check for concurrent modification");
} else if (entity == null) {
LOG.warn("the given entity is null");
} else if (entity.getVersion() != givenVersion.longValue()) {
throw new LockingException("The persistent entity " + entity.getClass().getName() + "#" + entity.getId()
+ " has a newer version than expected (" + givenVersion + " vs. " + entity.getVersion() + ")");
}
}
}
... вызывается перед каждой операцией "обновления"... Это, очевидно, работает как шарм, но добавляет некоторую сложность на бизнес-уровень, для темы, которая связана исключительно с сохранением и не должна быть видна на бизнес-уровне...
Прав ли я в том смысле, что это самостоятельная вещь, и нет ничего готового для реализации #2? Я что-то упускаю?? Как вы решаете эту проблему в своем развитии?
Большое спасибо за чтение,
SP
EDIT: как указано в комментариях, лучший способ, кажется,
- получить постоянную сущность из контекста (A)
- создать новую, чистую (отдельную/переходную) сущность (B)
- скопируйте все свойства (A) в (B)
- установите все свойства в (B) с тем, что исходит из DTO (может потребоваться преобразование)
- слияние (B) --> получить LockException, если @Version не соответствует
em.merge(obj)
. 4) Прибыль! Как я уже сказал, даже если бы вы не использовали DTO, это происходило бы под прикрытием Jackson/JAXB/MOxy/всего, что вы используете. - person dcsohl   schedule 18.12.2015