【问题标题】:Explicit delete on JPA relationships显式删除 JPA 关系
【发布时间】:2012-10-31 02:43:06
【问题描述】:

我对在 JPA 中管理关系有点困惑。 基本上我有两个具有一对多关系的实体

一个配置可以有一个或多个与之关联的电子邮件列表。

@Entity
public class Config {
    @OneToMany(mappedBy="owner",cascade=CascadeType.ALL, fetch=FetchType.EAGER)
    private List<Email> emailReceivers;
}
@Entity
public class Email {
    @ManyToOne
    private Config owner;
}

在 EJB 和更新/合并操作期间,我将编辑与配置关联的电子邮件列表, 我认为我不需要在我的电子邮件实体上显式调用删除操作,我只需通过删除配置电子邮件列表中的电子邮件来管理关系。

@Stateless
public class ConfigFacadeImpl implements ConfigFacade{
    @EJB
    private ConfigDao configDao;
    @EJB
    private EmailDao emailDao;

    @Override
    public void update(Config Config, List<Email> emailsForDelete) {
        if(emailsForDelete!=null && emailsForDelete.size() > 0){
            for(Email emailTemp: emailsForDelete){
                Email email = emailDao.find(emailTemp.getId());
                emailDao.delete(email);  // Do I need to explicitly call the remove??
                config.getEmailReceivers().remove(email);
            }
        }
        configDao.update(config);
    }
}

如果我不执行删除并且只从列表中删除它,它不会删除我的表格行。

UI 和数据库现在不同步,因为 UI 不会显示我已删除的电子邮件,但是当您检查数据库时,行仍然存在。

需要吗?我认为如果我在我的实体中删除它,JPA 会为我处理这个问题。

更新

在进行任何更改之前,我已经调整了我的代码以首先从数据库中获取实体,但它仍然没有删除我的子电子邮件实体。我想知道这是否是 apache derby 问题。 (这是正确的方法,因为我将我的实体从我的 JSF 托管 bean 传递到我的 EJB,所以我需要先从数据库中获取同步。)

@Override
public void update(Config config, List<Email> emailsForDelete) {
    Config configTemp = configDao.find(config.getId());
    if(emailsForDelete!=null && emailsForDelete.size() > 0){
        for(Email emailTemp: emailsForDelete){
            configTemp.getEmailReceivers().remove(emailTemp);
        }
    }
    configDao.update(config);
}

【问题讨论】:

    标签: java jpa eclipselink ejb-3.1


    【解决方案1】:

    由于您已经定义了级联类型 = CascadeType.ALL,因此 JPA 应该负责删除。 Explicit Delete 声明不需要

    这两个语句不是必需的:

         Email email = emailDao.find(emailTemp.getId());
         emailDao.delete(email);  // Do I need to explicitly call the remove??
    

    相反,您可能只想在config.getEmailReceivers() 中找到匹配的emailReceiver,然后删除匹配的EmailReceivers。无需从数据库中加载Email 实体。

    编辑:要删除孤立对象,您可能需要包含CascadeType.DELETE_ORPHAN 级联属性和CascadeType.ALL

    【讨论】:

    • 这是什么意思?我实际上并没有删除我所有的 emailReceivers。只是从我的 JSF UI 中删除了一些项目。如果我注释掉 emailDao.delete,它不会删除我的电子邮件表中的条目
    • @MarkEstrada NO. CascadeType.ALL 相当于 cascade={PERSIST, MERGE, REMOVE, REFRESH, DETACH}。保存时,它将仅删除您从集合中删除的那些记录,例如如果您的配置为 5 个电子邮件接收器,并且您删除了其中 2 个,然后保存配置;它只会删除 2 个而不是全部 5 个。
    • 这也是我的理解,因为我只是在阅读和关注在线教程......我只是不确定为什么我的代码仍然没有删除我的电子邮件列表,如果我删除了删除的代码行明确的。
    • @MarkEstrada 请参考我更新的答案。我只是怀疑,由于您删除它们的方式,它真的没有从列表中删除元素。您可以通过在删除调用之前和之后打印/记录 emailReceivers 大小来验证这一点。
    • 我也更新了我的更新方法。在更新期间,我首先从我的数据库中同步我的配置表,然后执行删除。在删除之前,我有五个……在 for 循环之后,现在有 3 个……但是在调用合并时,它并没有删除数据库中的两行..
    【解决方案2】:

    这与Why merging is not cascaded on a one to many relationship中的问题相同

    基本上,JPA 只能级联您集合中的实体。因此,从集合中删除的子对象的更改永远不会放入上下文中,因此不能推送到数据库。在这种情况下,oneToMany 由多音返回指针控制,因此除非子元素也被合并,否则即使集合更改也不会显示。一旦从树中修剪出一个子节点,就需要单独管理和合并它,以便获取对它的更改。

    【讨论】:

      【解决方案3】:

      使用 JPA 2.0,您可以在父实体中使用选项 orphanRemoval=true

      例子:

      @Entity
      public class Parent {
          ...
      
          @OneToMany(mappedBy="parentId",cascade=CascadeType.ALL, orphanRemoval=true)
          private List<Child> childList;
          ...
      
      }
      

      【讨论】:

        猜你喜欢
        • 2011-12-06
        • 2019-08-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-29
        • 2015-03-24
        相关资源
        最近更新 更多