6

我无法弄清楚如何为以下实体正确设置 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);

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

4

2 回答 2

10

在休眠中考虑级联的最好方法是,如果您在父级上调用方法 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
于 2012-09-19T12:13:36.513 回答
5

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

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

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

于 2012-09-19T13:44:57.850 回答