Java Persistence API — це стандартна технологія, що дозволяє відображати об'єкти на реляційні бази даних. POM-модель spring-boot-starter-data-jpa дозволяє швидко розпочати роботу. Вона забезпечує такі ключові залежності:

  • Hibernate: одна з найпопулярніших реалізацій JPA.

  • Spring Data JPA: допомагає реалізувати репозиторії на основі JPA.

  • Spring ORM: основний засіб підтримки ORM із Spring Framework.

Традиційно класи "сутностей" JPA вказуються у файлі persistence.xml. У Spring Boot цей файл не потрібний, замість нього використовується сканування сутностей (Entity Scanning). За замовчуванням пошук виконується у всіх пакетах, розташованих нижче основного конфігураційного класу (той, що анотований @EnableAutoConfiguration або @SpringBootApplication).

Враховуються будь-які класи, анотовані @Entity, @Embeddable або @MappedSuperclass. Типовий клас сутностей подібний до наступного прикладу:

Java
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class City implements Serializable {
    @Id
    @GeneratedValue
    private Long id;
    @Column(nullable = false)
    private String name;
    @Column(nullable = false)
    private String state;
    // ... додаткові члени, які часто містять відображення анотації @OneToMany
    protected City() {
        // конструктор без аргументів, необхідний специфікацією JPA
        // protected, оскільки не призначений для використання безпосередньо
    }
    public City(String name, String state) {
        this.name = name;
        this.state = state;
    }
    public String getName() {
        return this.name;
    }
    public String getState() {
        return this.state;
    }
    // ... і т.д.
}
Kotlin
import java.io.Serializable
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.Id
@Entity
class City : Serializable {
    @Id
    @GeneratedValue
    private val id: Long? = null
    @Column(nullable = false)
    var name: String? = null
        private set
    // ... і т.д.
    @Column(nullable = false)
    var state: String? = null
        private set
    // ... додаткові члени, які часто містять відображення анотації @OneToMany
    protected constructor() {
        // конструктор без аргументів, необхідний специфікацією JPA
        // protected, оскільки не призначений для використання безпосередньо
    }
    constructor(name: String?, state: String?) {
        this.name = name
        this.state = state
    }
}
Можна налаштувати розташування, які потрібно просканувати на предмет наявності сутностей, за допомогою анотації @EntityScan.

Репозиторії Spring Data JPA

Репозиторії Spring Data JPA — це інтерфейси, які можна визначати для отримання доступу до даних. JPA-запити створюються автоматично з урахуванням імен методів. Наприклад, інтерфейс CityRepository може оголосити метод findAllByState(String state) для пошуку всіх міст у цьому штаті.

У разі більш складних запитів можна анотувати метод за допомогою анотації Query зі Spring Data.

Репозиторії Spring Data зазвичай розширюються за рахунок інтерфейсів Repository або CrudRepository. Якщо ти використовуєш автоконфігурацію, пошук в репозиторіях ведеться від пакета, що містить основний клас конфігурації (той, який анотований @EnableAutoConfiguration або @SpringBootApplication), і вниз за ієрархією.

У наступному прикладі показано типове визначення інтерфейсу взаємодії з репозиторієм Spring Data:

Java
import org.springframework.boot.docs.data.sql.jpaandspringdata.entityclasses.City;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.Repository;
public interface CityRepository extends Repository<City, Long> {
    Page<City> findAll(Pageable pageable);
    City findByNameAndStateAllIgnoringCase(String name, String state);
}
Kotlin
import org.springframework.boot.docs.data.sql.jpaandspringdata.entityclasses.City
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.repository.Repository
interface CityRepository : Repository<City?, Long?> {
    fun findAll(pageable: Pageable?): Page<City?>?
    fun findByNameAndStateAllIgnoringCase(name: String?, state: String?): City?
}

