如何将一个已经存在的实体合并到持久化上下文中



我有以下实体:

一个任务和一个标签,在数据库端都有一个多对多的关系,但在我的应用程序中,我并不真正需要的信息,所以我没有使用@ manymany注释。

@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PUBLIC)
@AllArgsConstructor(access = AccessLevel.PUBLIC)
@Table(name = "TASK")
@Entity
@EntityListeners(AuditingEntityListener.class)
public class Task {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "text")
private String text;
@Column(name = "creation_time", nullable = false, updatable = false)
@CreatedDate
private Instant creationTime;
@Column(name = "update_time", nullable = false)
@LastModifiedDate // die By Variante ist dann interessant für Spring Security
private Instant updateTime;
@Column(name = "completion_time")
private Instant completionTime;
@OneToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST})
@JoinTable(
name = "join_table",
joinColumns = @JoinColumn(name = "task_id"),
inverseJoinColumns = @JoinColumn(name = "tag_id"))
private Set<Tag> tags;
@JsonInclude(JsonInclude.Include.NON_NULL)
@Transient
private Boolean checked;
@PostLoad
@PostPersist
@PostRemove
@PostUpdate
private void afterAnyUpdate() {
checked = completionTime != null;
}
}

标记:

@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PUBLIC)
@AllArgsConstructor(access = AccessLevel.PUBLIC)
@Table(name = "TAG")
@Entity
public class Tag {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String text;
}

然而,当我尝试使用已经存在的标签持久化任务时,我得到错误:传递给持久化的分离实体。(这是一个用例,可以发生在我的应用程序)

POST http://localhost:8080/tasks
Content-Type: application/json
{
"text": "neueTask",
"checked": false,
"tags": [
{
"id": 3,
"text": "ersterTag"
},
{
"text": "dritter"
}
]
}

So Far So Good,这是我已经预料到的行为,因为标签脱离了持久性上下文。

,

当我尝试将分离的标签合并到我的持久性上下文中时,没有任何变化。

我仍然得到相同的错误,这告诉我,我试图保存一个分离的实体。

(这是我的任务服务,其中处理HTTP请求)

@PersistenceContext 
private EntityManager manager;
public Task saveNewTask(Task taskToSave) {
taskToSave.getTags().forEach(tag -> {
if(this.manager.contains(tag)) {
manager.merge(tag);
}
});
return repository.save(taskToSave);
}

为什么?

(这是我关于stackoverflow的第一个问题,所以如果你需要额外的信息-让我知道!)

首先,您必须了解实体生命周期。Detached意味着实体有一个id,但当前持久性上下文不知道。有三种方法可以将对象从分离状态转换为托管状态。

  • EntityManager#find()从DB加载对象
  • EntityManager#getReference()获取对象的代理(当您假设它存在于DB中时)
  • EntityManager#merge()从DB加载对象并同时刷新更改

因为您似乎想要刷新更改,所以使用merge是可以的,但是您编写的代码实际上没有意义,因为使用contains,您实际上是在询问持久性上下文是否包含对象,这不是您需要的。

您应该检查对象是否分配了标识符:

@Transactional
public Task saveNewTask(Task taskToSave) {
taskToSave.getTags().forEach(tag -> {
if(this.manager.getEntityManagerFactory().getPersistenceUnitUtil().getIdentifier(tag) != null) {
manager.merge(tag);
}
});
return repository.save(taskToSave);
}

最新更新