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

Jör*_*ätt 5 jpa spring-data spring-data-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);
  }
} 
Run Code Online (Sandbox Code Playgroud)

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

 public interface TaskRepository extends CrudRepository<Task,Long> {}
Run Code Online (Sandbox Code Playgroud)

而这段代码创建并启动Task:

Task t1 = new Task("Task 1");
Task persisted = taskRepository.save(t1);
persisted.start();
Run Code Online (Sandbox Code Playgroud)

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

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

Oli*_*ohm 13

TL;博士

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

细节

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

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

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

  1. 持久化上下文被关闭.在Spring环境中,持久化上下文的生命周期通常绑定到事务.在您的特定示例中,存储库具有默认事务(以及持久性上下文)边界.如果您需要实体保持对其进行管理,则需要延长事务生命周期(通常通过引入具有@Transactional注释的服务层).在Web应用程序中,我们经常看到Open Entity Manager In View Pattern,它基本上是一个受请求限制的生命周期.

  2. 刷新持久化上下文.这可以手动发生(通过调用EntityManager.flush()或透明地发生.例如,如果持久性提供程序需要发出查询,它通常会刷新持久性上下文以确保查询可以找到当前挂起的更改.想象一下,您加载了一个用户,更改了他的地址到一个新的地方,然后发出一个查询,通过他们的地址查找用户.提供商将足够智能,先刷新地址变更,然后再执行查询.