Spring Boot中能否用CRUDRepository更新仅含单字段的Genre实体?
关于Spring Boot中用Name作为ID的Genre实体更新问题
嘿,这个场景我之前做项目时也遇到过,咱们一步步来理清楚:
1. Spring Data JPA的save()怎么处理你的实体?
首先你得知道,CrudRepository.save()的逻辑是根据实体的ID是否存在来判断是新增还是更新。因为你的Genre把name作为ID,所以当你调用save()时:
- 如果传入的Genre对象的
name在数据库里已经存在,JPA会找到对应的原实体,然后更新它的字段(当然,如果你没改其他字段,其实就是无意义的操作,但框架会执行更新); - 如果
name是新的,就会插入一条新记录。
但这里有个关键坑:如果你想修改name本身,比如把原name是"Rock"的Genre改成"Classic Rock",这时候传入的新实体ID是"Classic Rock",框架会认为这是个新实体,直接插入,而不会更新原来的"Rock"记录——因为ID是实体的唯一标识,JPA不允许修改已有实体的ID值。
2. 能不能自定义按名称更新的查询?
当然可以!有两种常用方式:
方式一:用@Query自定义更新语句
在你的GenreRepository里添加这样的方法:
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; import org.springframework.transaction.annotation.Transactional; public interface GenreRepository extends CrudRepository<Genre, String> { @Modifying @Transactional @Query("UPDATE Genre g SET g.name = :newName WHERE g.name = :oldName") void updateGenreName(@Param("oldName") String oldName, @Param("newName") String newName); }
@Modifying告诉Spring Data这是一个修改操作,不是查询;@Transactional是必须的,因为修改操作需要事务支持;- 这样你就能通过旧名称找到原实体,直接更新它的name字段。
方式二:用Spring Data的查询推导(更适合简单查询,更新还是推荐上面的方式)
如果是更新其他字段(不是ID字段),也可以用推导的方法,比如假设你后来给Genre加了description字段,想按name更新description:
@Modifying @Transactional void updateByDescription(String description, String name);
不过这种推导语法对更新的支持不如自定义@Query直观,所以还是推荐第一种方式。
3. 必须新增ID字段吗?
不是必须的,完全取决于你的业务需求:
- 如果你的业务中,Genre的名称永远不会修改,那用name作为ID完全没问题,既节省了一个字段,也符合业务逻辑;
- 如果业务上需要修改Genre的名称,那新增一个独立的ID字段(比如
Long id,加上@Id @GeneratedValue(strategy = GenerationType.IDENTITY))会更灵活。因为独立ID和业务字段解耦,你可以随意修改name,而save()方法能正常识别原实体进行更新,不用写自定义查询,更符合JPA的常规用法。
内容的提问来源于stack exchange,提问作者Valter Ekholm
相关产品推荐
相关产品推荐