Репозиторії Spring Data JPA підтримують три різні режими початкового завантаження: за замовчуванням, із затримкою та відкладене. Щоб активувати завантаження із затримкою або відкладене завантаження, встанови властивість spring.data.jpa.repositories.bootstrap-mode у значення deferred або lazy відповідно. При відкладеному завантаженні або завантаженні із затримкою автоконфігурований EntityManagerFactoryBuilder буде використовувати AsyncTaskExecutor контексту, якщо такий є, як виконавця завантаження. Якщо існує більше одного виконавця, буде використовуватися той, що названий applicationTaskExecutor.

При завантаженні із затримкою або відкладеним завантаженням слід переконатися, що доступ до інфраструктури JPA надається із затримкою після етапу початкового завантаження контексту програми. Можна використовувати SmartInitializingSingleton для виклику будь-якої ініціалізації, яка потребує інфраструктури JPA. Для компонентів JPA (таких як конвертери), які створюються у вигляді бінів Spring, використовуй ObjectProvider, щоб відтермінувати дозвіл залежностей, якщо такі є.

Репозиторії Spring Data Envers

Якщо доступний Spring Data Envers, JPA-репозиторії будуть автоматично конфігуруватися для забезпечення підтримки типових запитів Envers.

Щоб використовувати Spring Data Envers, переконайся, що репозиторій розширюється з RevisionRepository, як показано в наступному прикладі:

Java
import org.springframework.boot.docs.data.sql.jpaandspringdata.entityclasses.Country;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.history.RevisionRepository;
public interface CountryRepository extends RevisionRepository<Country, Long, Integer>, Repository<Country, Long> {
    Page<Country> findAll(Pageable pageable);
}
Kotlin
import org.springframework.boot.docs.data.sql.jpaandspringdata.entityclasses.Country
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.repository.Repository
import org.springframework.data.repository.history.RevisionRepository
interface CountryRepository :
        RevisionRepository<Country?, Long?, Int>,
        Repository<Country?, Long?> {
    fun findAll(pageable: Pageable?): Page<Country?>?
}

Створення та видалення баз даних JPA

За замовчуванням бази даних JPA створюються автоматично, якщо т використовуєш вбудовану базу даних (H2, HSQL або Derby), лише. Можна явно налаштувати параметри JPA за допомогою властивостей spring.jpa.*. Наприклад, для створення та видалення таблиць можна додати наступний рядок до application.properties:

Properties
spring.jpa.hibernate.ddl-auto=create-drop
Yaml
spring:
  jpa:
    hibernate.ddl-auto: "create-drop"
Власне внутрішнє ім'я властивості Hibernate для цієї процедури (якщо ти, звісно, добре запам'ятаєш його) — hibernate.hbm2ddl.auto. Можна встановити його поряд з іншими нативними властивостями Hibernate, використовуючи spring.jpa.properties.* (префікс видаляється перед додаванням до диспетчера сутностей). У наступному рядку показано приклад встановлення властивостей JPA для Hibernate:
Properties
spring.jpa.properties.hibernate[globally_quoted_identifiers]=true
Yaml
spring:
  jpa:
    properties:
      hibernate:
        "globally_quoted_identifiers": "true"

Рядок у попередньому прикладі передає значення true для властивості hibernate.globally_quoted_identifiers диспетчеру сутностей Hibernate.

За замовчуванням DDL-виконання (або валідація) відкладається до запуску ApplicationContext. Існує також прапор spring.jpa.generate-ddl, але він не використовується, якщо активна автоконфігурація Hibernate, оскільки налаштування ddl-auto більш точні.

Відкриття EntityManager у поданні

Якщо ти запускаєш вебдодаток, Spring Boot за замовчуванням реєструє OpenEntityManagerInViewInterceptor для застосування шаблону відкриття EntityManager в поданні (Open EntityManager in View), щоб забезпечити можливість відкладеного завантаження у вебподаннях. Якщо тобіне потрібна така логіка роботи, слід встановити spring.jpa.open-in-view у false у application.properties.