我们何时需要在Spring中保存.save()实体?

时间:2019-04-18 17:45:00

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

在一个相对较大的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(在视图中打开会话)。

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

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

1 个答案:

答案 0 :(得分:0)

结论

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

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

  

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

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

交易

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

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

无交易

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

这就是为什么必须显式调用persist()merge()(在上面的情况下,内部使用这两个变量的save())来刷新更改的原因。

更好地使用交易

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

通常,这是一种不好的做法,因为如果以后的一个“事务”之一失败(引发了某种Exception),则先前成功的事务已被刷新,从而可能使数据库从以下状态变为无效状态:业务逻辑角度。


在视图中打开会话

此行为与OSIV无关。

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