9

i には B エンティティを持つエンティティ A があり、B には @OneToOne 双方向関連付けを持つ A があります。

ここで、すべての A レコードを見つけると、hibernate は B で左外部結合を使用して次のような 2 つのクエリを実行します。

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 フィールドをロードすれば問題ありませんが、2 番目のクエリを実行して A をリロードするのはなぜですか? このクエリは A のコンテンツを B にロードすると思いますが、この A は明らかに B を含む A です...最初のクエリで既にロードされているので、そうではありませんか?

- 編集 -

エンティティ 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;
}

これが状況であり、A の findAll には 2 つのクエリが必要です...なぜですか?

4

2 に答える 2

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

}

そして B @PrimaryKeyJoinColumn のため、 mappedBy属性は必要ないことに注意してください

@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());

Integer は方法として不変の Type であるため、Integer の代わりに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;
    }

}   

そして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 を指す外部キー列が含まれていないために発生する2 つの select ステートメントが得られます

「from A a left join fetch ab where a.id = :id」

A は B_ID 外部キー列を使用して結合された B を取得する方法を知っているため、select ステートメントは 1 つだけ取得されます。

于 2010-08-02T03:33:24.990 に答える
0

あなたのマッピングは正確にどのように見えますか?

Hibernate が指すインスタンスが最初のインスタンスと同じであることを認識できるように、クラスABクラスは正しく実装されていますか?hashCode()equals()ABA

双方向の 1 対 1 マッピングをモデル化しようとしているようです。これに関するマニュアルのセクションを見て、それを達成するための推奨される方法を確認してください。

于 2010-08-01T12:57:32.187 に答える