(1 + N)选择OnetoOne关联

时间:2010-08-21 12:09:58

标签: java performance hibernate orm one-to-one

考虑以下模型:

@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执行

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

但是,由于我想要完整的用户实体(“来自用户”),所以hibernate会为第一个结果行生成一个选择。

我没有得到它,因为默认的提取策略应该是LAZY而不是EAGER。强迫它进入LAZY并没有解决问题。

3 个答案:

答案 0 :(得分:11)

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

  1. OneToOne的默认提取策略为EAGER(请注意,LAZY只是对持久性提供程序的提示)。
  2. 如果关联不可为空,则
  3. LAZY只能在OneToOne关联上工作(至少不使用字节码检测)。
  4.   

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

    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(不使用共享主键测试此映射)
    • 使用join fetch急切加载UserExt以避免N个后续选择(当然,这会以某种方式击败单独的表格)

    请注意,使用Query接口时,Hibernate&gt; = 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注释”

答案 1 :(得分:3)

使用-ToOne(如@ManyToOne和@OneToOne)时的默认提取策略为 fetch = FetchType.EAGER NOT fetch = FetchType.LAZY

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

from 
    User u
 left join fetch 
    u.userExt

答案 2 :(得分:0)

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

@OneToOne(fetch = FetchType.LAZY, optional=true)
@PrimaryKeyJoinColumn