在实际开发中,遇到一个问题,就是使用update语句更新后,再次查询同一条数据,依然查询到的是更新之前的旧数据,从而引发bug。原因如下:

由于hibernate默认开启一级缓存,仅当commit或者flush时会根据快照机制确定是否更新到数据库。(快照机制:数据操作时,不仅会把数据放入一级缓存区,还会把相同的数据放入快照区。在此期间,若数据变更,缓存区的数据也会发生变化。当commit或者flush时,会对比缓存区和快照区的数据是否一致,如果一致(数据无变化)不进行操作,若不一致(缓存区的数据发生了变化)则调用update方法,更新数据到数据库。)

下面来实验几个场景:

场景1:先查询,再更新,再次查询时查到脏数据

User user = userRepository.findById(userDTO.getId()).orElseGet(()->null);

userRepository.updateUserNameById(userDTO.getName(),userDTO.getId());

User user1 = userRepository.findById(userDTO.getId()).orElseGet(()->null);

/**

则 user1查询到的结果=user,原因是第二次查询时hibernate发现缓存中

已经存在user这条数据了,就不会访问数据库,从而查询到脏数据

**/

场景2:为了解决场景1中读取到脏数据问题,再update之后,调用了entityManager.clear(),

再次查询时可以正常查到最新的数据了,问题貌似解决了,no,别急,请看场景3

User user = userRepository.findById(userDTO.getId()).orElseGet(()->null);

userRepository.updateUserNameById(userDTO.getName(),userDTO.getId());

//update执行之后,使用clear清除缓存

entityManager.clear();

User user1 = userRepository.findById(userDTO.getId()).orElseGet(()->null);

/**

则 user1查询到的结果是最新的,clear方法把缓存清空了,所以hibernate会去数据库再查询一次

**/

场景3:在场景2的entityManager.clear()之前执行save操作去尝试更新某个字段,但是你发现save操作并没有生效,数据丢失了,原因是clear方法会把缓存清空,包括还没有更新到数据库的数据,

【save方法并不会立即执行update操作,仅当commit或者flush时会根据快照机制确定是否更新到数据库】

User user = userRepository.findById(userDTO.getId()).orElseGet(()->null);

//userRepository.updateUserNameById(userDTO.getName(),userDTO.getId());

user.setName(userDTO.getName());

userRepository.save(user);

entityManager.clear();

User user1 = userRepository.findById(userDTO.getId()).orElseGet(()->null);

场景4:上面说到了只有commit或者flush的时候save方法才真正生效,对于场景3commit肯定是行不通的,所以使用flush方法强制让save方法生效,这下数据正常了

User user = userRepository.findById(userDTO.getId()).orElseGet(()->null);

//userRepository.updateUserNameById(userDTO.getName(),userDTO.getId());

user.setName(userDTO.getName());

userRepository.save(user);

entityManager.flush();

entityManager.clear();

User user1 = userRepository.findById(userDTO.getId()).orElseGet(()->null);

场景5:场景4确实了彻底解决了二次查询无法查询到最新的数据的问题了,不过还有一些地方也需要注意,比如,先update了某些字段,然后又在clear之后不小心调用了save方法,save方法会所有的字段全部更新一遍,把之前的update覆盖掉了

User user = userRepository.findById(userDTO.getId()).orElseGet(()->null);

userRepository.updateUserNameById(userDTO.getName(),userDTO.getId());

entityManager.flush();

entityManager.clear();

//User user1 = userRepository.findById(userDTO.getId()).orElseGet(()->null);

//不小心调用了save方法

userRepository.save(user);

总结:

1.先update,再查询的业务场景,需要注意hibernate缓存导致脏数据的问题,update之后先调用flush,再调用clear方法,顺序不能反了

2.复杂业务场景个人觉得还是少用save方法去执行update操作

相关阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: