Странные befaviour пружинной поддержки транзакции JPA + В спящем режиме + @Transactional аннотация

Я узнал действительно странное поведение на относительно простом варианте использования, вероятно, я не могу понять это из-за не глубокие знания пружины @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;
}

Поскольку Вы видите, что я вручную вызываю, начинают и фиксируют. Какие-либо идеи?

8
задан Espen 26 May 2010 в 10:04
поделиться

3 ответа

Я думаю, что декларативные транзакции с аннотациями реализованы на основе прокси.

Если вы обращаетесь к своей DAO через динамический прокси, он проверяет, есть ли аннотация, и обертывает ее транзакцией.

Если вы вызываете свой класс изнутри класса, перехватить этот вызов невозможно.

Чтобы избежать проблемы, вы также можете пометить метод createSuperuser аннотацией.

7
ответ дан 5 December 2019 в 10:39
поделиться
  1. @Transactional учитывается только для вызовов извне объекта. Для внутренних звонков это не так. Чтобы решить эту проблему, просто добавьте @Transactional в свою точку входа.
  2. Не используйте @Transactional для вашего DAO - вместо этого используйте его в классах Service.
9
ответ дан 5 December 2019 в 10:39
поделиться

Ваша проблема связана с ограничениями Spring AOP. Ответ Божо является хорошим, и вам следует подумать о рефакторинге своего кода, чтобы поддержать его советы.

Но если вы хотите, чтобы ваша транзакция работала без изменения кода, это возможно!

Spring AOP - это выбор по умолчанию в технологиях аспектов Spring. Но с некоторой конфигурацией и добавлением переплетения AspectJ это будет работать, поскольку AspectJ - гораздо более мощная технология, которая позволяет переключать точки между двумя методами в одном классе.

0
ответ дан 5 December 2019 в 10:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: