有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java Spring引导休眠:无法单独删除子对象

我有一个简单的设置,有两个对象,EventTask,它们作为父子对象链接,还有一个用于某些CRUD操作的默认JPA存储库。我做了一个测试,将2Task附加到一个Event上,然后尝试对其中一个Task调用一个delete操作,该操作失败(没有错误,也没有删除)

如果我运行相同的测试,而没有将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());
}

更新:如果我从Event类中的@OneToMany上删除fetch = FetchType.EAGER,它会工作。不过,我还是想知道原因。下面是我的新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) 个答案

  1. # 1 楼答案

    来自JPA specification的相关摘录:

    第3.2.4节:

    The semantics of the flush operation, applied to an entity X are as follows:

    • If X is a managed entity, it is synchronized to the database.
      • For all entities Y referenced by a relationship from X, if the relationship to Y has been annotated with the cascade element value cascade=PERSIST or cascade=ALL, the persist operation is applied to Y

    第3.2.2节:

    The semantics of the persist operation, applied to an entity X are as follows:

    • If X is a removed entity, it becomes managed.

    那么,你的情况如何:

    1. 你删除了一个Task
    2. 在事务结束时,Hibernate将持久性上下文与数据库同步
    3. 它找到Event实例。它没有变化,但是PERSIST操作被级联到tasks
    4. PERSIST应用于tasks集合中的所有Task,包括已删除的,该集合将再次被管理

    要验证这一点,请为org.hibernate包启用TRACE日志级别,并搜索包含un-scheduling entity deletion的消息

    如果tasks是延迟加载的,那么PERSIST不会应用于它们(当然,如果集合在此期间没有初始化);这就是为什么在这种情况下你看不到这种行为

    解决方案是从Eventtasks集合中删除已删除的任务,这样PERSIST就不会应用于它

  2. # 2 楼答案

    add=true属性

    @OneToMany(mappedBy="event", cascade = CascadeType.ALL, orphanRemoval=true)
    @JsonIgnoreProperties({"event"})
    List<Task> tasks;