Cod*_*Med 5 java spring hibernate jpa hibernate-mapping
在通过 MySQL 数据库使用 hibernate 和 jpa 的 Spring MVC 应用程序中,每当我尝试保存包含子实体的父实体时,都会收到有关子实体的以下错误消息:
Duplicate entry 'string1-string2' for key 'PRIMARY'
Run Code Online (Sandbox Code Playgroud)
这里,string1和string2指的是子实体的复合主键的两部分。 我该如何解决这个错误?
以下是在父实体中定义实体之间关系的方式Address:
@ManyToOne(cascade = { CascadeType.ALL }, fetch=FetchType.EAGER)
@JoinColumns({ @JoinColumn(name = "usecode", referencedColumnName = "code", insertable = false, updatable = false),
@JoinColumn(name = "usecodesystem", referencedColumnName = "codesystem", insertable = false, updatable = false)
})
public HL7GeneralCode use;
Run Code Online (Sandbox Code Playgroud)
以下是在子实体中定义关系的方式GeneralCode:
@OneToMany(mappedBy = "use", cascade = {CascadeType.ALL})
private Set<HL7Address> addresses;
Run Code Online (Sandbox Code Playgroud)
单击此链接可以查看完整的堆栈跟踪。可以在此链接中找到
该实体的完整代码。 Address
可以在此链接中GeneralCode阅读该实体的完整代码。
复合主键类的代码可以在此链接中找到。可以在此链接中找到扩展的
类。BaseEntityAddress
我读过很多关于此错误消息的帖子。其他帖子的答案不能解决我的错误消息,并且它们通常不能解决我的实体使用复合主键的事实。
持久化地址的代码是:
@Override
public void savehl7Address(HL7Address addr) {
if ((Integer)addr.getId() == null) {
System.out.println("[[[[[[[[[[[[ about to persist address ]]]]]]]]]]]]]]]]]]]]");
this.em.persist(addr);}
else {
System.out.println("]]]]]]]]]]]]]]]]]] about to merge address [[[[[[[[[[[[[[[[[[[[[");
this.em.merge(addr);}
}
Run Code Online (Sandbox Code Playgroud)
我尝试遵循 @Ben75 的建议,但代码在该行崩溃了this.em.persist(addr.getUse());。请注意,他的 if 子句不适合我的实际对象模型,因此我将下面的 if 子句更改为if(addr.getUse() != null && addr.getId()==null). 这是我的代码。
@Override
public void savehl7Address(HL7Address addr) {
if(addr.getUse() != null && addr.getId()==null){
//this next line prints in the stack trace right before the app crashes
System.out.println("about to this.em.persist(addr.getUse());");
//HL7GeneralCode is not persistent yet
this.em.persist(addr.getUse());
//since there is a cascade ALL on the adresses relationship addr is now persistent
return;
}
System.out.println("=========================== inside jpahl7patientrespository.savehl7Address(addr)");
if ((Integer)addr.getId() == null) {
System.out.println("[[[[[[[[[[[[ about to persist address ]]]]]]]]]]]]]]]]]]]]");
this.em.persist(addr);}
else {
System.out.println("]]]]]]]]]]]]]]]]]] about to merge address [[[[[[[[[[[[[[[[[[[[[");
this.em.merge(addr);}
}
Run Code Online (Sandbox Code Playgroud)
HL7Address 的相关部分现在是:
@ManyToOne(fetch=FetchType.EAGER)
@JoinColumns({ @JoinColumn(name = "usecode", referencedColumnName = "code", insertable = false, updatable = false),
@JoinColumn(name = "usecodesystem", referencedColumnName = "codesystem", insertable = false, updatable = false)
})
public HL7GeneralCode use;
Run Code Online (Sandbox Code Playgroud)
HL7GeneralCode 的相关部分现在是:
@OneToMany(mappedBy = "use")
private Set<HL7Address> addresses;
Run Code Online (Sandbox Code Playgroud)
单击此链接可以读取新的堆栈跟踪。
我该如何解决这个错误?
我按照 ben75 的建议将以下代码添加到保存地址方法中:
if(addr.getUse() != null && !this.em.contains(addr.getUse())){
System.out.println("about to this.em.persist(addr.getUse());");
this.em.persist(addr.getUse());return;
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,尽管堆栈跟踪SYSO表明上述代码在应用程序崩溃之前正在运行,但我仍然遇到相同的错误。
您可以通过单击此链接来读取生成的堆栈跟踪。
首先有一些事情需要明确:
HL7GeneralCode(父级)和 HL7Address(子级)之间存在双向关联。如果 HL7GeneralCode.addresses 是“反向”端 (mappedBy) 那么为什么拥有端 HL7Address.use 的 insertable/updatable false ?拥有方应该控制此关联,因此您应该删除 insertable/updatable=false 标志。
从父级到子级的级联总是有意义的,而不是相反。但在您的用例中,您尝试保留子项并自动保留父项。这就是为什么多对一的 CASCADE.ALL 没有意义。
使用双向关联时,双方都必须设置:
HL7Address addr = new HL7Address();
HL7GeneralCode code = new HL7GeneralCode();
...
code.getAddresses().add(addr);
addr.setUse(code);
Run Code Online (Sandbox Code Playgroud)持久操作旨在插入临时实体,而不是合并它们或重新附加实体。这意味着当您调用服务方法时,HL7Address 和 HL7GeneralCode 都是新实体。如果您已经保存了具有相同 ID 的 HL7GeneralCode,您将收到主键约束冲突异常。
如果 HL7GeneralCode 可能存在,那么您应该从数据库中获取它。
HL7GeneralCode code = em.find(HL7GeneralCode, pk);
HL7Address addr = new HL7Address();
if(code != null) {
code = new HL7GeneralCode();
em.persist(code);
}
code.getAddresses().add(addr);
addr.setUse(code);
em.persist(addr);
Run Code Online (Sandbox Code Playgroud)更新
HL7Address 地址不会覆盖 equals/hashCode,因此应用默认对象相同引用检查规则。这将确保我们可以从 code.addresses 列表中添加/删除地址。如果您以后改变主意,请确保正确实现 equals 和 hashCode。
尽管与您的问题无关,但您可能希望使用 getter/setter 而不是公开您的字段。这提供了更好的封装,并且您将避免将设置器与公共字段访问混合。
savehl7地址方法:
@Override
public void savehl7Address(HL7Address addr) {
HL7GeneralCode code = addr.use();
if(code != null && code.getId()==null){
//HL7GeneralCode is not persistent. We don't support that
throw new IllegalStateException("Cannot persist an adress using a non persistent HL7GeneralCode");
//In case you'd want to support it
//code = em.find(HL7GeneralCode, code.getId());
}
//Merge the code without any address info
//This will ensure we only reattach the code without triggering the address
//transitive persistence by reachability
addr.setUse(null);
code.getAddresses().remove(addr);
code = em.merge(code);
//Now set the code to the address and vice-versa
addr.setUse(code);
code.getAddresses().add(addr);
if ((Integer)addr.getId() == null) {
System.out.println("[[[[[[[[[[[[ about to persist address ]]]]]]]]]]]]]]]]]]]]");
em.persist(addr);
}
else {
System.out.println("]]]]]]]]]]]]]]]]]] about to merge address [[[[[[[[[[[[[[[[[[[[[");
addr = em.merge(addr);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2963 次 |
| 最近记录: |