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.