在使用SpringDataJpa进行更新数据时,出现了更新后再查询但是查询不到的问题,经查询原来是因为Hibernate的缓存机制导致的。
1.问题
repository代码如下:
public interface BoxRepository extends JpaRepository<Box, Integer> {
@Modifying
@Transactional
@Query(value = "update Box set name = ?2 where id = ?1")
void update(Integer id, String name);
Box findByRoomId(Integer roomId);
}
controller代码:
System.out.println("更新前使用ID查询:" + boxR.findById(2).get().getName());
LocalTime now = LocalTime.now();
String s = now.toString();
System.out.println("要更新的值:" + s);
boxR.update(2, s);
System.out.println("更新后使用ID查询:" + boxR.findById(2).get().getName());
System.out.println("更新后使用另一个字段查询:" + boxR.findByRoomId(1).getName());
执行结果如下:
更新前使用ID查询:17:44:45.685
要更新的值:17:44:49.579
更新后使用ID查询:17:44:45.685
更新后使用另一个字段查询:17:44:45.685
可以看到更新后无论是使用ID查询还是使用另一个字段查询都看不到更新的值。
打开JPA的show-sql再执行一遍,结果如下:
Hibernate: select box0_.id as id1_0_0_, box0_.image as image2_0_0_, box0_.name as name3_0_0_, box0_.parent_box_id as parent_b4_0_0_, box0_.room_id as room_id5_0_0_ from box box0_ where box0_.id=?
更新前使用ID查询:09:41:26.774
要更新的值:09:41:33.308
Hibernate: update box set name=? where id=?
更新后使用ID查询:09:41:26.774
Hibernate: select box0_.id as id1_0_, box0_.image as image2_0_, box0_.name as name3_0_, box0_.parent_box_id as parent_b4_0_, box0_.room_id as room_id5_0_ from box box0_ where box0_.room_id=?
更新后使用另一个字段查询:09:41:26.774
在更新后使用ID查询时,并没有真正执行SQL语句,直接返回了Hibernate缓存中的实体。
但是在使用另外字段查询的时候,执行了查询语句,但是还是返回了缓存中的实体,这里不太明白是为什么。
2.解决方案
- 执行更新语句后执行EntityManager的clear方法,这样再次查询时一定会返回更新之后的值
- 不再使用SQL语句进行更新,而是修改实体的值之后再使用save方法保存,这样会使Hibernate缓存中的实体也进行更新,再次查询时虽然还是返回的缓存中的实体但已经是最新的了