2010-08-01 2 views
8

У меня есть объект A, который имеет -B-объект, а B имеет A с @OneToOne двунаправленной ассоциацией.Почему спящий режим выполняет два запроса для активной загрузки @OneToOne двунаправленной ассоциации?

Теперь, когда я FindAll А записи, спящий режим выполнять два запроса с левым внешним соединением на 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? Я думаю, что этот запрос загружает контент A в B, но этот A является obviusly A, который содержит B ... поэтому его уже загруженный первым запросом не является истинным?

- EDIT -

Entity A:

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

Entity B:

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

Это ситуация, и FindAll на необходимость двух запросов .. . Зачем?

ответ

6

Blow, если А и В одни и те же первичный ключ столбца, где оба объекта соединены с помощью первичного ключа, вы должны использовать @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; 
    } 

} 

And 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; 
    } 

} 

Давайте Test (Вы можете проверить, если вы хотите)

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) вместо Integer, так как Integer является непреложным Тип как способ А и в одни и те же присвоен идентификатор

Но если а и в соединены с помощью другого, чем их первичного ключа, вы должны использовать @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, вы получите два операторов выбора Это происходит потому, что B Таблица не содержит какой-либо внешний ключа столбец, который указывает на таблицу A Но с использованием

«от А левый присоединиться выборки AB, где a.id =: идентификатор»

вы получите только один оператор выбора, поскольку А знает, как получить его присоединился к B, используя его B_ID столбец внешнего ключа

+0

Спасибо! Очень исчерпывающий! – blow

0

Как выглядит ваше отображение?

ли ваши A и B классы правильно реализовать hashCode() и equals() так, что Hibernate может сказать, что экземпляр A указывает B тот же экземпляр первой A?

Похоже, вы пытаетесь моделировать двунаправленное взаимно однозначное отображение - посмотрите на the section in the manual on this, чтобы увидеть рекомендуемые методы его выполнения.

+0

Я отредактировал мое сообщение. Да, у меня очень простая реализация equals и hashCode, основанная просто на ID. Все мои полномочия расширяют AbstractEntity с помощью этой реализации. – blow

 Смежные вопросы

  • Нет связанных вопросов^_^