【问题标题】:Hibernate, many-to-one and delete休眠、多对一和删除
【发布时间】:2012-09-04 10:05:16
【问题描述】:

假设您有一个从 A 类到 B 类的单向一对多关联,如下所示:

public class A {
    @OneToMany(cascade = CascadeType.ALL)
    private List<B> myBs;
}

public class B {
    //I know nothing about A
}

在数据库中,它们通过第三个表连接,并保留它们的 ID。

现在,我想删除一个连接到 A 的 B 对象。A 有自己的存储库类,B 有自己的存储库类。

在我的项目中以类似设置完成此操作的方式是,首先要求有问题的 A 删除有问题的 B,然后告诉 EnitityManager 从数据库中删除 B。

这让我有点卡在两个选择之间,在我看来它们都不是最佳选择:

  1. BRepository 中的存储库方法处理从其连接的 A 中删除 B 以及通过 EntityManager 从数据库中删除。我不喜欢这样,因为那样 B 的存储库类将不得不操作 A 对象。

  2. BRepository 中的存储库方法仅处理通过 EntityManager 进行的删除,由调用者将其从 A 的集合中删除。我更不喜欢这样,因为如果有人在没有先从 A 的集合中删除 B 的情况下调用存储库,它将严重失败。

在这两个中,我发现第一个是迄今为止最好的。但是,我真的不认为它是干净的。

Hibernate 中是否有一些结构允许在从数据库中删除时从它所属的任何集合中删除要删除的项目? (试图只删除 B 失败,因为包含它的 A 也加载到同一个事务中,所以它在事务结束时失败,当它试图存储被删除的东西时。)

(在A中的@OneToMany-mapping上添加mappedBy会解决问题吗?)

【问题讨论】:

  • “保留他们的”是什么意思?

标签: java hibernate hibernate-onetomany


【解决方案1】:

我找到了解决此问题的方法,即使用孤儿删除。

通过将 A 中的映射更改为:

public class A {
   @OneToMany(cascade = CascadeType.ALL)
   @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
   private List<B> myBs;
}

我只能说

a.getMyBs().remove(b);

b 将在a 被持久化后被删除。

【讨论】:

    【解决方案2】:

    跳出 Hibernate 思维模式,问问自己:谁拥有 Bs?

    业主对其财产负最终责任。通常,当B 没有A 并且每个B 都由一个A 拥有时,这是正确的。

    所以如果As 拥有Bs,ARepository 负责清理Bs。我会在ARepository 中创建一个方法来进行清理并调用BRepository 中的删除方法。

    【讨论】:

    • 我喜欢这个想法,但为了实现它,我需要 BRepository 中的一个公共方法来删除给定的 B。然后,我将无法控制谁将调用此方法,并且不能保证要删除的 B 不是与 A 的关系的一部分。因此,通过这种方式实现它,B 的删除不一定是原子操作 - 它取决于调用者。
    • 正确的方法是:只有调用者才能知道这是更大操作的一部分(涉及A)还是BRepository的单元测试
    • 我不同意,删除 B 应始终要求将其从属于其的任何 A 中删除。这样做并不能保证它会这样做。由于每个 B 都与 A 相连(可能忘记在原始问题中这么说 ;) ),因此正确的方法是处理从它所属的任何集合中删除 B 以及从其中删除 B数据库作为原子操作。而你的建议并没有做到这一点。此外,仅仅为了使其符合单元测试而更改代码也不是很好,不是吗?
    • 数据库看起来如何? B中是否有包含A的PK的FK列?
    • 不,由于某种原因,它是一个连接表。我知道,对于多对一来说没有必要,所以我可能会将其更改为您所描述的内容。顺便说一句(自己添加了一个答案),但无论如何谢谢:)
    【解决方案3】:

    如果您确实希望将存储库分开,另一个选项是在顶部创建一个服务类,该服务类可以识别两者,并以与您的选项 1 方法类似的方式为您处理删除。

    (会在 A 中的 @OneToMany-mapping 上添加 mappedBy 解决 问题?)

    我认为这取决于他的存储库实现。对于 JPA EntityManager,如果会话关闭,A 将在您删除 B 时分离,即使它是双向映射。

    【讨论】:

    • 这类似于今天的情况(实际上是在控制器中完成的),但是使用此解决方案也不能保证没有人只是跳过服务并直接调用存储库方法,然后我们回到广场 1。
    • 如果您的 B 不知道 A,这是否意味着应用程序中有一个使用 B 的上下文,甚至不知道 A 的存在?在应用程序的另一部分的某个地方,有一些处理 A 的东西,希望它知道的 A 在 B 删除器正在流失时对其集合进行操作?
    • 不一定需要是双向映射,只需要改变底层数据库中关系的实现方式即可。所以通过添加mappedBy,当B被删除时(在数据库中......),A和B之间的关系就消失了。但是,当分离的 A 仍然包含对已删除 B 的引用时,它可能会导致问题在事务结束时保持不变。
    • mappedBy 是一种在 B 中引用 A 而不更改底层数据库的方法?
    • Glen:Bs 总是与 As 结合使用。所有 B 都连接到 A。不确定我是否理解第二个问题。 A 中的 mappedBy 需要 B 中的新列,不是吗?所以它会涉及到对数据库的更改..
    猜你喜欢
    • 2011-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-22
    • 2012-05-13
    相关资源
    最近更新 更多