(1 + N) выбирается с помощью ассоциаций OnetoOne

Учитывая следующую модель:

@Entity
public class User {

    @Id
    @Column(name = "USER_ID")
    private Long userId;

    @Column(name = "FIRST_NAME")
    private String firstName;

    @Column(name = "LAST_NAME")
    private String lastName;

    @OneToOne
    @PrimaryKeyJoinColumn
    private UserExt userExt;
...     //getters and setters

}

@Entity
public class UserExt {

    @Id
    @Column(name="USER_ID")
    private Long id;

    private String cdpId;

    private Date lastChanged;
...     //getters and setters
}

при выполнении:

Query query = session.createQuery("from User");
List<User> list = query.list();

Выполняется спящий режим

Hibernate: select user0_.USER_ID as USER1_0_, user0_.FIRST_NAME as FIRST2_0_, user0_.LAST_NAME as LAST3_0_, user0_.EXT_USERNAME as EXT4_0_ from USER user0_
Hibernate: select userext0_.USER_ID as USER1_1_0_, userext0_.cdpId as cdpId1_0_, userext0_.lastChanged as lastChan3_1_0_ from USER_EXT userext0_ where userext0_.USER_ID=?
Hibernate: select userext0_.USER_ID as USER1_1_0_, userext0_.cdpId as cdpId1_0_, userext0_.lastChanged as lastChan3_1_0_ from USER_EXT userext0_ where userext0_.USER_ID=?
...
...

Использование запроса с определенными свойствами работает (выберите u.firstName, u.userExt.cdpId).

Однако, поскольку я хочу, чтобы полное User Entity ( "от пользователя" ), hibernate генерирует один выбор для каждой строки результата в первом.

Я не понимаю, так как стратегия выбора по умолчанию должна быть LAZY, а не EAGER. Принуждение к LAZY не устранило проблему.

Ответ 1

Есть две проблемы, препятствующие ленивой загрузке здесь:

  • Стратегия выборки по умолчанию OneToOne - EAGER (и имейте в виду, что LAZY - это всего лишь подсказка поставщику персистентности).
  • LAZY может работать только с ассоциацией OneToOne, если ассоциация не является нулевой (по крайней мере, без использования инструментария байт-кода).

9.1.23 OneToOne Annotation

Аннотация OneToOne определяет однозначная связь с другой объект, который имеет один к одному множественность. Это не нормально необходимо указать связанные целевой объект явно, поскольку он может обычно выводятся из типа объект, на который делается ссылка.

В таблице 16 перечислены элементы аннотации который может быть указан для OneToOneаннотации и значения по умолчанию.

@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface OneToOne {
    Class targetEntity() default void.class;
    CascadeType[] cascade() default {};
    FetchType fetch() default EAGER;
    boolean optional() default true;
    String mappedBy() default "";
}

Я протестировал следующее:

@OneToOne(optional = false, fetch = FetchType.LAZY)
@PrimaryKeyJoinColumn
private UserExt userExt;

И подтвердите, что простой from User загружает только всех пользователей

Hibernate: 
    select 
        user0_.USER_ID as USER1_0_,
        user0_.FIRST_NAME as FIRST2_0_,
        user0_.LAST_NAME as LAST3_0_
    from 
        USER user0_

И не выполняет N дополнительных запросов, UserExt загружается лениво.

Итак, если ваша ассоциация является обязательной, используйте соответствующее сопоставление. И если это необязательно, вам нужно либо:

  • использовать инструментарий для байт-кода и выбор без прокси (см. соответствующий вопрос ниже)
  • использовать поддельный ManyToOne вместо этого (не тестировал это сопоставление с общим первичным ключом)
  • Загрузите UserExt с помощью join fetch, чтобы избежать N последующих выборок (конечно, это как-то поражает точку отдельной таблицы)

Обратите внимание, что Hibernate >= 3.x игнорирует аннотацию Fetch при использовании интерфейса Query. В этом случае вам нужно написать это явно. это пример:

EntityManager em = [...]
[...]
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<User> criteria = builder.createQuery(User.class);

Root<User> usersRoot = criteria.from(User.class);
usersRoot.fetch("Address", JoinType.LEFT);

List<User> users = em.createQuery(criteria).getResultList();

Связанные вопросы

Ссылка

  • Спецификация JPA 1.0
    • Раздел 9.1.23 "OneToOne Annotation"

Ответ 2

Стратегия выборки по умолчанию при использовании -ToOne, таких как @ManyToOne и @OneToOne, fetch = FetchType.EAGER NOT fetch = FetchType.LAZY

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

from 
    User u
 left join fetch 
    u.userExt