Я узнал действительно странное поведение на относительно простом варианте использования, вероятно, я не могу понять это из-за не глубокие знания пружины @Transactional природа, но это довольно интересно.
У меня есть простой Пользовательский дао, который расширяет пружинный класс JpaDaoSupport и содержит стандартный метод сохранения:
@Transactional
public User save(User user) {
getJpaTemplate().persist(user);
return user;
}
Если хорошо работал, пока я не имею, добавляет новый метод к тому же классу: Пользователь getSuperUser (), этот метод должен возвратить пользователя с isAdmin == верный, и если нет никакого суперпользователя в дб, метод должен создать тот. Это - то, как это было похоже:
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;
}
Поскольку Вы видите, что код является странным вперед, и я был очень смущен при обнаружении, что никакая транзакция не создавалась и фиксировалась на вызове сохранения (администратор), метод и никакой новый пользователь не были на самом деле созданы несмотря на @Transactional аннотацию.
В результате у нас есть ситуация: то, когда сохраняют (), метод вызывает от за пределами класса UserDAO - @Transactional считаемая аннотация и пользователь, успешно созданный, но если сохраняют () вызывает изнутри другого метода того же класса дао - @Transactional проигнорированная аннотация.
Здесь, как я был изменением, сохраняют (), метод для принуждения его всегда создает транзакцию.
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;
}
Поскольку Вы видите, что я вручную вызываю, начинают и фиксируют. Какие-либо идеи?
Я думаю, что декларативные транзакции с аннотациями реализованы на основе прокси.
Если вы обращаетесь к своей DAO через динамический прокси, он проверяет, есть ли аннотация, и обертывает ее транзакцией.
Если вы вызываете свой класс изнутри класса, перехватить этот вызов невозможно.
Чтобы избежать проблемы, вы также можете пометить метод createSuperuser аннотацией.
@Transactional
учитывается только для вызовов извне объекта. Для внутренних звонков это не так. Чтобы решить эту проблему, просто добавьте @Transactional
в свою точку входа. @Transactional
для вашего DAO - вместо этого используйте его в классах Service. Ваша проблема связана с ограничениями Spring AOP. Ответ Божо является хорошим, и вам следует подумать о рефакторинге своего кода, чтобы поддержать его советы.
Но если вы хотите, чтобы ваша транзакция работала без изменения кода, это возможно!
Spring AOP - это выбор по умолчанию в технологиях аспектов Spring. Но с некоторой конфигурацией и добавлением переплетения AspectJ это будет работать, поскольку AspectJ - гораздо более мощная технология, которая позволяет переключать точки между двумя методами в одном классе.