如何级联只保留新实体

Rol*_*olf 4 java persistence many-to-many jpa 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
}
Run Code Online (Sandbox Code Playgroud)

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

User newUser = new User();

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

newUser.setCompanies(companies);

entityManager.persist(newUser);
Run Code Online (Sandbox Code Playgroud)

通过在@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);
Run Code Online (Sandbox Code Playgroud)

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

Pac*_*ace 9

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

这种情况不是理想情况下用级联处理的.级联持久性适用于所有子项都是使用父级创建的情况(例如,如果用户具有"技能"列表),则更多用于一对多.

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

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

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
Run Code Online (Sandbox Code Playgroud)


Jam*_*mes 5

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

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

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