JPA / Hibernate: отношения @ManyToOne и @OneToOne, помеченные как FetchType.LAZY и необязательные = false, не загружаются лениво на em.find ()? - PullRequest
15 голосов
/ 12 октября 2011

У меня есть следующая сущность (показаны только соответствующие сопоставления):

@Entity
@Table(name = "PQs")
public class PQ implements Serializable
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column
    private Integer id;

    @Column
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)                 // lazy XToOne
    @JoinColumn(name = "user_id", referencedColumnName = "person_id")
    private User user;

    @OneToOne(mappedBy = "pq", fetch = FetchType.LAZY) // lazy XToOne
    private Group group;

    @OneToOne(mappedBy = "pq", fetch = FetchType.LAZY) // lazy XToOne
    private Tendering tendering;

    ...
}

Обратите внимание на комментарии выше: существует три @XToOne отношения с другими сущностями:

Пользователь (подкласс SecurityIdentity с простым идентификатором в виде PK, на который ссылается PQ, представляющий владельца):

@Entity
@Table(name = "Users")
@DiscriminatorValue(value = "user")
public class User extends SecurityIdentity
{
    @Column
    private String name;

    @OneToMany(mappedBy = "user")
    private Set<PQ> pqs = new HashSet<PQ>();

    ...
}

Группа (также подкласс SecurityIdentity с простым идентификатором в видеPK, ссылается на PQ для представления набора пользователей, которые могут взаимодействовать с этим PQ):

@Entity
@Table(name = "Groups")
@DiscriminatorValue(value = "group")
public class Group extends SecurityIdentity
{
    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "pq_id", referencedColumnName = "id")
    private PQ pq;

    ...
}

Тендер :

@Entity
@Table(name = "Tenderings")
public class Tendering implements Serializable
{
    @Id
    @Column(name = "pq_id", insertable = false, updatable = false)
    private Integer pqId;

    @Column(name = "external_code")
    private String externalCode;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "pq_id", referencedColumnName = "id")
    private PQ pq;

    ...
}

Не путайтео группах и пользователях, разделяющих идентификаторы, просто рассматривайте их как простые идентификаторы.Тендер - это просто отдельный объект документа (один-к-одному).

Как вы можете видеть, существует три отношения @XToOne для объекта PQ, которые, если тип выборки не был установлен, будут загруженыс нетерпением (JPA по умолчанию).Поэтому, чтобы предотвратить это, я пометил все @XToOne отношения как FetchType.LAZY.

Теперь при использовании

em.find(PQ.class, someExistingId);

я получаю вывод Hibernate:

23:53:55,815 INFO  [stdout] Hibernate: select pq0_.id as id291_0_, pq0_.description as descript2_291_0_, pq0_.name as name291_0_, pq0_.submission_date as submission4_291_0_, pq0_.user_id as user5_291_0_ from PQs pq0_ where pq0_.id=?
23:53:55,818 INFO  [stdout] Hibernate: select user0_.id as id280_0_, user0_1_.identity_type_id as identity2_280_0_, user0_.is_enabled as is1_297_0_, user0_.name as name297_0_, user0_.password as password297_0_, user0_.person_id as person5_297_0_ from Users user0_ inner join SecurityIdentities user0_1_ on user0_.id=user0_1_.id where user0_.person_id=?
23:53:55,821 INFO  [stdout] Hibernate: select group0_.id as id280_0_, group0_1_.identity_type_id as identity2_280_0_, group0_.pq_id as pq2_281_0_ from Groups group0_ inner join SecurityIdentities group0_1_ on group0_.id=group0_1_.id where group0_.pq_id=?
23:53:55,823 INFO  [stdout] Hibernate: select tendering0_.pq_id as pq1_296_0_, tendering0_.binary_file as binary2_296_0_, tendering0_.customer_id as customer6_296_0_, tendering0_.description as descript3_296_0_, tendering0_.external_code as external4_296_0_, tendering0_.title as title296_0_ from Tenderings tendering0_ where tendering0_.pq_id=?

Три дополнительных SELECT происходят из отношений @XToOne (как описано во многих местах в сети).Источник, на который я в основном смотрю, таков:

Создание ленивого отношения OneToOne

Как уже упоминалось, отношение @ManyToOne User user не должно бытьизвлечено:

@ManyToOne(fetch=FetchType.LAZY) должно работать очень хорошо.

... здесь отношения от PQ до Пользователь , но он извлекается, как вы можете видеть из оператора select user0_.id as id280_0_, ... ...

