9

我有一个实体 A,它有一个 B 实体,而 B 有一个带有 @OneToOne 双向关联的 A。

现在,当我找到所有 A 记录时,hibernate 在 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 ...所以它已经加载了第一个查询,不是吗?

- 编辑 -

实体 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 需要两个查询……为什么?

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 注意你不需要 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 属性封装)而不是 Integer,因为 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,您将获得两个选择语句它发生是因为B表不包含任何指向表A的外键列但是通过使用

“从 A 左连接获取 a.id = :id 的 ab”

您将只得到一个 select 语句,因为 A 知道如何使用其 B_ID 外键列检索其连接的 B

于 2010-08-02T03:33:24.990 回答
0

你的映射到底是什么样的?

AB类是否正确实现hashCode()equals()以便 Hibernate 可以判断A指向B的实例与第一个实例相同A

听起来您正在尝试对双向一对一映射进行建模 - 请查看手册中关于此的部分以了解完成它的推荐方法。

于 2010-08-01T12:57:32.187 回答