Spring Framework поддерживает интеграцию с Java Persistence API (JPA) и поддерживает нативный Hibernate для управления ресурсами, реализации объектов доступа к данным (DAO) и стратегии транзакций. Например, для Hibernate имеются первоклассные средства поддержки с несколькими удобными IoC-функциями, которые решают многие типичные проблемы интеграции Hibernate. Можно сконфигурировать все поддерживаемые функции для инструментов OR (объектно-реляционного) отображения с помощью внедрения зависимостей. Они могут участвовать в управлении ресурсами и транзакциями Spring, а также они соответствуют типизированным иерархиям транзакций и исключений DAO в Spring. Рекомендуемый стиль интеграции – кодировать DAO вместо простых API-интерфейсов Hibernate или JPA.

Spring привносит значительные средства, расширяющие функционал, в выбранный вами уровень ORM при создании приложений доступа к данным. Можно использовать столько средств поддержки интеграции, сколько пожелаете, и затем сравните эти усилия по интеграции со стоимостью и риском создания аналогичной инфраструктуры собственными силами. Можно использовать большую часть средств поддержки ORM так же, как и библиотеки, независимо от технологии, потому что все разработано в виде набора многократно используемых JavaBeans. ORM в IoC-контейнере Spring облегчает конфигурирование и развертывание. Таким образом, в большинстве примеров в данном разделе показана конфигурация внутри контейнера Spring.

Преимуществами использования Spring Framework для создания ORM DAO-объектов являются:

  • Упрощенное тестирование. IoC-подход Spring позволяет с легкостью менять местами реализации и конфигурации экземпляров SessionFactory из Hibernate, экземпляров DataSource из JDBC, диспетчеров транзакций и реализаций отображенных объектов (при необходимости). Это, в свою очередь, значительно облегчает тестирование каждой части кода, связанного с постоянным хранением, в отдельности.

  • Общие исключения доступа к данным. Spring может обернуть исключения из вашего ORM-инструмента, преобразуя их из собственных (потенциально проверяемых) исключений в общую иерархию DataAccessException. Эта функция позволяет обрабатывать большинство исключений постоянного хранения, которые не подлежат восстановлению, только на соответствующих уровнях, без раздражающих стереотипных перехватов, генерации и объявления исключений. Но все еще можно отлавливать и обрабатывать исключения по мере необходимости. Помните, что исключения JDBC (включая диалекты, специфичные для БД) также преобразуются в ту же иерархию, что означает, что можно выполнять некоторые операции с JDBC в рамках модели согласованного программирования.

  • Общее управление ресурсами. Контексты приложений Spring могут обрабатывать местоположение и конфигурацию экземпляров SessionFactory из Hibernate, экземпляров EntityManagerFactory из JPA, экземпляров DataSource из JDBC и других связанных ресурсов. Это упрощает управление и изменение этих значений. Spring предлагает эффективную, простую и безопасную работу с ресурсами постоянного хранения. Например, в зависимом коде, использующем Hibernate, обычно нужно использовать одну и ту же Session из Hibernate для обеспечения эффективности и надлежащей обработки транзакций. Spring позволяет легко создать и привязать Session к текущему потоку прозрачным образом, открывая текущую Session через SessionFactory из Hibernate. Таким образом, Spring решает многие хронические проблемы типичного пользования Hibernate для любого локального или транзакционного JTA-окружения.

  • Интегрированное управление транзакциями. Можно обернуть ORM-код декларативным перехватчиком методов на основе аспектно-ориентированного программирования (АОП) либо с помощью аннотации @Transactional, либо явным образом сконфигурировав АОП-Advice для транзакций в XML-файле конфигурации. В обоих случаях семантика транзакций и обработка исключений (откат и т.д.) выполняются за вас. Как было описано в разделе " Управление ресурсами и транзакциями" , также можно менять различные диспетчеры транзакций, не затрагивая код, связанный с ORM. Например, можно переключаться между локальными транзакциями и JTA, при этом в обоих сценариях будут доступны одни и те же полноценные службы (например, декларативные транзакции). Кроме того, код, связанный с JDBC, может полностью транзакционно интегрироваться с кодом, который используется для ORM. Это полезно для такой организации доступа к данным, которая не подходит для ORM (например, пакетная обработка и потоковая передача BLOB), но при этом необходимо совместно использовать общие транзакции вместе с операциями ORM.

Для обеспечения более полной поддержки ORM, включая поддержку альтернативных технологий баз данных, таких как MongoDB, стоит обратить внимание на набор проектов Spring Data. Если вы являетесь пользователем JPA, руководство "Начало работы с доступом к данным с помощью JPA" с сайта https://spring.io будет отличным введением.

Общие подходы к интеграции ORM

