(1)前言:
最近在使用Spring Data Jpa开发的时候,发现更新单个对象的时候,可以这么写:
@Transactional(value = "oracleTM")
public E update(Serializable id, E newEntity) {
E entity = getEntity(id);
BeanUtils.copyProperties(newEntity, entity);
return entity;
}
没有写任何Update的语句,竟然就自动执行刷新了
(2)Spring Data Jpa实体状态分析
图中虚线框代表在持久化上下文中,所谓在持久化上下文的意思就是一旦该实体对象处于该环境中的时候,那么此实体的属性发生了任何的改变都会同步到数据库中,无需再自己手工调用管理器的方法,该记录会自动同步,这些操作是由AbstractFlushingEventListener完成。
(3)Spring Data Jpa更新分析
通过AbstractFlushingEventListener执行更新
//DAO层
public Z getEntity(Serializable id) {
//获取出来的数据是托管状态
return (Z) entityManager.find(getEntityClass(), new Long(id.toString()));
}
//SERVICE层
@Transactional(value = "oracleTM", readOnly = true)
public E getEntity(Serializable id) {
return getDao().getEntity(id);
}
@Transactional(value = "oracleTM")
public E update(Serializable id, E newEntity) {
E entity = getEntity(id);//托管状态
BeanUtils.copyProperties(newEntity, entity);//托管中的数据发生变化
return entity;
}
后台处理日志:
DEBUG: [org.hibernate.event.internal.AbstractFlushingEventListener.(143)prepareEntityFlushes] - Processing flush-time cascades
DEBUG: [org.hibernate.event.internal.AbstractFlushingEventListener.(184)prepareCollectionFlushes] - Dirty checking collections
DEBUG: [org.hibernate.event.internal.AbstractFlushingEventListener.(117)logFlushResults] - Flushed: 0 insertions, 1 updates, 0 deletions to 1 objects
(4)Spring Data Jpa 实体状态详解
瞬时状态:
实际上就是new了一个普通的JavaBean对象。
托管状态:
1.当1.瞬时对象调用了管理器的persist()后,即可将一般的JavaBean做为了持久Bean,该Bean的任何属性改动都会牵涉到数据库记录的改动。2.一旦该记录flush到数据库之后,并且事务提交了,那么此对象不在持久化上下文中,即:变为了游离(没人管的孩子)状态了。在游离状态的时候调用更新、刷新方法后,游离状态对象就变为了在持久化上下文的托管状态了。3.通过管理器的find方法,将实体从数据库查询出来后,该实体也就变为了托管形态。
持久化状态:
当处在托管状态的实体Bean被管理器flush了,那么就在极短暂的时间进入了持久化状态,事务提交之后,立刻变为了游离状态。您可以把持久化状态当做实实在在的数据库记录。
游离状态:
游离状态就是提交到数据库后,事务commit后实体的状态,因为事务已经提交了,此时实体的属性任你如何改变,也不会同步到数据库,因为游离是没人管的孩子,不在持久化上下文中。
销毁对象:
一般要删除一个持久化对象的时候都是先find出来,之后调用remove方法删之,此时这个对象就是销毁对象,实际上就是瞬时对象的另一种形态罢了。
(5)Spring Data Jpa 应用分析
1.实体管理器高级操作——getReference()
用于查询单记录实体,和find相似
代码如下
// 加载一个实体 T entity = entityManager.getReference(entityClass, id); |
它与find的区别就是:当根据主键查询记录不存在的时候,将抛出异常EntityNotFoundException。这样我们就可以捕获异常后做一些自己的处理。
2.实体管理器高级操作——提交方式FlushModeType
提交(调用flush)分为2种方式:
AUTO:自动提交,实体管理器会在适当的时机同步实际记录到数据库,也是默认的提交方式。
COMMIT:一旦一个事务完毕了,那么就立刻提交到数据库(忽略事务共享、事务传播)。
很多人建议使用默认的AUTO。
3.大量数据分批提交
有的时候我们需要循环保存数据,当保存大量数据的时候,如果到最后才提交所有数据,那么数据库的负载可能会比较大。我们可以这样做,每30个记录就提交(flush)一次。代码如下:
public void updateBatch(List<Z> list) {
for (int i = 0; i < list.size(); i++) {
entityManager.merge(list.get(i)); //变成托管状态
if (i % 30 == 0) {
entityManager.flush(); //变成持久化状态
entityManager.clear(); //变成游离状态
}
}
}
public void saveBatch(List<Z> list) {
for (int i = 0; i < list.size(); i++) {
entityManager.persist(list.get(i)); //变成托管状态
if (i % 30 == 0) {
entityManager.flush(); //变成持久化状态
entityManager.clear(); //变成游离状态
}
}
}
每到30条记录的时候就强制提交。
4.refresh()
该方法是和flush()相反,是将数据库记录重新读到实体中,这样实体也是出于持久化环境中了,处于托管状态。
5.clear()
该方法是将所有的处于上下文中的实体全部转换成游离状态,此时还没有及时flush到数据库的信息,很遗憾,将不会持久化到数据库中。不是急于释放资源的情况下,请慎用之。