为什么我的JPA实体的更改不会持久存储到数据库中?

Spring Boot Applicaion中,我有一个实体Task,其状态在执行期间发生变化:

@Entity
public class Task {

  public enum State {
    PENDING,
    RUNNING,
    DONE
  }

  @Id @GeneratedValue
  private long id;
  private String name;
  private State state = State.PENDING;

  // Setters omitted

  public void setState(State state) {
    this.state = state; // THIS SHOULD BE WRITTEN TO THE DATABASE
  }

  public void start() { 
    this.setState(State.RUNNING);

    // do useful stuff
    try { Thread.sleep(2000); } catch(InterruptedException e) {}
    this.setState(State.DONE);
  }
} 

如果状态发生更改,则应将对象保存在数据库中.我正在使用这个Spring Data接口作为存储库:

 public interface TaskRepository extends CrudRepository<Task,Long> {}

而这段代码创建并启动一个任务:

Task t1 = new Task("Task 1");
Task persisted = taskRepository.save(t1);
persisted.start();

根据我的理解,持久化现在附加到持久性会话,如果对象发生更改,则此更改应存储在数据库中.但这种情况并没有发生,重新加载时状态是PENDING.

我在这里做错了什么想法?

最佳答案
TL;博士

将实例附加到持久性上下文并不意味着对象状态的每个更改都会直接持久化.变更检测仅发生在持久性上下文生命周期中的某些事件上.

细节

你似乎误解了变化检测的工作方式. JPA的一个非常核心的概念是所谓的持久化上下文.它基本上是unit-of-work pattern的实现.您可以通过两种方式向其添加实体:从数据库加载实体(执行查询或发出EntityManager.find(…))或者将它们主动添加到持久性上下文中.这就是对save(…)方法的有效调用.

这里要实现的一个重点是“将实体添加到持久化上下文”不必等于“存储在数据库中”.只要认为合理,持久性提供者就可以自由推迟数据库交互.提供商通常这样做是为了能够批量修改数据操作.然而,在很多情况下,将直接执行初始保存(…)(转换为EntityManager.persist(…)),例如,如果您正在使用自动ID增量.

也就是说,现在该实体已成为一个管理实体.这意味着,持久化上下文可以识别它,并且如果发生需要发生的事件,则会将对实体所做的更改保持透明.最重要的两个是以下几个:

>持久性上下文被关闭.在Spring环境中,持久化上下文的生命周期通常绑定到事务.在您的特定示例中,存储库具有默认事务(以及持久性上下文)边界.如果您需要实体在其周围保持管理,则需要延长事务生命周期(通常通过引入具有@Transactional注释的服务层).在Web应用程序中,我们经常看到Open Entity Manager In View Pattern,它基本上是一个受请求限制的生命周期.
>刷新持久性上下文.这可以手动发生(通过调用EntityManager.flush()或透明地发生.例如,如果持久性提供程序需要发出查询,它通常会刷新持久性上下文以确保查询可以找到当前挂起的更改.想象一下加载用户,将他的地址更改为新地点,然后发出查询以按地址查找用户.提供商将足够智能地先刷新地址更改并在之后执行查询.

转载注明原文:为什么我的JPA实体的更改不会持久存储到数据库中? - 代码日志