2010-08-01 58 views
8

我有实体A有一个B实体,而B有一个A和@OneToOne双向关联。为什么hibernate为了加载@OneToOne双向关联而执行两个查询?

现在,当我的findAll A记录,休眠执行两个查询与左外连接上B,这样的事情:

select a.id, a.id_b, a.field1, b.id, b.field1 from A as a, B as b left outer join b ON b.id=a.id_b; 
select a.id, a.id_b, a.field1, b.id, b.field1 from A as a, B as b left outer join b ON b.id=a.id_b WHERE b.id=? 

首先查询负载A和B场,这是好的,但为什么执行第二个查询重新加载A? 我认为这个查询加载了B中的A内容,但是这个A明显是包含B的A ......所以它已经加载了第一个查询,是不是真的?

- 编辑 -

实体答:

@Entity 
public class A implements Serializable{ 
    // id and other ecc ecc 
    @OneToOne 
    @JoinColumn(name="id_b") 
    B b; 
} 

实体B:

@Entity 
public class B implements Serializable{ 
    // id and other ecc ecc 
    @OneToOne(mappedBy="b") 
    A a; 
} 

情况是这样的,并在需要两个查询一个的findAll ..为什么?

回答

6

打击,如果A和B份额其中两个实体通过使用加入了相同的主键列他们的主键,你应该使用@PrimaryKeyJoinColumn代替

@Entity 
public class A implements Serializable { 

    private MutableInt id = new MutableInt(); 

    private B b; 

    public void setIdAsMutableInt(MutableInt id) { 
     this.id = id; 
    } 

    @Id 
    @GeneratedValue 
    public Integer getId() { 
     return id.intValue(); 
    } 

    public void setId(Integer id) { 
     this.id.setValue(id); 
    } 

    /** 
     * Any ToOne annotation, such as @OneToOne and @ManyToOne, is EARGELY loaded, by default 
     */ 
    @OneToOne(fetch=FetchType.LAZY) 
    @PrimaryKeyJoinColumn 
    @Cascade(CascadeType.SAVE_UPDATE) 
    public B getB() { 
     return b; 
    } 

    public void setB(B b) { 
     b.setIdAsMutableInt(id); 

     this.b = b; 
    } 

} 

和早餐通知你做不需要的mappedBy属性,因为@PrimaryKeyJoinColumn

@Entity 
public class B implements Serializable { 

    private MutableInt id = new MutableInt(); 

    private A a; 

    public void setIdAsMutableInt(MutableInt id) { 
     this.id = id; 
    } 

    @Id 
    public Integer getId() { 
     return id.intValue(); 
    } 

    public void setId(Integer id) { 
     this.id.setValue(id); 
    } 

    @OneToOne(fetch=FetchType.LAZY) 
    @PrimaryKeyJoinColumn 
    public A getA() { 
     return a; 
    } 

    public void setA(A a) { 
     this.a = a; 
    } 

} 

让我们测试(可以测试,如果你想)

A a = new A(); 
B b = new B(); 

a.setB(b); 

/** 
    * b property will be saved because Cascade.SAVE_UPDATE 
    */ 
Serializable id = session.save(a); 

b = (B) session 
     .createQuery("from B b left join fetch b.a where b.id = :id") 
     .setParameter("id", id) 
     .list() 
     .get(0); 

Assert.assertEquals(b.getId(), b.getA().getId()); 

注意我用的是MutableInt场(由Integer属性封装),而不是整数,因为整数是一个不可改变的类型,以此A和B股分配的同一个ID

但是,如果A和B 通过使用其他比其主键加入,你应该使用@JoinColumn和的mappedBy(双向关系,右)如下

@Entity 
public class A implements Serializable { 

    private Integer id; 

    private B b; 

    @Id 
    @GeneratedValue 
    public Integer getId() { 
     return id; 
    } 

    public void setId(Integer id) { 
     this.id = id; 
    } 

    /** 
     * mappedBy="a" means: Look at "a" field/property at B Entity. If it has any assigned value, join us Through B_ID foreign key column 
     */ 
    @OneToOne(fetch=FetchType.LAZY, mappedBy="a") 
    /** 
     * Table A has a foreign key column called "B_ID" 
     */ 
    @JoinColumn(name="B_ID") 
    @Cascade(CascadeType.SAVE_UPDATE) 
    public B getB() { 
     return b; 
    } 

    public void setB(B b) { 
     this.b = b; 
    } 

} 

和早餐

@Entity 
public class B implements Serializable { 

    private Integer id; 

    private A a; 

    public void setIdAsMutableInt(MutableInt id) { 
     this.id = id; 
    } 

    @Id 
    @GeneratedValue 
    public Integer getId() { 
     return id; 
    } 

    public void setId(Integer id) { 
     this.id = id; 
    } 

    @OneToOne(fetch=FetchType.LAZY) 
    public A getA() { 
     return a; 
    } 

    public void setA(A a) { 
     this.a = a; 
    } 

} 

为了测试

A a = new A(); 
B b = new B(); 

/** 
    * Set up both sides 
    * Or use some kind of add convenience method 
    */ 
a.setB(b); 
b.setA(a); 

/** 
    * b property will be saved because Cascade.SAVE_UPDATE 
    */ 
Serializable id = session.save(a); 

b = (B) session 
     .createQuery("from B b left join fetch b.a where b.id = :id") 
     .setParameter("id", id) 
     .list() 
     .get(0); 

通过业主方B,你会得到两个SELECT语句它的发生原因是b表不含有任何外键栏指向表A但通过使用

“从左连接抓取AB其中a.id =:id为”

你会得到只是一个select语句因为A知道如何使用它B_ID外键列

检索其加入乙
+0

谢谢!非常详尽! – blow 2010-08-02 19:51:10

0

你的贴图看起来像什么?

做你AB类正确地贯彻执行hashCode()equals()使Hibernate可以告诉大家,A实例指向B是第一A的同一个实例?

听起来像您正在尝试建模双向一对一映射 - 请参阅the section in the manual on this以查看实现它的推荐方法。

+0

我编辑了我的帖子。 是的,我有一个简单的基于ID的简单的equals和hashCode实现。我的所有实体都使用此实现扩展了AbstractEntity。 – blow 2010-08-01 13:20:06