Spring boot hibernate:不能单独删除子对象

时间:2015-10-29 09:10:41

标签: java spring hibernate jpa

我有一个简单的设置,其中包含两个对象EventTask,它们作为父子链接,并且是某些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;
}

2 个答案:

答案 0 :(得分:5)

JPA specification的相关摘录:

部分 3.2.4

  

应用于实体X的刷新操作的语义如下   如下:

     
      
  • 如果X是托管实体,则会将其同步到数据库。      
        
    • 对于来自X的关系引用的所有实体Y,如果与Y的关系已使用级联元素值注释   cascade = PERSIST或cascade = ALL,持久化操作应用于Y
    •   
  •   

部分 3.2.2

  

应用于实体X的持久化操作的语义如下   如下:

     
      
  • 如果X是已删除的实体,则会被管理。
  •   

那么,在你的案件中发生了什么:

  1. 您删除了Task
  2. 在事务结束时,Hibernate将持久化上下文与数据库同步。
  3. 找到Event个实例。没有任何更改,但PERSIST操作级联到tasks
  4. PERSIST已应用于Task集合中的所有tasks,包括已删除的TRACE,这将再次受到管理。
  5. 要验证这一点,请为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;