如何级联只保留新实体

时间:2012-09-19 11:59:51

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

我无法确定如何为以下实体正确设置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,因为它试图使用相同的主键持久化(INSERT)一个新的公司:

User newUser = new User();

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

newUser.setCompanies(companies);

entityManager.persist(newUser);

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

2 个答案:

答案 0 :(得分:9)

在hibernate中考虑级联的最佳方法是,如果在父级上调用方法X,则它将在每个子级上调用方法X.所以是的,如果你对用户进行持久调用,那么无论是否持久存在,它都会对每个孩子进行持久调用。

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

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

如果您认为必须使用级联,则可以使用级联合并。合并将在实体尚未持久化时保留。但是,合并有一些非常奇怪的副作用,这可能是你没注意到它起作用的原因。请考虑以下示例:

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

答案 1 :(得分:4)

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

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

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