1

在一个相对较大的 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

Car,Person并且Engine都是@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()方法以及何时可以忽略它?

4

1 回答 1

2

结论

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

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

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

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

交易

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

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

无交易

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

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

更好地使用事务

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

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


在视图中打开会话

该行为与 OSIV 无关。

OSIV 在视图呈现期间保持数据库会话打开,以便在主请求处理完成后可以在视图层执行进一步的查询(事务)。使用 OSIV 被广泛认为是一种反模式。

于 2019-04-19T13:40:28.107 回答