2015-07-17 2 views
0

У меня есть веб-приложение REST.
При выполнении запроса PUT на ресурс под названием ThingGroup мой контроллер вызывает слой службы, как этотНевозможно уловить исключения в служебном слое

@RequestMapping(method = RequestMethod.PUT) 
@ResponseBody 
public void updateThingGroup(@Valid @RequestBody final ThingGroup thingGroup, final Principal principal) throws UnknownResourceException { 
    final User user = userService.findByUserName(principal.getName()); 
    thingGroupService.update(thingGroup, user); 
} 

И соответствующий thingGroupService, который @Transactional

@Override 
@Transactional 
public ThingGroup update(final ThingGroup thingGroup, User user){ 
    ThingGroup found = null; 
    try { 
     found = find(thingGroup.getId(),user); 

     found.setName(thingGroup.getName()); 

     found = getDao().save(found); 

    } catch (final DataIntegrityViolationException e) { 

     // I want to throw that custom exception here 

     MyBadRequestException ex = new MyBadRequestException("MyBadRequestException.update.violation.thingGroup", "violation_data_integrity"); 
     ex.setParameters(new Object[] { thingGroup.getName()}); 
     throw ex; 
    } 
    return found; 
} 

В основной базе данных MySQL У меня есть ограничение единства на имени thingGroup.

Так что если я попытаюсь обновить вещьGroup с существующим именем, она не сработает.

Когда он терпит неудачу, я хотел бы бросить специальное исключение, чтобы передать понятное сообщение на уровень контроллера.

Моя проблема заключается в том, что из-за аннотации @Transactional исключения Hibernate выдаются после совершения транзакции, что означает, что после возврата метода обслуживания (я думаю).

А это означает, что я не могу поймать исключение на сервисном уровне.

У меня есть ответы на подобные ответы, и, как я понял, мне нужно будет поймать исключение на уровне контроллера. Однако это не кажется хорошим решением, потому что уровень контроллера должен быть осведомлен о базовом поведении уровня сервиса, что не очень хорошо.

Есть ли общее решение этой проблемы?

ответ

1

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

+0

Будут ли автоматические откаты работать должным образом, если я подключу несколько методов @Transactionnal, используя saveAndFlush, и один из них не работает? – singe3

+0

Несомненно, флеш просто синхронизирует текущие изменения (выполняет DML) в базе данных, он не фиксирует транзакцию. –

+0

Хорошо, я попробую. – singe3

0

Самое простое решение - перенести фактическую транзакцию из этого метода обслуживания в отдельный метод. Таким образом транзакция завершается до того, как вы покинете свой метод обслуживания, и вы можете реагировать на исключения Hibernate.

@Override 
public ThingGroup update(final ThingGroup thingGroup, User user){ 
    ThingGroup found = null; 
    try { 
     return doUpdate(thingGroup, user) 
    } catch (final DataIntegrityViolationException e) { 
     // I want to throw that custom exception here 
     MyBadRequestException ex = new MyBadRequestException("MyBadRequestException.update.violation.thingGroup", "violation_data_integrity"); 
     ex.setParameters(new Object[] { thingGroup.getName()}); 
     throw ex; 
    } 
    return found; 
} 

@Transactional 
public ThingGroup doUpdate(final ThingGroup thingGroup, User user){ 
    ThingGroup found = find(thingGroup.getId(),user); 
    found.setName(thingGroup.getName()); 
    found = getDao().save(found); 
    return found; 
} 

Метод doUpdate здесь является публичной для

а. более легкие модульные испытания и

b. возможность использовать транзакции, если вы не используете аспекты.

+0

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

+0

Тогда ваши методы должны были бы знать, были ли они вызваны извне или через цепочку. Это было бы уродливо. Как насчет выполнения цепочки с помощью «внутренних» (doSomething) методов, и пусть методы обслуживания (интерфейса) обрабатывают внешние вызовы? –

+0

Благодарим за предложения, но это не удобное решение. Это загромождает все классы обслуживания с помощью дублированных методов (метод и метод doMethod). Не могу поверить, что другого пути нет. Уловки исключений в методах @Transactionnal кажутся мне очевидной проблемой. Должен быть чистый способ справиться с этим, но я недостаточно знаком с Hibernate. – singe3

Смежные вопросы