【问题标题】:When do we need to .save() an entity in Spring?我们什么时候需要 .save() Spring 中的实体?
【发布时间】:2019-04-19 21:58:29
【问题描述】:

在一个相对较大的 Spring Boot 项目中,我有一个方法具有以下(过于简化)事件序列:

Car car = carRepository.save(new Car());
Person person = personRepository.save(new Person(car)); // Car is a field of Person

Engine engine = engineRepository.save(new Engine());
person.getCar().setEngine(engine);

carRepository.save(person.getCar()); // without this the engine and car relation is not registered

CarPersonEngine 都是 @Entity 类(数据库对象)。对于此示例,它们的实现可能如下:

// Car.java
@Entity
@Table(name = "car_tbl")
public class Car {
    @Id
    @GeneratedValue
    @Column(name = "car_id")
    private Long id;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "engine_id", unique = true)
    private Engine engine;
}

// Person.java
@Entity
@Table(name = "person_tbl")
public class Person {
    @Id
    @GeneratedValue
    @Column(name = "person_id")
    private Long id;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "car_id", nullable = false, unique = true)
    private Car car;
}

// Engine.java
@Entity
@Table(name = "engine_tbl")
public class Engine {
    @Id
    @GeneratedValue
    @Column(name = "engine_id")
    private Long id;
}

上述方法仅在 REST API 方法内部使用。 并且在配置属性中启用了 OSIV(Open Session In View)。

问题是,为什么我需要保存person.getCar() 对象?它与我刚刚在同一请求处理期间创建的 Car 对象相同。什么时候它会变成 transient 而不是 managed 由持久性上下文?我认为所有更改都会自动刷新,因为启用了 OSIV。

如何检测此类情况并确切了解何时使用.save() 方法以及何时可以忽略它?

【问题讨论】:

  • 我猜最后一行是carRepository.save(,而不是engineRepository
  • 引擎是否有 Car 作为依赖项?以 OneToOne 为例
  • Engine 没有任何依赖关系。

标签: hibernate spring-boot spring-data-jpa open-session-in-view


【解决方案1】:

结论

取决于方法是否包含在事务中。

  • 在事务中:上述代码按预期隐式工作。
  • 不在事务中:必须显式调用.save() 才能更新数据库。

如何检测此类情况并准确知道何时使用.save() 方法以及何时可以忽略它?

  • 如果出现以下情况,则必须为修改的实体显式调用 .save() 方法:
    1. 更改代码不属于事务
    2. 新创建的实体:new Car()
    3. 实体对象是从外部获取的,而不是从同一事务中的数据库获取。 (例如,在交易开始之前已经保存在 List 中。)
  • .save() 方法确实必须为在同一事务中从 JPA 查询的实体调用。 (例如carRepository.findById(id) 和其他类似方法)。

交易

如果从@Transactional注解的方法调用上述事件序列,那么它将按预期工作。

然后创建的Car 对象由持久性上下文管理,并且在同一事务中对其所做的所有更改都会在事务完成后刷新。

没有交易

如果上述方法不属于任何事务,则对对象的所有更改都是本地的(对象属性更改,但不会刷新到数据库)。

这就是为什么必须显式调用 persist()merge()(在上述情况下,save() 在内部使用这两个)以刷新更改。

更好地使用事务

保存单个实体会执行单个 SQL 查询,该查询是原子的,因此本身就是一个事务。如果数据库事件序列不包含在事务中,则每个save() 调用都充当独立的“事务”。

通常,这是不好的做法,因为如果后面的“事务”之一失败(某种Exception 抛出),先前成功的事务已经被刷新,可能会使数据库进入业务无效状态逻辑视角。


在视图中打开会话

该行为与 OSIV 无关。

OSIV 在视图呈现期间保持数据库会话打开,以便在主请求处理完成后可以在视图层执行进一步的查询(事务)。 Using OSIV is widely considered an anti-pattern.

【讨论】:

    猜你喜欢
    • 2022-01-15
    • 1970-01-01
    • 2016-03-12
    • 2019-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-31
    • 1970-01-01
    相关资源
    最近更新 更多