我遇到了JPA和通过JSON反序列化从应用程序外部传递的分离实体对象的问题。
我从JSON反序列化对象图(通过HTTP POST请求传递),然后尝试从中更新现有实体。
据我了解JPA,在对象上使用EntityManager.merge()
应该自动递归(取决于cascade
)附加它并为数据库中的所有对象发出UPDATE。
然而,这显然只适用于1)没有子节点的单个实体或2)对象图中最顶层的实体。
对于子实体,JPA将始终发出INSERT INTO,无论该对象是否已存在,因此会遇到主键冲突。
我做错了什么?
实施的相关部分是:
在AdminResource.java
:
@Autowire
protected CustomerConfigDAO customerConfigDao; // wrapper for the JPA persistence
@POST
@Consumes(MediaType.APPLICATION_JSON)
public void storeConfigurationData(CustomerConfigEntity config) {
customerConfigDao.update(config); // calls EntityManager.merge(...)
}
在CustomerConfigEntity.java
:
@Entity
public class CustomerConfigEntity implements Serializable {
@Id
private String customerID;
@OneToOne(mappedBy = "customerConfigEntity", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private DefaultValues defaultValues;
/* ... snip ... */
@PrePersist
@PreUpdate
public void fixBackReference() {
if(this.defaultValues != null)
this.defaultValues.setCustomerConfigEntity(this);
}
}
在DefaultValues.java
:
@Entity
public class DefaultValues implements Serializable {
@Id
@OneToOne
@JoinColumn(name = "customerID", referencedColumnName = "cloudID")
@JsonIgnore
private CustomerConfigEntity customerConfigEntity;
/* ... snip ... */
}
有人建议放弃双向一对一关系,转而采用单向关系,但由于customerConfigEntity
是DefaultValues
中的外键和主键,因此删除它需要我介绍一个代理键。我通过引入一个自动递增的键来实现这一点,但结果是每当CustomerConfigEntity
被保存时,JPA会在DEFAULTVALUES中创建一个新记录。
答案 0 :(得分:0)
我通过消除所有双向关系并用单向关系替换它来解决问题。
在CustomerConfigEntity.java
:
public class CustomerConfigEntity implements Serializable {
// ...
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@PrimaryKeyJoinColumn(name = "customerId", referencedColumnName = "customerId")
private DefaultValues defaultValues;
// ...
}
请注意,此处不得使用mappedBy
。
在DefaultValues.java
:
@Id
private String customerId;
CustomerConfigEntity
有更多关系,这些关系都是@OneToMany
基数。
对于那些人,我做了同样的事情但必须使用@JoinColumn
而不是@PrimaryKeyJoinColumn
customerId
- 尽管customerId
是{ strong>子实体的主键的一部分。但是使用@PrimaryKeyJoinColumn
导致JPA创建连接表,这不是我想要的。
然而,如果我真的需要双向关系,我仍然不知道如何解决这个问题。