1. 概述
在实际开发中,我们经常会用到 Spring Data JPA 提供的 save()
和 saveAndFlush()
方法来保存实体对象到数据库。虽然它们功能相似,但两者在行为上有一些关键区别,尤其是在与数据库同步的时机方面。
本文将通过一个简单示例,带你了解这两个方法的差异,并说明在什么场景下应该使用哪个方法。
2. 示例应用
我们先通过一个简单例子来看这两个方法的使用方式。
首先定义一个实体类 Employee
:
@Entity
public class Employee {
@Id
private Long id;
private String name;
public Employee() {}
public Employee(Long id, String name) {
this.id = id;
this.name = name;
}
// standard getters and setters
}
然后定义一个继承自 JpaRepository
的 Repository 接口:
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}
3. save() 方法
✅ 作用:save()
方法用于将实体对象保存到数据库中。它定义在 CrudRepository
接口中,是 Spring Data 提供的基础方法之一。
✅ 使用方式:
employeeRepository.save(new Employee(1L, "John"));
❌ 注意:
调用 save()
时,数据并不会立即写入数据库。Hibernate 会将更改保存在一级缓存中,只有在触发以下操作时才会真正刷新到数据库:
- 显式调用
flush()
- 当前事务提交(
commit()
) - 触发某些需要与数据库同步的查询操作(如 JPQL 查询)
⚠️ 事务可见性问题:
如果我们在事务中手动调用 flush()
但没有 commit
,此时其他事务是看不到这些更改的,除非外部事务的隔离级别是 READ_UNCOMMITTED
。
4. saveAndFlush() 方法
✅ 作用:saveAndFlush()
方法也用于保存实体对象,但它在保存后立即刷新缓存,将数据写入数据库。该方法定义在 JpaRepository
接口中。
✅ 使用方式:
employeeRepository.saveAndFlush(new Employee(2L, "Alice"));
✅ 适用场景:
当我们需要在同一个事务中立即读取刚刚保存的数据时,应该使用这个方法。例如:
- 执行一个依赖于刚刚保存实体 ID 的存储过程
- 在保存后紧接着进行查询,依赖数据库生成的字段值(如触发器或序列)
❌ 踩坑点:
如果只使用 save()
,后续查询可能无法获取到最新数据,因为 Hibernate 还没将更改同步到数据库。
5. 总结
方法 | 是否立即刷新 | 常见用途 |
---|---|---|
save() |
❌ 否 | 一般用于批量保存,性能更高 |
saveAndFlush() |
✅ 是 | 需要立即访问数据库中最新数据的场景 |
✅ 建议:
- 平时优先使用
save()
,因为它更高效; - 在需要立即访问数据库中保存状态的场景下使用
saveAndFlush()
; - 不要滥用
flush()
,避免不必要的性能损耗。
如需查看完整示例代码,欢迎访问 GitHub 仓库。