问题描述
1、开启了数据库事务
2、通过EntityManager执行查询,获得返回对象
3、代码业务逻辑处理,其中有对象set属性值的操作
4、没有执行过JPA的save方法或者update语句
5、提交数据库事务,发现数据库中对应的数据更新成了新的属性值
问题复现
@Transactional @Override public void selectAndFlush(String id) { User user = crudDao.selectByPrimaryKey(id); user.setName("testFlushNotUpdateDB"); }
Hibernate: selectuser0_.AGE as AGE8_0_, user0_.CARD as CARD9_0_, user0_.NAME as NAME10_0_, user0_.PRICE as PRICE11_0_ from USER user0_ where user0_.ID=? Hibernate: update USER set AGE=?, CARD=?, NAME=?, PRICE=? where ID=?
可以看到在set方法之后JPA自动帮我们执行了update操作
问题原因
根本原因是因为我们在执行查询以后,查询结果对象在EntityManager上下文中进行了一级缓存,执行set方法以后缓存对象状态【持久态】,事务提交时会自动帮我们flush到数据库中,导致数据被更新,下图为一级缓存对象:
解决方法
这里提供4中解决方法:
1、提交事务之前执行EntityManager.clear()方法,清理缓存对象
2、业务代码在不需要更新数据时避免直接对查询对象进行set操作,可以使用BeanUtils.copyProperties()方法复制对象,再进行set操作
3、使用EntityManager.evict()方法清理指定对象
4、修改JPA的FlushMode,没有测试过,理论可行,代码如下:
((Session) getEntityManager().getDelegate()).setFlushMode(FlushMode.MANUAL);