2010-08-01 34 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

相關問題