在Spring Data JPA中使用Update query更新实体
使用诸如Hibernate之类的对象关系映射(ORM)工具,通常通过首先从数据库中获取实体,修改某些字段,然后再次持久化实体来修改实体。在许多情况下,这是一种很好的方法,但在某些情况下,这可能并不理想。例如,如果要更新实体集合(即数据库中的行),或者需要大量数据来构造实体对象。在后一种情况下,例如,可能需要太多资源来组装实体对象以仅改变单个字段的值。
使用Spring Data JPA,可以轻松地使用JPQL创建更新查询,从而转换为SQL更新查询。这可以如下完成。
@Repository
public interface CompanyRepository extends JpaRepository<Company, Integer> {
@Modifying
@Query("UPDATE Company c SET c.address = :address WHERE c.id = :companyId")
int updateAddress(@Param("companyId") int companyId, @Param("address") String address);
}
如您所见,在这种特殊情况下,JPQL查询是一个有效的SQL查询。我们在存储库方法上方使用@Modifying注释,因为它修改了数据库的状态而不选择数据。方法的返回类型是Integer,它等于受影响的行数,但如果不需要或不需要,可以将其设置为void。
注意不同步的实体
在更新这样的实体时要注意的一件事是,EntityManager可能在查询执行后包含过时的实体。如果您的JPA提供程序使用缓存(例如Hibernate的二级缓存),或者存在待刷新的更改,则可能就是这种情况。结果,EntityManager 中的实体的状态 可能不准确。如果在EntityManager中引用了受更新查询影响的实体,则会出现这种情况。有人可能想知道为什么要使用EntityManager不仅在执行查询后清除以避免这种不一致,但原因是可能存在尚未刷新的更改,然后会丢失。但是,您可以通过将@Modifying批注的clearAutomatically属性设置为true来选择配置此选项。
@Repository
public interface CompanyRepository extends JpaRepository<Company, Integer> {
@Modifying(clearAutomatically = true)
@Query("UPDATE Company c SET c.address = :address WHERE c.id = :companyId")
int updateAddress(@Param("companyId") int companyId, @Param("address") String address);
}
这可确保在执行查询时自动清除EntityManager,从而确保没有实体未同步。在某些情况下这可能是合乎需要的,但它取决于使用存储库方法的上下文,因此您必须小心这一点; 在没有注意到或记住这种副作用的情况下很容易使用这种方法,这可能会导致应用程序出现问题。希望单元测试可以用来捕捉这样的错误,但可能并非如此。所以明智地使用这个标志。