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. 总结
在这篇文章中,我们讲解了 CrudRepository
的 save()
方法的两种主要用途:
- ✅ 插入新数据
- ✅ 更新已有数据
虽然用法简单粗暴,但在事务管理下的行为需要注意细节,避免踩坑。
一如既往,本文的完整代码可以在 GitHub 上找到:Spring Data JPA 示例代码。