我有一个简单的设置,其中包含两个对象Event
和Task
,它们作为父子链接,并且是某些CRUD操作的默认JPA存储库。我做了一个测试,我将2 Task
附加到Event
,然后我尝试在其中一个delete
上调用Task
操作,但失败了(不错误,也没有删除)。
如果我在没有将Task
添加到Event
的情况下运行相同的测试,则操作成功。我下面的所有代码,关于我接下来需要做什么的任何想法?
@Entity
public class Event {
@Id
@GeneratedValue(generator = "hibernate-uuid")
@GenericGenerator(name = "hibernate-uuid", strategy = "uuid2")
@Column(unique = true)
String id;
String subject;
@OneToMany(mappedBy="event", cascade = { CascadeType.ALL }, fetch = FetchType.EAGER)
@JsonIgnoreProperties({"event"})
List<Task> tasks;
}
@Entity
public class Task {
@Id
@GeneratedValue(generator = "hibernate-uuid")
@GenericGenerator(name = "hibernate-uuid", strategy = "uuid2")
@Column(unique = true)
String id;
String subject;
@ManyToOne
@JsonIgnoreProperties({"tasks"})
Event event;
}
public interface TaskRepository extends JpaRepository<Task, String> {
public Task findById(String id);
}
测试:
@Test
public void testSequenceOfActionsOnEventWithSubtasks() {
// Save a new task
Task savedTask = given()
.body(
new TaskBuilder()
.subject("Previously saved task")
.build()
).contentType(ContentType.JSON)
.when().post(TASKS_RESOURCE).as(Task.class);
Task unsavedTask = new TaskBuilder()
.subject("Unsaved task")
.build();
// Save a new event with an unsaved and a saved task attached
Event savedEvent = given()
.body(
new EventBuilder()
.subject("Main event")
.addTask(savedTask)
.addTask(unsavedTask)
.build()
).contentType(ContentType.JSON)
.when().post(EVENTS_RESOURCE).as(Event.class);
// Make sure the event now contains two tasks with GUID's attached
List<Task> tasksForEvent = when().get(EVENT_RESOURCE, savedEvent.getId()).as(Event.class).getTasks();
assertEquals(2, tasksForEvent.size());
// Get all tasks, make sure firstTask is in there
when().get(TASKS_RESOURCE).then().body(SUBJECT_FIELD, hasItems(savedTask.getSubject()));
// Delete a task that was attached to the event
when().delete(TASK_RESOURCE, savedTask.getId()).then().statusCode(HttpStatus.SC_OK);
// Get all tasks, make sure firstTask is NOT in there
when().get(TASKS_RESOURCE).then().body(SUBJECT_FIELD, not(hasItems(savedTask.getSubject())));
// Check the event again
tasksForEvent = when().get(EVENT_RESOURCE, savedEvent.getId()).as(Event.class).getTasks();
assertEquals(1, tasksForEvent.size());
}
更新:如果我从fetch = FetchType.EAGER
课程中的@OneToMany
移除Event
,则可以。虽然知道原因仍然很好。以下是我的新Event
课程。
@Entity
public class Event {
@Id
@GeneratedValue(generator = "hibernate-uuid")
@GenericGenerator(name = "hibernate-uuid", strategy = "uuid2")
@Column(unique = true)
String id;
String subject;
@OneToMany(mappedBy="event", cascade = { CascadeType.ALL })
@JsonIgnoreProperties({"event"})
List<Task> tasks;
}
答案 0 :(得分:5)
JPA specification的相关摘录:
部分 3.2.4 :
应用于实体X的刷新操作的语义如下 如下:
- 如果X是托管实体,则会将其同步到数据库。
- 对于来自X的关系引用的所有实体Y,如果与Y的关系已使用级联元素值注释 cascade = PERSIST或cascade = ALL,持久化操作应用于Y
部分 3.2.2 :
应用于实体X的持久化操作的语义如下 如下:
- 如果X是已删除的实体,则会被管理。
那么,在你的案件中发生了什么:
Task
。Event
个实例。没有任何更改,但PERSIST
操作级联到tasks
。PERSIST
已应用于Task
集合中的所有tasks
,包括已删除的TRACE
,这将再次受到管理。要验证这一点,请为org.hibernate
包启用un-scheduling entity deletion
日志级别,然后搜索包含tasks
的邮件。
如果PERSIST
被懒惰加载,则Event
不会应用于它们(如果集合在此期间未初始化,当然);这就是为什么你不会在这种情况下看到这种行为。
解决方案是从tasks
的{{1}}集合中删除已删除的任务,以便PERSIST
不适用于此。
答案 1 :(得分:1)
添加orphanRemoval = true属性
@OneToMany(mappedBy="event", cascade = CascadeType.ALL, orphanRemoval=true)
@JsonIgnoreProperties({"event"})
List<Task> tasks;