Hibernate
Что это
Hibernate - библиотека предназначенная для решения задач объектно-реляционного отображения (ORM), самая
популярная реализация спецификации JPA. Объектно-реляционная модель описывает отношения
между программными объектами и записями в БД.
Слои взаимодействия
- Application
- Spring Data JPA(Repository) - это механизм организации репозиториев, а репозиторий - это абстракция,
лежащая на уровень выше ORM.
- Java Persistence API(JPA) — спецификация API Java EE, предоставляет возможность сохранять в удобном виде
Java-объекты в базе данных. Имеет только интерфейс. Как реализация чаще всего используется Hibernate.
- Hibernate
- JDBC
- Relational Database
Минусы
- Необходимо отслеживать проблему "N + 1"
Проблема "N + 1" запросов возникает, когда при работе с базой данных выполняется один основной запрос для
получения списка объектов (N), а затем для каждого из этих объектов выполняется еще один дополнительный
запрос (+1). Это приводит к множеству SQL-запросов, что сильно снижает производительность.
Решения:
Если вам нужна максимальная производительность, лучше использовать JOIN FETCH или @EntityGraph.
- Fetch Join (JOIN FETCH) - позволяет загружать связанные сущности. Минусы: Может приводить к дублирующим
данным, если есть несколько связанных коллекций.
List<Book> books = entityManager.createQuery(
"SELECT b FROM Book b JOIN FETCH b.author", Book.class)
.getResultList();
- @EntityGraph - позволяет указывать, какие связанные сущности загружать сразу. Минусы: Не работает с
nativeQuery.
@EntityGraph(attributePaths = {"author"})
@Query("SELECT b FROM Book b")
List<Book> findAllWithAuthors();
- @BatchSize - загружать связанные объекты партиями, уменьшая количество SQL-запросов. Минусы: Не даёт
полного контроля над SQL-запросами. Минусы: Не даёт полного контроля над SQL-запросами.
@Entity
public class Book {
@ManyToOne
@BatchSize(size = 10) // Загружает авторов по 10 за раз
private Author author;
}
- @Fetch(FetchMode.SUBSELECT) - позволяет загружать коллекции через один подзапрос вместо множества
маленьких SQL. Минусы: Может быть неэффективным на больших объемах данных.
@Entity
public class Author {
@OneToMany(mappedBy = "author")
@Fetch(FetchMode.SUBSELECT) // Загружает все книги автора одним подзапросом
private List<Book> books;
}
- @NamedEntityGraph - создать предопределённые графы загрузки. Минусы: Нужно явно указывать граф в запросе.
@NamedEntityGraph(name = "Book.withAuthor",
attributeNodes = @NamedAttributeNode("author"))
@Entity
public class Book {
@ManyToOne
private Author author;
}
EntityGraph<?> entityGraph = entityManager.getEntityGraph("Book.withAuthor");
List<Book> books = entityManager.createQuery("SELECT b FROM Book b", Book.class)
.setHint("javax.persistence.fetchgraph", entityGraph)
.getResultList();
Java Persistence Query Language (JPQL)
JPQL - платформенно-независимый объектно-ориентированный язык запросов, являющийся частью спецификации Java
Persistence API (JPA). JPQL используется для написания запросов к сущностям, хранящимся в реляционной базе
данных.
Spring конфигурация application.properties
- spring.jpa.hibernate.ddl-auto=none - чтобы Hibernate не выполнял никаких действий по модификации схемы.
К примеру для работы через Liquibase.
Аннотации для управления
- @Entity(name = "") - указывает что данный класс является сущностью(entity bean). Такой класс должен:
- Иметь конструктор по-умолчанию (пустой конструктор) public или protected.
- Не может быть final и не может содержать final-полей/свойств;
- Должен содержать хотя бы одно @Id-поле.
- Не может быть вложенным, интерфейсом или enum
- @Table(name = "") - указывает с какой именно таблицей необходимо связать (map) данный класс.
Имеет различные аттрибуты для указания имя таблицы, каталог, БД и уникальность столбцов в таблице БД.
- @Id - указывает первичный ключ (Primary Key) данного класса.
- @GeneratedValue - Эта аннотация используется вместе с аннотацией @Id и определяет такие параметры, как
strategy и generator.
- @Column - определяет к какому столбцу в таблице БД относится конкретное поле класса.
Аттрибуты: name, unique, nullable, length.
- @Access - определяет режим доступа к полям. Мы можем использовать @Access на уровне класса, поля
или метода. Мы можем даже смешивать два типа в одном классе сущностей. По умолчанию тип доступа
определяется местом, в котором находится ваш идентификатор(@ Id). Если он будет в поле -
это будет AccessType.FIELD, если он будет в геттере - это AccessType.PROPERTY. Иногда вам может
потребоваться аннотировать не поля, а свойства (например, потому что вы хотите иметь какую-то
произвольную логику в геттере или потому, что вы так предпочитаете). В такой ситуации вы должны
определить геттер и аннотировать его как AccessType.PROPERTY.
- AccessType.FIELD - обычный доступа к полю.
- AccessType.PROPERTY - для доступа к полю через get/set.
Отношения между таблицами
Дополняется CascadeType, FetchType и др.
- @OneToOne - рекомендуется EAGER.
- @OneToMany - рекомендуется LAZY.
- @ManyToOne - рекомендуется EAGER.
- @ManyToMany - рекомендуется LAZY.
CascadeType
- CascadeType.ALL - необходимо выполнять каскадно сразу все операции.
- CascadeType.PERSIST - когда выполняется метод save().
- CascadeType.MERGE
- CascadeType.REMOVE
- CascadeType.REFRESH
- CascadeType.DETACH
JPA имеет 2 типа загрузки(FetchType)
- LAZY(by default) - ORM загрузит сущность или коллекцию отложено, при первом обращении к ней из кода.
- EAGER - заставляет ORM загружать связанные сущности и коллекции сразу, вместе с корневой сущностью.
Состояние Entity:
- New
- Managed
- Removed
- Detached
Session methods
- Get() - получение данных. Если объекта нет вернет null. Вернет полностью инициализированный
объект(eager load) по этому медленнее чем Load(). Если вы не уверенны что объект есть - используйте
Get().
- Load() - получение данных. Если объекта нет вернет ObjectNotFoundException. Вернет proxy объект
(lazy load) по быстрее чем Get(). Если вы уверенны что объект есть - используйте Load().