В этом разделе рассматриваются рекомендации, применимые ко всем ORM-технологиям. Раздел, посвященный Hibernate, содержит более подробную информацию, а также демонстрирует эти возможности и конфигурации в конкретном контексте.

Основной целью интеграции ORM в Spring является четкая компоновка приложений (при любой технологии доступа к данным и транзакциях) и свободное связывание объектов приложения – больше никаких зависимостей бизнес-служб от стратегии доступа к данным или транзакций, никакого жестко закодированного поиска ресурсов, никаких труднозаменяемых объектов-одиночек, а также никаких кастомных реестров служб. Цель в том, чтобы организовать один простой и последовательный подход к обнаружению и связыванию объектов приложения, в максимально возможной мере обеспечивая возможность их повторного использования и свободу от зависимостей от контейнеров. Все отдельные функции доступа к данным можно использовать порознь, но также они прекрасно взаимодействуют с концепцией контекста приложения из Spring, предоставляя конфигурацию на основе XML и перекрестные ссылки на обычные экземпляры JavaBean, которые не обязательно должны быть совместимы со Spring. В типичном приложении Spring многие важные объекты являются JavaBeans: шаблоны доступа к данным, объекты доступа к данным, диспетчеры транзакций, бизнес-службы, использующие объекты доступа к данным и диспетчеры транзакций, распознаватели веб-представлений, веб-контроллеры, использующие бизнес-службы, и так далее.

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

В типичных бизнес-приложениях существует нагромождение повторяющегося кода для управления ресурсами. Многие проекты пытаются придумать собственные решения, иногда жертвуя надлежащим устранением сбоев в угоду удобства программирования. Spring выступает за простые решения для надлежащей обработки ресурсов, а именно IoC через шаблонизацию в случае JDBC и применение перехватчиков АОП для ORM-технологий.

Инфраструктура обеспечивает надлежащую обработку ресурсов и соответствующее преобразование специфических исключений API-интерфейсов в непроверяемую иерархию исключений инфраструктуры. Spring вводит иерархию исключений DAO, применимую к любой стратегии доступа к данным. В случае прямого JDBC класс JdbcTemplate, упомянутый в предыдущем разделе, обеспечивает обработку соединений и правильное преобразование SQLException в иерархию DataAccessException, включая преобразование специфических для базы данных кодов ошибок SQL в осмысленные классы исключений. В случае ORM-технологий см. следующий раздел о том, как воспользоваться теми же преимуществами преобразования исключений.

Если дело доходит до управления транзакциями, класс JdbcTemplate подключается к средствам поддержки транзакций Spring и поддерживает транзакции JTA и JDBC через соответствующие диспетчеры транзакций Spring. Что касается поддерживаемых ORM-технологий, Spring предлагает поддержку Hibernate и JPA через менеджеры транзакций Hibernate и JPA, а также поддержку JTA. Подробнее о поддержке транзакций см. в главе "Управление транзакциями".

Преобразование исключений

Если Hibernate или JPA используются в DAO, то нужно решить, каким образом обрабатывать нативные классы исключений технологии поддержки постоянного хранения. DAO генерирует подкласс HibernateException или PersistenceException в зависимости от технологии. Все эти исключения являются исключениями времени выполнения и не должны быть объявлены или перехвачены. Возможно, также придется столкнуться с IllegalArgumentException и IllegalStateException. Это означает, что вызывающие программы могут работать только с теми исключениями, которые считаются критическими, если нужно избежать зависимости от собственной структуры исключений технологии поддержки постоянного хранения. Выяснить конкретные причины (таких как сбой оптимистической блокировки) невозможно без привязки вызывающей программы к стратегии реализации. Такой компромисс может быть приемлем для приложений, которые в значительной степени основаны на ORM или не нуждаются в особом обращении с исключениями (или и то, и другое). Однако Spring позволяет прозрачно применять преобразование исключений через аннотацию @Repository. В следующих примерах (один для конфигурации на Java и один для конфигурации на XML) показано, как это сделать:

Java
@Repository
public class ProductDaoImpl implements ProductDao {
    // тело класса здесь...
}
Kotlin
@Repository
class ProductDaoImpl : ProductDao {
    // тело класса здесь...
}
<beans>
    <!-- Исключение постпроцессора преобразования бинов... -->
    <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
    <bean id="myProductDao" class="product.ProductDaoImpl"/>
</beans>

Постпроцессор автоматически ищет все преобразователи исключений (реализации интерфейса PersistenceExceptionTranslator) и снабажет Advice-ом все бины, помеченные аннотацией @Repository, чтобы обнаруженные преобразователи смогли перехватить и применить соответствующее преобразование к сгенерированным исключениям.

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