Правильное использование Spring mvc 3 с Hibernate (Spring ORM) - PullRequest
8 голосов
/ 15 июля 2011

Я начинаю новый проект, пытаюсь сделать все правильно в этот раз (так что больше, чем один вопрос), мне может понадобиться помощь, я не уверен, что делаю неправильно:

  1. Весенний контекст
  2. Контроллер
  3. Сервисный интерфейс
  4. Реализация сервиса
  5. Интерфейс DAO
  6. Реализация DAO

Я хочу максимально использовать Spring MVC, как сделать так, чтобы @Transactional обрабатывал открытие / закрытие сессии?

Как мне перехватить исключения (т. Е. Несуществующая запись или сбой базы данных), если таковые имеются. моя база данных не принимает повторяющиеся записи, подобные этой:

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry

Как я могу поймать это?

И для каждого следующего запроса я получаю следующее исключение:

org.hibernate.AssertionFailure: null id in com.test.spring.ws.service.impl.TestObject entry (don't flush the Session after an exception occurs)

Что я делаю не так? Кто-нибудь может предложить некоторые улучшения в моем проекте?

Ответы [ 4 ]

12 голосов
/ 15 июля 2011

Сканирование компонентов

Перво-наперво: вы используете @Controller, @Service, @Repository и @Autowired, но ничего с ними не делаете.Я рекомендую использовать сканирование пути к классам .Удалите bean-компоненты «testServiceDAO» и «testService» из вашего весеннего контекстного файла и вместо этого используйте:

<context:component-scan base-package="com.test.spring.ws"/>

, который найдет и создаст эти bean-компоненты по их аннотациям вместо того, чтобы требовать их объявления в XML.Добавьте @Autowired в поле testServiceDAO вашего сервиса и в поле sessionFactory вашего DAO.Удалите сеттеры для этих полей.Они больше не нужны.Тэг компонента-сканирования также сделает автоматическую разводку для вас.Чтобы использовать пространство имен context, вам необходимо добавить его в корневой элемент.Например:

<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="
         http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd">

Управление транзакциями

Для используйте @ Transactional , как сказал Шон, вам нужно добавить элемент к вашей пружинефайл контекста:

<tx:annotation-driven/>

Поскольку ваш bean-компонент менеджера транзакций называется "actionManager ", он найдет его автоматически.Вам также нужно добавить пространство имен "tx" в ваш корневой элемент, чтобы оно выглядело примерно так:

<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:context="http://www.springframework.org/schema/context"
     xmlns:tx="http://www.springframework.org/schema/tx"
     xsi:schemaLocation="
         http://www.springframework.org/schema/beans 
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/tx 
         http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd">

Чтобы это работало, вам нужно удалить и session.beginTransaction(), и session.close() от вашего метода DAO.Открытие вашей собственной транзакции таким образом смешивает программную и декларативную разграничение транзакций, и декларативный способ обычно лучше.Кроме того, вы никогда не должны когда-либо закрывать сеанс в DAO в реальном проекте.Это доставит вас ко всем видам неприятностей.

Обработка исключений

Ваш MySQLIntegrityConstraintViolationException, будучи исключением для конкретной базы данных, будет пойман Hibernate и упакован в ConstraintViolationException , который будет исходить из вашего DAO;однако, поскольку ваш DAO теперь является @Repository, вы можете воспользоваться переводом исключений Spring .При этом исключение Hibernate будет перехвачено Spring и переведено в DataIntegrityViolationException .Обработка исключений в базе данных всегда забавна!

Управление сеансами

Используете ли вы OpenSessionInViewFilter или OpenSessionInViewInterceptor ?Если это так, сеанс Hibernate открывается при первом получении запроса и закрывается после записи ответа.Если нет, то сеанс не начинается до тех пор, пока не начинается транзакция (с помощью метода @Transactional), и закрывается, когда эта транзакция завершается.С фильтром / перехватчиком вы можете делать вещи на уровне «представления», которые требуют обратного вызова к базе данных - особенно когда у вас есть ленивые отношения или лениво загруженные объекты, которые вам нужны для визуализации представления.Если сеанс недоступен - как и если он существует только для длины метода транзакционного сервиса - вы не можете делать эти вещи в представлении и получите печально известную LazyInitializationException .

Что касается ошибки «не очищать сессию после возникновения исключения», которую я получаю, я не вижу сразу ничего, что заставило бы меня думать, что это должно произойти.Возможно, что-то в контексте пружины вашего веб-уровня неправильно настроено, или, может быть, есть какое-то странное взаимодействие в том, как вы обрабатываете транзакцию и сеанс непосредственно в DAO.

2 голосов
/ 15 июля 2011

как сделать, чтобы открытие / закрытие сеанса обрабатывалось @Transactional?

Вам необходимо <tx:annotation-driven /> (и пространство имен tx) в контексте Spring.

(см. Использование @Transactional)

1 голос
/ 15 июля 2011

Я бы предложил расширить HibernateDaoSupport и использовать HibernateTemplate вместо того, чтобы явно использовать SessionFactory (и создавать транзакции) в вашем коде DAO.

0 голосов
/ 15 июля 2011

Я бы настоятельно рекомендовал бы оставаться далеко, далеко от @Transactional весной. Для вашего использования используйте шаблон open-session-in-view . Когда приходит запрос, открывается сессия и начинается транзакция. Если возникла исключительная ситуация или другая ошибка, откат транзакции. В противном случае совершить. Таким образом, Hibernate позаботится обо всей тяжелой работе для вас.

Если вы пойдете по пути @Transactional, вы попадете в мрачное царство исключений ленивой загрузки и другого странного поведения, когда вы попытаетесь разобраться в том, что произошло откуда. В худшем случае вам нужно будет внимательно следить за порядком, в котором вызываются методы (т. Е. Уделять пристальное внимание вашему стеку), чтобы убедиться, что у вас есть необходимые разрешения.

Хуже всего, если вы поместите @Transactional на вещи, которые уже находятся в кэше Hibernate первого или второго уровня, вы получите множество и много транзакций BEGIN / END, идущих в вашу базу данных без выполнения реальных запросов ( потому что они в кеше). Это может убить вашу производительность и почти невозможно отменить.

После того, как транзакция в вашей сессии прервется, вам нужно откатиться. Возможно, вам придется повторить ваш сеанс, в зависимости от семантики вокруг этого. Исправление для первого из них простое - сначала проверьте, существует ли сущность, прежде чем выполнять сохранение. Это исправит второй.

Ознакомьтесь с этой статьей, в которой сравниваются Hibernate / JPA и myBatis , чтобы получить дополнительные комментарии.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...