Автор оригинала: Vlad Mihalcea.
Во время моего Высокопроизводительного обучения сохраняемости Java я понял , что не все разработчики знакомы с методом getReference
JPA EntityManager
и большинство из них используют find
почти исключительно.
В этой статье мы увидим разницу между методами find
и getReference
, чтобы было ясно, когда их применять в зависимости от базового варианта использования.
Смотрите разницу между методом поиска и getReference в JPA и #Hibernate – @vlad_mihalcea https://t.co/vfnBexGpOt pic.twitter.com/xMqeh8Lf2f
В следующих примерах рассмотрим, что у нас есть родительская Запись
сущность и дочерний Комментарий к сообщению
, который имеет @ManyToOne
связь со своим родителем:
Объект Post
отображается следующим образом:
@Entity(name = "Post") @Table(name = "post") public class Post { @Id private Long id; private String title; //Getters and setters omitted for brevity }
И Комментарий к сообщению
вот так:
@Entity(name = "PostComment") @Table(name = "post_comment") public class PostComment { @Id @GeneratedValue private Long id; private String review; @ManyToOne(fetch = FetchType.LAZY) private Post post; //Getters and setters omitted for brevity }
Причина, по которой ассоциация @ManyToOne
использует Тип выборки.Атрибут LAZY
связан с тем, что по умолчанию используется Тип выборки.НЕТЕРПЕЛИВЫЙ
это очень плохо сказывается на производительности .
Теперь давайте предположим, что у нас есть следующая Запись
сущность в нашей базе данных:
Post post = new Post(); post.setId(1L); post.setTitle("High-Performance Java Persistence"); entityManager.persist(post);
Если вы хотите получить объект, чтобы связанный SQL
запрос select был выполнен сразу, вам необходимо использовать метод find
текущего EntityManager
:
Post post = entityManager.find(Post.class, 1L); assertEquals("High-Performance Java Persistence", post.getTitle());
Если вы просмотрите журнал запросов, вы увидите, что SQL-запрос выполняется вызовом метода find
:
SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 1
Если вам нужно, чтобы объект Post
был извлечен, потому что вам нужно получить доступ к его свойствам, то метод find
является правильным для вызова.
Однако давайте предположим, что мы хотим создать Комментарий к сообщению
и нам нужна сущность Post
, чтобы установить базовый post_id
Внешний ключ.
Если мы используем метод find
, как в следующем примере:
PostComment comment = new PostComment(); comment.setReview("Just awesome!"); Post post = entityManager.find(Post.class, 1L); comment.setPost(post); entityManager.persist(comment);
Hibernate создаст следующие инструкции SQL:
SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 1 INSERT INTO post_comment (post_id, review, id) VALUES (1, 'Just awesome!', 1)
Запрос SELECT на этот раз бесполезен, потому что нам не нужно извлекать объект Post
. Мы хотим только установить базовый столбец post_id
Внешний ключ.
Чтобы исправить предыдущую проблему, мы можем использовать метод getReference
:
PostComment comment = new PostComment(); comment.setReview("Just awesome!"); Post post = entityManager.getReference(Post.class, 1L); comment.setPost(post); entityManager.persist(comment);
И на этот раз Hibernate выдаст только инструкцию INSERT:
INSERT INTO post_comment (post_id, review, id) VALUES (1, 'Just awesome!', 1)
В отличие от find
, getReference
возвращает только прокси-сервер сущности , у которого установлен только идентификатор. Если вы получите доступ к Прокси-серверу, соответствующая инструкция SQL будет активирована до тех пор, пока EntityManager
все еще открыт.
Однако в этом случае нам не нужно обращаться к прокси-серверу сущности. Мы хотим распространить внешний ключ только на базовую запись таблицы, поэтому для этого варианта использования достаточно загрузить прокси-сервер.
При загрузке прокси-сервера вам необходимо знать, что исключение LazyInitializationException
может возникнуть, если вы попытаетесь получить доступ к ссылке прокси-сервера после закрытия EntityManager. Для получения более подробной информации об обработке исключения LazyInitializationException
ознакомьтесь с этой статьей .
Понимание того, какие операции связаны с каждым переходом состояния сущности, имеет первостепенное значение при использовании JPA и Hibernate.
Поэтому, когда вам нужно установить @ManyToOne
или @OneToOne
отношения, метод getReference
намного эффективнее, если вам не нужно извлекать связанную родительскую запись.