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

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

@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 отношения к другим сущностям:

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

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

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

    ...
}

Группа (также подкласс SecurityIdentity с простым ID в качестве 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;

    ...
}

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

Как вы видите, на объекте PQ есть три отношения @XToOne, которые, если бы не был установлен тип выборки, загружались бы нетерпеливо (по умолчанию 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 (как описано во многих местах в сети). Источник, который я просматривал, в основном такой:

Making a OneToOne-relation lazy

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

@ManyToOne(fetch=FetchType.LAZY) должно работать просто отлично.

... здесь связь от PQ к User, но она есть, как видно из select user0_.id as id280_0_, ... оператора...

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

Обратите внимание, что все три отношения не являются необязательными: у PQ всегда есть владелец (пользователь), и на PQ всегда ссылаются сущности tendering и group. Я просто еще не смоделировал это в 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 извлекается с нетерпением (учитывая, что было сказано, что она работает лениво, см. Making a OneToOne-relation lazy)?
  2. Почему только отношение OneToOne к сущности Tendering оставлено без поиска? Это потому, что сущность Tendering ссылается на колонку PK PQ как на сам PK (@Id в Tendering), а сущность Group нет (обычная связь с PK PQ)?

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

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

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

16
задан Community 23 May 2017 в 10:29
поделиться