【问题标题】:How to cascade persist only new entities如何级联只保留新实体
【发布时间】:2012-09-11 17:41:17
【问题描述】:

我无法弄清楚如何为以下实体正确设置 JPA 持久性(使用 EclipseLink 和 transaction-type="RESOURCE_LOCAL"):

@Entity
public class User {
    // snip various members

    @ManyToMany
    private List<Company> companies;

    public void setCompanies(List<Company> companies) {
           this.companies = companies;
    }
}

@Entity
public class Company {
    // snip various members
}

我要做的是为公司列表设置级联,这样,如果以前没有持久化的新公司在列表中,它将自动与用户一起持久化:

User newUser = new User();

Company newCompany = new Company();
List<Company> companies = new ArrayList<Company>();
companies.add(newCompany);

newUser.setCompanies(companies);

entityManager.persist(newUser);

通过在@ManyToMany 上设置cascadeType.PERSIST,这可以正常工作。但是,如果公司列表中包含以前持久化的公司,我会收到 MySQLIntegrityConstraintViolationException,因为它正在尝试持久化(插入)具有相同主键的新公司:

User newUser = new User();

Company oldCompany = companyDAO.find(oldCompanyId);
List<Company> companies = new ArrayList<Company>();
companies.add(oldCompany);

newUser.setCompanies(companies);

entityManager.persist(newUser);

那么应该如何设置,以便自动保留新公司,而现有公司只是简单地添加到用户-公司映射中?

【问题讨论】:

  • 你试过CascadeType.MERGE
  • 已尝试 MERGE 是的,但新公司不会自动持久化。

标签: java jpa many-to-many persistence eclipselink


【解决方案1】:

您的问题是您正在破坏您的持久性上下文。托管对象应该只引用其他托管对象。所以让你的新对象引用一个现有的分离对象是错误的。

您需要做的是执行 find() 以获取现有分离对象的托管版本并让您的新对象引用它,然后对其调用 persist。

您也可以使用 merge() 而不是 persist,它应该会解析对象的引用。请注意,合并不会使分离的对象成为托管对象,它会返回托管的分离对象的副本。

【讨论】:

  • “做一个 find() 来获取现有分离对象的托管版本并让你的新对象引用它,然后在它上面调用持久化”——但这不是我正在做的然后? companyDAO.find(oldCompanyId) 调用 entityManager.find() 并返回结果添加到用户的公司列表中,然后将用户与 entityManager.persist(newUser);
  • @Rolf 在这种情况下的问题可能是您使用不同的实体管理器。 oldCompany 由 dao 的实体管理器获取,然后另一个用于持久化新实体。坚持不懈的 em 眼中的 oldCompany 基本是超然的。
【解决方案2】:

在休眠中考虑级联的最佳方式是,如果您在父级上调用方法 X,那么它将在每个子级上调用方法 X。所以是的,如果你对用户调用persist,那么它将对每个孩子调用persist,不管它们是否被持久化。

这种情况不能通过级联理想地处理。 Cascade persist 适用于所有子级都由父级创建的情况(例如,如果一个用户有一个“技能”列表),更适用于一对多。

在这种情况下,我个人不会使用级联。在不需要时公然使用级联会减慢应用程序的速度。

如果您觉得必须使用级联,可以使用级联合并。当实体尚未持久化时,Merge 将持久化它们。然而,合并有一些非常奇怪的副作用,这可能是你没有注意到它起作用的原因。考虑以下示例:

x = new Foo();
y = new Foo();

em.persist(x);
Foo z = em.merge(y);

//x is associated with the persistence context
//y is NOT associated with the persistence context
//z is associated with the persistence context

【讨论】:

  • 感谢您的见解。也许我会完全跳过级联。但实际上,我对 CascadeType.MERGE 的问题是,在为新公司保留新用户时它不起作用。这导致了 InvalidDataAccessApiUsageException “通过未标记为级联 PERSIST 的关系找到了一个新对象”。
  • 所以我最终放弃了级联,而是修改了负责保存用户实体的 DAO,以便在持久化或合并用户之前,它会遍历用户的公司,并首先持久化/合并这些公司。我最初没有意识到的是,更新用户和公司之间的关系不需要级联——这是作为更新用户的一部分自动处理的,因为关系只是用户的属性。
猜你喜欢
  • 2012-02-23
  • 2019-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-05
相关资源
最近更新 更多