14

考虑以下模型:

@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)。

但是,由于我想要完整的用户实体(“来自用户”),因此休眠会为第一个结果行中的每个结果行生成一个选择。

我不明白,因为默认的获取策略应该是 LAZY 而不是 EAGER。强迫它懒惰并没有解决问题。

4

3 回答 3

12

这里有两个防止延迟加载的问题:

  1. 的默认获取策略OneToOneEAGER(请记住,这LAZY只是对持久性提供者的提示)。
  2. LAZYOneToOne如果关联不可为空(至少不使用字节码检测),则只能对关联起作用。

9.1.23 OneToOne 注解

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只加载所有用户

休眠:
    选择
        user0_.USER_ID 为 USER1_0_,
        user0_.FIRST_NAME 为 FIRST2_0_,
        user0_.LAST_NAME 作为 LAST3_0_
    从
        用户用户0_

并且不执行 N 个额外的查询,它们UserExt是延迟加载的。

因此,如果您的关联是强制性的,请使用适当的映射:) 如果它是非强制性的,您将不得不:

  • 使用字节码检测和无代理获取(请参阅下面的相关问题)
  • 改用假的 ManyToOne(没有使用共享主键测试此映射)
  • 使用 a 急切加载 UserExtjoin fetch以避免 N 次后续选择(当然,这以某种方式破坏了单独表的要点)

请注意,当您使用 Query 接口时,Hibernate >= 3.x 会忽略 Fetch 注释。在这种情况下,您需要明确地编写它。这是一个例子:

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 注释”
于 2010-08-21T19:00:15.380 回答
3

使用 -ToOne 时的默认获取策略,例如 @ManyToOne 和 @OneToOne 是fetch=FetchType.EAGER NOT fetch=FetchType.LAZY

但是 Hibernate HQL 会覆盖默认的获取策略。如果您想通过仅使用一个查询来检索完全初始化的对象,您必须调用

from 
    User u
 left join fetch 
    u.userExt
于 2010-08-21T12:18:22.013 回答
0

使用 optional =true 和这样的一对一关系来避免您自己的 Fetch 策略出现 n+1 问题。

@OneToOne(fetch = FetchType.LAZY, optional=true)
@PrimaryKeyJoinColumn
于 2021-03-05T07:47:29.423 回答