Spring @Transactional проблема/проблема при размещении на сервисном уровне

Мое приложение Spring состоит из Bean, Service и DAO. Все аннотации @Transactional находятся на сервисном уровне.

Это псевдокод в одном конкретном сценарии.

UserBean.java

saveUser() {
    userService.manageUser();
}

UserServiceImpl.java

@Transactional
public void manageUser() {
    userDAO.createUser();
    userDAO.updateParentUser();
}

UserDAOImpl.java

createUser() {
    //insert user record in database
}

updateParentUser() {
    //update parent user's database record
}

В моем тестовом примере сохранения пользователя операция обновления родительского пользователя может завершиться ошибкой в ​​некоторых случаях из-за нарушения первичного ключа, что вполне ожидаемо.

Поскольку аннотация @Transactional реализована в классе обслуживания, это исключение нарушения будет уведомлено только в классе компонента.

Как можно получить это уведомление о нарушении PK в моем классе обслуживания? [Тогда я могу обработать это оттуда в другом бизнес-процессе.]

Если я добавлю новый метод в класс обслуживания и вызову manageUser() оттуда, аннотация @Transactional не будет работать должным образом. Это связано с ограничением/свойством АОП. Spring ожидает внешний вызов методов @Transactional.


person Sam    schedule 02.07.2015    source источник
comment
Что вы имеете в виду, когда говорите, что это исключение нарушения будет уведомлено только в классе компонента. Конечно, если исключение генерируется в классе обслуживания, то вы также можете поймать его в классе обслуживания и правильно с ним справиться?   -  person ConMan    schedule 02.07.2015
comment
@ConMan это исключение происходит, когда метод @ Transactional «завершен». Так что с текущим кодом я не смогу поймать его в классе обслуживания.   -  person Sam    schedule 03.07.2015


Ответы (3)


Создание/обновление не будет зафиксировано, пока вы не вернетесь из метода @Transactional. Если создание/обновление сбрасывается в базу данных до этого, вы можете получить исключение внутри метода, но в вашем случае оно не сбрасывается до фиксации.

Вы можете принудительно сбросить создание/обновление перед фиксацией. Вы не говорите, используете ли вы Hibernate или JPA, но session.flush() или entityManager.flush() должно помочь.

person MattR    schedule 06.07.2015
comment
В точку !!!. Я использую JPA, entityManager.flush() работает хорошо. Теперь я могу обработать исключение в классе обслуживания. Спасибо. - person Sam; 06.07.2015

  1. Используйте программное управление транзакциями и обрабатывайте исключения в блоке try catch.
  2. Введите класс делегата и выполните в нем команду manageUser в транзакции:

    @Transactional(REQUIRED)
    public void manageUser() {
        try{
           delegate.manageUser();
        } catch (Exception ex ) {
        //handle
        }
    }
    

    И в классе делегата

    @Transactional(REQUIRES_NEW)
    public void manageUser() {
    }
    
person 6ton    schedule 02.07.2015
comment
Спасибо вам за ваши предложения. К сожалению, эти варианты могут не подойти для моего случая. Программное управление транзакциями наименее предпочтительно. У меня есть несколько мест с таким сценарием, потому что добавление одного для уровня делегата не является хорошим предложением для меня. - person Sam; 03.07.2015

Вместо AOP на основе прокси-сервера Spring я перешел на подход AspectJ. Это дает мне возможность сделать вызов метода manageUser() из другого метода того же класса обслуживания, и я могу обработать исключение там.

person Sam    schedule 04.08.2015