我无法确定如何为以下实体正确设置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);
那么应如何设置以便新公司自动保留,但现有公司只是添加到用户公司映射中?
答案 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,它应该解析你的对象的引用。请注意,合并不会使托管的分离对象受管,它会返回托管的分离对象的副本。