在某一方法中对A对象进行修改操作,且在这个方法返回之前对A对象进行查询。发现得到的数据是A对象修改之前的数据,而并非A对象修改之后的数据。这是什么原因造成的呢?
答:缓存问题。
为什么说是缓存问题呢?

都知道JPA的底层实现是Hibernate,也可以说JPA是Hibernate的升级版本。

Hibernate的缓存机制分为:一级缓存和二级缓存。

一级缓存:hibernate默认开启一级缓存,仅当commit或者flush时会根据快照机制确定是否更新到数据库。(快照机制:数据操作时,不仅会把数据放入一级缓存区,还会把相同的数据放入快照区。在此期间,若数据变更,缓存区的数据也会发生变化。当commit或者flush时,会对比缓存区和快照区的数据是否一致,如果一致(数据无变化)不进行操作,若不一致(缓存区的数据发生了变化)则调用update方法,更新数据到数据库。)
二级缓存:二级缓存默认是关闭的,多用于存放频繁查询,且变更机率较小的数据。相同数据查询时,直接从缓存中取,而不再访问数据库。减轻了数据库的压力。
默认查询时会先去一级缓存中取数据,取不到数据会去二级缓存中取(配置了二级缓存),如果还是没有取到数据,则访问数据库。

Repository中方法都加了事务,默认@Transactional(readOnly = true)只读。
查询操作时无影响,进行修改和删除操作时需要添加@Modifying和@Transactional
@Modifying标识该操作是修改操作
@Transactional声明事务性操作,Transactional(readOnly = false)可进行修改操作
不加@Modifying会出现以下问题:

org.hibernate.hql.QueryExecutionRequestException: Not supported for DML operations

不加@Transactional会出现以下情况:

org.springframework.dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query

使用时你可能会发现,对某一数据进行update操作后查询该数据,得到的是update之前的数据,而不是最新的数据。这是缓存造成的。

解决办法:
1.设置@Modifying(clearAutomatically=true) 会刷新一级缓存,得到最新的数据。
注意:clear后会把最新的数据刷新到一级缓存中,但不会flush,如果此时调用save()方法(刷新缓存)但不会把最新数据更新到数据库,在此期间(未flush)调用@Modifying(clearAutomatically=true)方法操作的数据可能就不是正确的,因此必须使用saveAndFlush(),或者设置flushAutomatically = true
设置如下:会在执行修改操作之前flush数据,从而拿到最新的数据。

@Modifying(clearAutomatically = true, flushAutomatically = true) 

2.另外一种清缓存的办法:引入EntityManager,在获取最新数据之前进行clear

@Autowired
    private EntityManager entityManager;
    
entityManager.unwrap(Session.class).clear();
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