сегодня я закодировал тестовый пример для своего приложения, чтобы посмотреть, как ведут себя транзакции. И я обнаружил, что ничего не работает так, как я думал.JPA и весенние транзакции - пожалуйста, объясните
У меня есть приложение Spring, использующее Hibernate как поставщик JPA, поддерживаемый MySQL. У меня есть объекты DAO, расширяющие JpaDaoSupport Spring. Они охвачены управлением транзакциями Spring.
Тестовый пример я создал как это работает: 1) Объект создается с некоторым счетчиком, установленным в 0. 2) Затем две нити созданы, которые оба вызова метода DAO incrementCounter() в петля.
Я думал, что когда методы DAO будут покрыты транзакцией, то внутри него будет только один поток (т. Е. Spring позаботится о синхронизации). Но это оказалось ложным предположением.
После (временного) решения этого вопроса путем добавления synchronized
к методу DAO я обнаружил, что Hibernate не сохраняет изменения, внесенные методом DAO, а другой поток, когда find() принадлежит сущности, имеет старые данные , Помог только явный вызов this.getJpaTemplate().flush();
.
Я также думал, что диспетчер объектов предоставит мне тот же экземпляр объекта из кеша контекста персистентности, но это также неверно. Я проверил hashCode() и equals(), и они прекрасны - на основе ключа бизнес-сущности.
Любые комментарии приветствуются, поскольку, кажется, мне не хватает некоторых базовых понятий о том, как JPA/Spring работает с транзакциями.
- Должны ли методы DAO быть
synchronized
? - Должен ли я вызвать flush() в конце каждого метода DAO?
- Является ли весна ответственной за совершение звонков методам DAO с точки зрения транзакций? (то есть не позволяя двум потокам работать с одним и тем же объектом одновременно)
- Если нет, то как это сделать?
Обратите внимание, что в моем тестовом примере я использую один объект DAO, но это должно быть ОК, поскольку фасоль Весны - это одноточие - правильно?
Спасибо за любую помощь.
public class EntityDaoImpl extends JpaDaoSupport implements EntityDao {
public synchronized void incrementCounter(String znacka)
{
String threadName = Thread.currentThread().getName();
log.info(threadName + " entering do incrementCounter().");
Entity ent = this.getJpaTemplate().find(Entity.class, znacka);
log.info("Found an entity "+ent.getZnacka()+"/"+ent.hashCode()+" - " + ObjectUtils.identityToString(ent));
log.info(threadName + ": Actual count: "+ent.getCount());
ent.setCount(ent.getCount() + 5);
int sleepTime = threadName.endsWith("A") ? 700 : 50;
try { Thread.sleep(sleepTime); }
catch(InterruptedException ex) { }
ent.setCount(ent.getCount() + 5);
this.getJpaTemplate().flush();
log.info(threadName + " leaving incrementCounter().");
}
}
Без synchronized
и flush()
, это дает мне выход, как
Thread A: Actual count: 220
...
Thread B: Actual count: 220
...
Thread A: Actual count: 240
...
Thread B: Actual count: 250
...
Thread A: Actual count: 250
... и т.д., а это означает, что одна нить перезаписаны изменения по сравнению с другой.
Спасибо за ответ. Я начал помещать прецеденты в DAO, потому что с JpaDaoSupport объекты DAO были только тонкой оболочкой, служащей для ссылки на соответствующие типы. Если я это хорошо понимаю, с другим слоем для использования, я бы закончил с другими управляемыми Spring бобами, реализующими варианты использования, AOP'ed с весенними транзакциями вместо DAO; так что это будет технически одинаково, не так ли? Какой подход вы рекомендуете позаботиться о синхронизации? Вы порекомендовали бы небольшое учебное приложение Spring 2.5 + JPA? Спасибо –
Я процитировал вам весеннюю идиому - я бы рекомендовал следовать ей, особенно если это ваш первый раз с весной. Обычно я запускаю Spring на сервере приложений Java EE, таком как Tomcat или JBOSS или WebLogic, поэтому каждый запрос запускается в своем потоке. Сервер приложений останавливает запросы на обработку. Я бы рекомендовал справочные документы Spring. – duffymo
Ну, что я делаю, это бэкэнд для веб-приложения, работающего на Tomcat. Весенняя ссылка хороша и обширна, но до сих пор не содержит подсказок по синхронизации - по крайней мере, я не нашел. Я предполагаю, что EntityManager # lock (объект объекта, LockModeType lockMode) - вот что я должен рассмотреть, не так ли? –