Я обнаружил действительно странное поведение в относительно простом случае использования, возможно, я не могу понять его из-за не глубоких знаний о природе @Transactional, но это довольно интересно.
У меня есть простой пользовательский дао, который расширяет весенний класс JpaDaoSupport и содержит стандартный метод сохранения:
@Transactional
public User save(User user) {
getJpaTemplate().persist(user);
return user;
}
Если все работало нормально, пока я не добавил новый метод в тот же класс: пользователь getSuperUser (), этот метод должен возвращать пользователя с isAdmin == true, и если в БД нет суперпользователя, метод должен его создать. Вот как это выглядело:
public User createSuperUser() {
User admin = null;
try {
admin = (User) getJpaTemplate().execute(new JpaCallback() {
public Object doInJpa(EntityManager em) throws PersistenceException {
return em.createQuery("select u from UserImpl u where u.admin = true").getSingleResult();
}
});
} catch (EmptyResultDataAccessException ex) {
User admin = new User('login', 'password');
admin.setAdmin(true);
save(admin); // THIS IS THE POINT WHERE STRANGE THING COMING OUT
}
return admin;
}
Как вы видите, код выглядит странно, и я был очень смущен, когда узнал, что транзакция не была создана и зафиксирована при вызове метода save (admin) и новый пользователь фактически не был создан, несмотря на аннотацию @Transactional.
В результате мы имеем ситуацию: когда метод save () вызывает извне класса UserDAO - аннотация @Transactional считается и пользователь успешно создается, но если save () вызывает изнутри другого метода того же класса dao - аннотация @Transactional игнорироваться.
Вот как я изменил метод save (), чтобы заставить его всегда создавать транзакцию.
public User save(User user) {
getJpaTemplate().execute(new JpaCallback() {
public Object doInJpa(EntityManager em) throws PersistenceException {
em.getTransaction().begin();
em.persist(user);
em.getTransaction().commit();
return null;
}
});
return user;
}
Как видите, я вручную запускаю begin и commit. Есть идеи?