1、问题出现(这个问题出现在JPA整合Hibernate框架中,它查询的数据会自动持久化)
有一天,我们XX平台的XX系统需要查询某张数据库表的数据。然后同学A实现方式如下。
@Resource
private OdbDbTableDao odbDbTableDao;
public OdbDbTable dataAssetsDetail(String id) {
OdbDbTable tableBean = odbDbTableDao.findById(id);
Integer categoryId = tableBean.getCatalogClass();
DataCatalogClass catalogClassBean = dataCatalogClassDao.findById(categoryId);
tableBean.setCatalogClassNameCn(catalogClassBean.getName());
return tableBean;
}
代码看似没有问题,无非就是把对应Bean查询出来,然后对Bean的某个字段进行Set赋值返回给前端使用。
2、问题暴露
上面的代码放在其他持久化框架可能不会出现问题。但是这是JPA的框架中啊,所以出现的问题就是:
数据库查询出来的Bean进行Set方法设置值后,等到方法执行完。
那么JPA会自动帮你把这个Bean的数据持久化到数据库中。
如果你前后设置的值是一致的还好,如果不一致,那么就会导致不经意间怎么数据被修改了?
我明明是查询数据,怎么查询完数据后数据库的值还被改变了呢!
3、问题解决
要解决这个问题也很简单。下面提供2种方式!
方法一:创建一个新对象重新赋值
import org.springframework.beans.BeanUtils;
@Resource
private OdbDbTableDao odbDbTableDao;
public OdbDbTable dataAssetsDetail(String id) {
OdbDbTable tableBean = odbDbTableDao.findById(id);
// 不在源对象上直接修改,是因为jpa自动持久化会导致数据不一致
OdbDbTable newBean = BeanUtils.instantiate(OdbDbTable.class);
BeanUtils.copyProperties(tableBean, newBean);
Integer categoryId = newBean.getCatalogClass();
DataCatalogClass catalogClassBean = dataCatalogClassDao.findById(categoryId);
newBean.setCatalogClassNameCn(catalogClassBean.getName());
return newBean;
}
数据库查询出来的Bean存在数据后,重新创建一个新的Bean,将数据库的Bean的值全部拷贝到新的Bean中再返回给前端,就不会造成数据持久化了!
方法二:将Session从持久态变为游离态
// 从容器中引入这个Bean
@PersistenceContext
private EntityManager entityManger;
public OdbDbTable dataAssetsDetail(String id) {
OdbDbTable tableBean = odbDbTableDao.findById(id);
// 获得hibernate的session管理对象
HibernateEntityManager hEntityManager = (HibernateEntityManager)entityManager;
Session session = hEntityManager.getSession();
// 将该对象从持久态变为游离态,一定要先变成游离态再赋值
session.evict(tableBean);
// 再进行赋值
Integer categoryId = newBean.getCatalogClass();
DataCatalogClass catalogClassBean = dataCatalogClassDao.findById(categoryId);
tableBean.setCatalogClassNameCn(catalogClassBean.getName());
return tableBean;
}
从容器中拿到Hibernate的实体管理器Session,将Session从持久态变为游离态。那么再对数据库查询出来的数据Bean进行更改操作就不会体现在数据库中了。
4、后言
出现这个问题的原因还是自我本身对框架的逻辑以及实现原理不太清楚,导致在项目中出现了这个问题,弄得项目组排查了好一段时间。所以,谨在此记录这个问题!