个人特别喜欢用jpa体系,新手初次使用JPA/Hibernate时修改某一个实体的属性值不需要手动save或者update,jpa会自动更新实体,你是否会好奇其原理实现?
tips: 文章中并不是一步步读源码,而是从初学者找问题的角度去反找源码
0x01 找源码
1 | //简单demo,这样jpa会自动执行update sql语句 |
我们都知道jpa是一个方案,hibernate是其中的一种实现,我们对这个方法打断点f8看源码如何实现的
源码真的很多初次不建议一点点观察,我的方法是一直f8发现哪一句执行之后会调用update语句就代表找到了关键点;
最终我们会找到这一个方法会执行update语句1
2
3
4
5
6
7
8
9
10
11
12
13// DefaultFlushEventListener.class
public void onFlush(FlushEvent event) throws HibernateException {
//省略源码...
flushEverythingToExecutions( event );
//下面这一句执行完控制台就会打印update语句
performExecutions( source );
postFlush( source );
//省略源码...
}
再依次进去方法里面查找实际执行处,actionQueue.executeActions(); -> executeActions( l ); -> e.execute() -> EntityUpdateAction.execute() -> persister.update(...);
直到发现
1 | //AbstractEntityPersister |
我们发现hibernate的更新操作实际去基于一个叫dirtyFields的东西来标记的,我们现在只需要找到ditryFields是在哪儿wirte进来的就能找到脏字段检测原理
我们找到dirtyFields的类EntityUpdateAction,并右键dirtyFields -> Find Usages 并打开idea弹出的usage窗口中value write写入值的地方,发现是传参进来的
1 | public EntityUpdateAction( |
并继续寻找EntityUpdateAction方法的调用处发现
1 | // 通过event获取到我们得核心dirty参数, 我们接着从event下手 |
接着打开event的类,FlushEntityEvent并右键字段dirtyProperties -> find Usages 找到value wirte写入的地方,发现两处
1 | //1 完全就是一个set方法没有任何用 |
发现传入核心参数的地方dirtyProperties,它是通过persister.findDirty( values, loadedState, entity, session );这个方法构造出来的,这就是核心点
我们再依次进入findDirty()方法
1 | public int[] findDirty(Object[] currentState, Object[] previousState, Object entity, SharedSessionContractImplementor session) throws HibernateException { |
进入TypeHepler.findDirty就清楚了原理(最好打个断点自己再调试深入理解一下)
1 | public static int[] findDirty( |
最终看到了内部逻辑,核心就是判断两个值是否相等,如果不相等就说明修改过了,则放入dirtyFields中并只更新这几个字段;
0x02 总结
当一个实体对象被加入到Session缓存中时,Session会为实体对象的值类型的属性复制一份快照。当Session清理缓存时,会先进行脏检查,即比较实体对象的当前属性与它的快照,来判断实体对象的属性是否发生了变化,如果发生了变化,就称这个对象是“脏对象”,Session会根据脏对象的最新属性来执行相关的SQL语句,从而同步更新数据库。