問題描述
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);