我想导入有一组专业的个人资料,以及一组专业小组。专业分布在ProfessionGroups。
以下实体已定义:
@Entity
public class Profile extends BaseEntity<Profile> { // B.E. defines id, creation_time,etc..
@OneToMany(cascade = CascadeType.ALL)
private Collection<Profession> professions;
@OneToMany(cascade = CascadeType.ALL)
private Collection<ProfessionGroup> professionGroups;
// .. getters and setters
}
@Entity
private class Profession extends BaseEntity<Profession> {
@Column(unique = true)
private String name;
// getters and setters
}
@Entity
public class ProfessionGroup extends BaseEntity<ProfessionGroup> {
@Column(unique = true)
private String name;
@ManyToOne(cascade = CascadeType.All)
private Collection<Profession> professions;
// getters and setters
}
以下代码读取了一些序列化为json的配置文件,并希望将其存储到数据库中:
// ...
Profile p = ...; // read from json using some deserializer
p.getProfessionGroups().forEach(pg -> pg.setProfessions(p.getProfessions());
// ..
ProfileService profileService = ...; //
profileService.save(profile);
ProfileService在内部调用entityManager.persist(...)。 这里的问题是,每当我想将所有职业分发到所有专业组时,我都会得到“重复密钥值违反唯一约束”。如何安全地存储配置文件,而不会获得唯一的键约束违规。 JPA显然希望为专业组中的每个条目创建一个新的职业。但是,专业的参考是相同的。调用merge(...)没有做到这一点。
答案 0 :(得分:2)
问题在于级联的定义,以及JPA,特别是hibernate如何处理新的实体实例。
当调用entitymanager.persist时,它会存储和管理实体的状态,但不会传递和传递给EntityManager.persist的实际对象。托管实例和传递的参数将不同。因此,如果您手动生成ID,则使用相同对象调用entitymanager.persist两次将导致数据库中的duplicatekeyexcpetion,而不是来自jpa。为了解决这个问题,你需要坚持并引用Profession实例的托管实体,你可以在Profile和ProfessionGroup中使用它们:
Profile profile = loadProfiles();
List<Profession> managedProfessions = profile
.getProfessions()
.stream()
.map((p) -> entityManager.merge(p)) //Note that we use the returned value, since the returned value is what is actually managed, the passed parameter is not, and will be discarded by the persistent-context
.Collect(Collectors.toList());
profile.setProfessions(managedProfessions);
profile.getProfessionGroups().forEach((gr)->gr.setProfessions(managedProfessions));
profileService.save(profile);
有了这个,您可能想要删除Cascade.ALL
并仅用```Cascade.MERGE````替换它。
答案 1 :(得分:1)
有时可以在其他地方找到解决方案:
实体与职业之间的关系&#39;和&#39; professiongroup&#39;是错的。应该是:
@Entity
public class ProfessionGroup extends BaseEntity<ProfessionGroup> {
@Column(unique = true)
private String name;
@ManyToMany(cascade = CascadeType.All)
private Collection<Profession> professions;
// getters and setters
// ..
}
专业可以在一个或多个专业组中,因为专业组可以有一个或多个职业。解决了这个问题后,解决方案现在看起来像这样:
Profile profile = mapper.readValue(json, Profile.class);
List<Profession> managedProfessions = profile
.getProfessions()
.stream()
.map(p -> {
return professionService.update(p);
})
.map(Optional::get)
.collect(Collectors.toList());
profile.setProfessions(managedProfessions);
profile.getProfessiongroups().forEach(professionGroup -> {
professionGroup.setProfessions(managedProfessions);
});
profileService.save(profile);
一块蛋糕。