Hibernate 循环外键

4

我需要测试一些代码,但这些代码不是我自己写的。这是一个集成测试:应用程序在服务器上持续运行,我的测试针对它运行。

这些测试是使用Selenium编写的,它们启动浏览器,在其中执行一些JavaScript以模拟用户操作,并检查数据库是否正确更新。我必须在每次测试后将数据库恢复到其初始状态。

为此,我使用Spring注释和Hibernate通过DAO进行操作,但这些DAO并不是我自己编写的。

问题在于存在循环外键。A类对象与B类型对象具有OneToMany关系,并且还有与同一类的ManyToOne关联。我尝试在同一事务中删除类型A的对象及其所有相关的B,但它不起作用,因为Hibernate在删除A类型对象之前尝试将"defaultB"设置为null。虽然在删除引用的B类型对象后将其置空是有意义的,但完全没有必要在删除A类型对象之前这样做。

我(天真地)认为,因为这2个操作在同一事务中执行,所以删除引用(并被引用)类B对象的A对象并同时删除b对象不会有问题。然而,我错了。如果没有更改DB模型(我没有编写),有没有办法做到这一点?

更新1:我不明白为什么当我执行mySession.delete(B)时,Hibernate尝试将一个已知为非空的键置为空值...对此有何想法吗?

更新2:从类C到类B存在一对多关系。当我删除具有此c_id的C对象时,Hibernate还尝试将相应于B的表中的“c_id”字段置为空。即使在其“父级”之前删除了B类对象。我知道Hibernate会重新排序查询并添加一些自己的内容,但我不明白为什么要重新排序已经按正确顺序排列的查询以使它们失败。

这里是(相关部分的)类:

@Entity
@Table(name = "A")
public class A {

    private Set<B> bs;
    private B defaultB;

    @OneToMany(mappedBy = "a", fetch = LAZY)
    public Set<B> getBs() {
        return bs;
    }

    public void setBs(Set<B> bs) {
        this.bs = bs;
    }

    @ManyToOne(fetch = LAZY, optional = false)
    @JoinColumn(name = "default_b_id", nullable = false)
    public B getDefaultB(){
        return defaultB;
    }

    public void setDefaultB(B defaultB) {
        this.defaultB = defaultB;
    }
}

@Entity
@Table(name = "B")
public class B {

    private a;

    @ManyToOne(fetch = LAZY, optional = false)
    @JoinColumn(name = "A_id", nullable = false)
    public A getA() {
        return a;
    }

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

}

2
我对JPA注解不是很熟悉,但你不想在@ManyToOne关系上设置某种级联选项吗? - matt b
Hibernate不会重新排列查询以使其失败。它基于关联和您对它们的操作来重新排序它们,以使查询成功。如果顺序错误,很可能是您做错了什么。无论如何,defaultB是否是Bs的成员?您能展示一下您的删除逻辑吗? - Pascal Thivent
2个回答

2
我尝试在同一个事务中删除类型为A的对象及其所有相关的B。如果您不想手动删除所有B,则应该对其进行级联REMOVE操作。我建议尝试以下方法(在两个关联上都使用cascade):
@Entity
@Table(name = "A")
public class A {

    private Set<B> bs;
    private B defaultB;

    @OneToMany(mappedBy = "a", fetch = LAZY, cascade=CascadeType.REMOVE)
    public Set<B> getBs() {
        return bs;
    }

    public void setBs(Set<B> bs) {
        this.bs = bs;
    }

    @ManyToOne(fetch = LAZY, optional = false, cascade=CascadeType.REMOVE)
    @JoinColumn(name = "default_b_id", nullable = false)
    public Strategy getDefaultB(){
        return defaultB;
    }

    public void setDefaultB(B defaultB) {
        this.defaultB = defaultB;
    }
}

我无法更改这些注释。顺便说一下,我手动删除所有相关的B,只是Hibernate发出的查询不做我想要的。

好的...但是我的猜测是,在删除实体之前,您没有正确地更新双向关联的两个方面。这通常在防御性编程方法中完成,就像这样(在A中):

public removeFromBs(B b) {
    b.setA(null);
    this.getBs().remove(b);
}

我无法更改这些注释。顺便说一下,我会手动删除所有相关的B,只是Hibernate发出的查询不符合我的要求。 - Pierre Gardin
当'a'是非空的时候,为什么要使用b.setA(null)? - Pierre Gardin

0

我猜你想删除a,但是Hibernate不允许,因为b仍然引用它?

由于你的元模型没有指定级联删除,所以在删除a之前,你需要“断开”b对a的链接。因此,在删除a之前执行b.setA(null)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接