1. 概述

CrudRepository 是 Spring Data 提供的一个通用接口,用于对特定类型的实体执行基本的增删改查(CRUD)操作。它内置了多个实用方法,可以让我们快速与数据库进行交互。

本文将重点讲解如何以及何时使用 CrudRepository 中的 save() 方法。

如果你对 Spring Data 的其他 Repository 接口感兴趣,可以参考我们之前的文章:Spring Data Repositories 比较

2. 依赖配置

我们需要在 pom.xml 文件中添加以下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

✅ 这里使用了 H2 内存数据库,方便测试和演示。

3. 示例应用

首先,我们创建一个实体类 MerchandiseEntity,用于表示商品信息:

@Entity
public class MerchandiseEntity {
 
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private double price;

    private String brand;

    public MerchandiseEntity() {
    }

    public MerchandiseEntity(String brand, double price) {
        this.brand = brand;
        this.price = price;
    }
}

接着,创建一个继承自 CrudRepository 的接口,用于操作这个实体:

@Repository
public interface InventoryRepository 
  extends CrudRepository<MerchandiseEntity, Long> {
}

⚠️ 注意:这里我们指定了实体类 MerchandiseEntity 和主键类型 Long。Spring Data 会自动生成实现类,使我们可以直接调用 save() 等方法。

4. 使用 save() 方法插入新记录

我们可以通过 save() 方法将一个新的实体保存到数据库中:

InventoryRepository repo = context
  .getBean(InventoryRepository.class);

MerchandiseEntity pants = new MerchandiseEntity(
  "Pair of Pants", 34.99);
pants = repo.save(pants);

✅ 执行后,会在数据库中新增一条记录。即使我们没有手动设置 id,Spring Data 也会自动为我们生成。

💡 save() 方法返回的是保存后的实体对象,其中包含了数据库生成的主键值。

5. 使用 save() 方法更新已有记录

同一个 save() 方法也可以用来更新已有记录。假设我们之前保存了一个商品:

MerchandiseEntity pants = new MerchandiseEntity(
  "Pair of Pants", 34.99);
pants = repo.save(pants);

之后如果需要修改价格,可以先从数据库中取出该对象,修改后再调用 save()

MerchandiseEntity pantsInDB = repo.findById(pantsId).get(); 
pantsInDB.setPrice(44.99); 
repo.save(pantsInDB);

✅ 这样就完成了更新操作。

⚠️ 但要注意:如果你在事务方法中使用了 findById() 获取实体,这个实体是被持久化上下文管理的(即“托管状态”),此时即使不调用 save(),修改也会自动同步到数据库。

来看一个测试用例验证这个行为:

@Test
@Transactional
public void shouldUpdateExistingEntryInDBWithoutSave() {
    MerchandiseEntity pants = new MerchandiseEntity(
      ORIGINAL_TITLE, BigDecimal.ONE);
    pants = repository.save(pants);

    Long originalId = pants.getId();

    // Update using setters
    pants.setTitle(UPDATED_TITLE);
    pants.setPrice(BigDecimal.TEN);
    pants.setBrand(UPDATED_BRAND);

    Optional<MerchandiseEntity> resultOp = repository.findById(originalId);

    assertTrue(resultOp.isPresent());
    MerchandiseEntity result = resultOp.get();

    assertEquals(originalId, result.getId());
    assertEquals(UPDATED_TITLE, result.getTitle());
    assertEquals(BigDecimal.TEN, result.getPrice());
    assertEquals(UPDATED_BRAND, result.getBrand());
}

📌 这个测试说明:在事务环境中,托管实体的变更会自动持久化,无需显式调用 save()

6. 总结

在这篇文章中,我们讲解了 CrudRepositorysave() 方法的两种主要用途:

  • ✅ 插入新数据
  • ✅ 更新已有数据

虽然用法简单粗暴,但在事务管理下的行为需要注意细节,避免踩坑。

一如既往,本文的完整代码可以在 GitHub 上找到:Spring Data JPA 示例代码


原始标题:Spring Data - CrudRepository save() Method