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 仓库


原始标题:Difference Between save() and saveAndFlush() in Spring Data JPA