Для остальных двух Group group и Tendering tendering, оба @OneToOne реверс отображений, внешние ключи ссылаются на PK (ID) таблицы PQ, что приводит к одинаковому отображению в объекте PQ.

Обратите внимание, что все три отношения не являются обязательными: у PQ всегда есть владелец (пользователь), и PQ всегда ссылается на тендер и групповой объект.Я просто еще не смоделировал это в JPA выше ...

Итак, при добавлении optional = false к трем отношениям сущности PQ:

@Entity
@Table(name = "PQs")
public class PQ implements Serializable
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column
    private Integer id;

    @Column
    private String name;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "user_id", referencedColumnName = "person_id")
    private User user;

    @OneToOne(mappedBy = "pq", fetch = FetchType.LAZY, optional = false)
    private Group group;

    @OneToOne(mappedBy = "pq", fetch = FetchType.LAZY, optional = false)
    private Tendering tendering;

    ...
}

... я получаюследующий вывод Hibernate:

00:47:34,397 INFO  [stdout] Hibernate: select pq0_.id as id361_0_, pq0_.description as descript2_361_0_, pq0_.name as name361_0_, pq0_.submission_date as submission4_361_0_, pq0_.user_id as user5_361_0_ from PQs pq0_ where pq0_.id=?
00:47:34,410 INFO  [stdout] Hibernate: select user0_.id as id350_0_, user0_1_.identity_type_id as identity2_350_0_, user0_.is_enabled as is1_367_0_, user0_.name as name367_0_, user0_.password as password367_0_, user0_.person_id as person5_367_0_ from Users user0_ inner join SecurityIdentities user0_1_ on user0_.id=user0_1_.id where user0_.person_id=?
00:47:34,413 INFO  [stdout] Hibernate: select group0_.id as id350_0_, group0_1_.identity_type_id as identity2_350_0_, group0_.pq_id as pq2_351_0_ from Groups group0_ inner join SecurityIdentities group0_1_ on group0_.id=group0_1_.id where group0_.pq_id=?

Обратите внимание, что я играл только с optional = false на объекте PQ, так как именно этот я использую в em.find(...).(Если этого недостаточно, пожалуйста, просветите меня.)

Мой вопрос теперь имеет два аспекта:

  1. Почему с энтузиазмом извлекается сущность @ManyToOne для User (учитывая, что это, как говорят, работает лениво, см. Создание отношения OneToOne ленивым )?
  2. Почему только отношение OneToOne к сущности Tendering прекращается, извлекается?Это потому, что объект Tendering ссылается на столбец PK PQ как на сам PK (@Id in Tendering), а объект Group (обычное отношение к PK PQ) отсутствует?

Что не так?Как мне сделать эти необязательные отношения ленивыми?(без инструментария кода или других взломов, просто аннотации ...)

Я знаю, что LAZY - это всего лишь подсказка для JPA-провайдера, что-то делать с отложенной загрузкой или нет, но в этом случае кажется, чтокак будто что-то не так (как часть этого работает).

PS: я использую Hibernate 4.0 BETA, версию, которая поставляется с JBoss 7.0.0.Final вместе только с аннотациями JPA (вышесовместимы с JPA 1.0).

Ответы [ 4 ]

2 голосов
/ 06 декабря 2011

Есть различия между использованием аннотаций hibernate и аннотаций jpa, и, насколько я знаю, hibernate загружается по умолчанию лениво, за исключением некоторых случаев. вот краткое обсуждение:

http://community.jboss.org/wiki/AShortPrimerOnFetchingStrategies

2 голосов
/ 18 октября 2011

Привет, я не уверен насчет JPA, но для отображений many-to-one и one-to-one отложенные значения, которые поддерживаются в спящем режиме: proxy, no-proxy и false, из которых по умолчанию установлено значение false проверьте эту часть DTD

lazy="proxy|no-proxy|false"

и вы можете проверить это Ссылка . Я думаю, что это ответит на ваш первый вопрос.

0 голосов
/ 08 мая 2016

Привет, Каву, вы показываете дополнительные SELECT, потому что он выполнит SELECT для всех задействованных объектов, попробуйте использовать FetchType.LAZY для значения по умолчанию Один-ко-многим и в большинстве случаев, поэтому используйте FETCH JOIN для получения результатов.

Iнадеюсь помочь вам или кому-то, кому нужна эта информация

0 голосов
/ 21 октября 2011

* Отношение ToOne подразумевает, что после инициализации объекта должен быть (прокси) bean-компонент, который будет вызывать select (вы видите его), как только к нему обращаются каким-либо образом, вы уверены, что ничего не делаете с объектами, которые могут вызвать загрузку?

...