我们什么时候需要 .save() Spring 中的实体?
When do we need to .save() an entity in Spring?
在一个相对较大的 Spring 引导项目中,我有一个具有以下(过度简化)事件序列的方法:
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
对象相同。在什么时候它变成了 transient 而不是 managed 由持久性上下文?我以为所有更改都会自动刷新,因为启用了 OSIV。
如何检测此类情况并准确知道何时使用.save()
方法以及何时可以忽略它?
结论
这取决于该方法是否包含在事务中。
- 在事务中: 上面的代码隐含地按预期工作。
- 不在事务中:必须显式调用
.save()
来更新数据库。
How can I detect cases like these and know exactly when to use the .save()
method and when can I ignore it?
- 如果出现以下情况,则必须显式为修改后的实体调用
.save()
方法:
- 更改代码不属于事务。
- 实体是新创建的:
new Car()
。
- 实体对象是从外部获取的,不是在同一个事务中从数据库中获取的。 (例如,在交易开始之前已经保存在
List
中。)
.save()
方法 而不是 必须为同一事务中从 JPA 查询的实体调用。 (例如 carRepository.findById(id)
和其他类似方法)。
交易
如果上述事件序列是从 @Transactional
注释的方法中调用的,那么它将按预期工作。
然后,创建的 Car
对象由持久性上下文 管理,并且在同一事务中对其进行的所有更改都会在事务完成后刷新。
无交易
如果上述方法不属于任何事务,则对象的所有更改都是本地的(对象属性更改,但不会刷新到数据库)。
这就是为什么调用 persist()
或 merge()
(在上面的例子中,save()
内部使用了这两个)必须显式完成以刷新更改。
更好地使用交易
保存单个实体执行单个 SQL 查询,该查询是原子的,因此是事务本身。如果数据库事件序列不包含在事务中,则每个 save()
调用都作为一个独立的 "transaction".
通常,这是不好的做法,因为如果后面的 "transactions" 之一失败(某种 Exception
抛出),先前成功的事务已经被刷新,可能会使数据库从业务逻辑角度来看是无效状态。
在视图中打开会话
该行为与 OSIV 无关。
OSIV 在视图渲染期间保持数据库 session 打开,以便在主请求处理完成后,可以在视图层中执行进一步的查询(事务)。 Using OSIV is widely considered an anti-pattern.
- Why is Hibernate Open Session In View considered a bad practice?
- JPA @Entity not managed after creation?
在一个相对较大的 Spring 引导项目中,我有一个具有以下(过度简化)事件序列的方法:
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
对象相同。在什么时候它变成了 transient 而不是 managed 由持久性上下文?我以为所有更改都会自动刷新,因为启用了 OSIV。
如何检测此类情况并准确知道何时使用.save()
方法以及何时可以忽略它?
结论
这取决于该方法是否包含在事务中。
- 在事务中: 上面的代码隐含地按预期工作。
- 不在事务中:必须显式调用
.save()
来更新数据库。
How can I detect cases like these and know exactly when to use the
.save()
method and when can I ignore it?
- 如果出现以下情况,则必须显式为修改后的实体调用
.save()
方法:- 更改代码不属于事务。
- 实体是新创建的:
new Car()
。 - 实体对象是从外部获取的,不是在同一个事务中从数据库中获取的。 (例如,在交易开始之前已经保存在
List
中。)
.save()
方法 而不是 必须为同一事务中从 JPA 查询的实体调用。 (例如carRepository.findById(id)
和其他类似方法)。
交易
如果上述事件序列是从 @Transactional
注释的方法中调用的,那么它将按预期工作。
然后,创建的 Car
对象由持久性上下文 管理,并且在同一事务中对其进行的所有更改都会在事务完成后刷新。
无交易
如果上述方法不属于任何事务,则对象的所有更改都是本地的(对象属性更改,但不会刷新到数据库)。
这就是为什么调用 persist()
或 merge()
(在上面的例子中,save()
内部使用了这两个)必须显式完成以刷新更改。
更好地使用交易
保存单个实体执行单个 SQL 查询,该查询是原子的,因此是事务本身。如果数据库事件序列不包含在事务中,则每个 save()
调用都作为一个独立的 "transaction".
通常,这是不好的做法,因为如果后面的 "transactions" 之一失败(某种 Exception
抛出),先前成功的事务已经被刷新,可能会使数据库从业务逻辑角度来看是无效状态。
在视图中打开会话
该行为与 OSIV 无关。
OSIV 在视图渲染期间保持数据库 session 打开,以便在主请求处理完成后,可以在视图层中执行进一步的查询(事务)。 Using OSIV is widely considered an anti-pattern.
- Why is Hibernate Open Session In View considered a bad practice?
- JPA @Entity not managed after creation?