java - Spring 启动 hibernate : cannot delete child object separately

标签 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());
}

更新:如果我从 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;
}

最佳答案

相关摘录自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. 您删除了一个任务
  2. 在事务结束时,Hibernate 将持久化上下文与数据库同步。
  3. 它找到Event 实例。它没有变化,但 PERSIST 操作级联到 tasks
  4. PERSIST 应用于 tasks 集合中的所有 Task,包括已删除的,它再次被管理。

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

如果延迟加载任务,则不会将PERSIST应用于它们(当然,如果同时未初始化集合);这就是为什么在这种情况下您看不到这种行为的原因。

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

关于java - Spring 启动 hibernate : cannot delete child object separately,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33409972/

相关文章:

java - 解码加密消息

java - AJAX 与 Spring MVC

java - 带有 @Transactional 注释的 Spring OpenSessionInViewFilter

java - Android Studio 1.1.0 + Robolectric 2.4 导致 java.lang.annotation.AnnotationFormatError

java - 如何在生成之后但测试执行之前修改数据库模式?

spring - 为 spring boot 应用程序生成 war 包时,maven 构建失败?

java - Grails save() 方法中的 SQLException

java - JPA/Hibernate - 父类(super class)查询时如何确定子类类型?

java - 按值/引用传递,什么?

java - 如何配置spring mvc根据Jackson注释填充@RequestMapping的参数?