版本:Spring Boot 1.5.8.RELEASE
JpaRepository
强烈建议 repo 接口继承 JpaRepository 因为其中拥有 flush 相关的一系列的方法,当执行save()
不一定会去提交到数据库,与数据库进行约束的匹配。
在通常情况下 JpaRepository 在注入的时候会是 SimpleJpaRepository 的实例
save()
@Transactional
public <S extends T> S save(S entity) {
// 判断一个对象是新的还是查询出来的
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
// 核心方法
return em.merge(entity);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
isNew()
判断一个对象是否为一个新实体,感觉目前有点困惑
当执行 isNew()
方法实际上的执行的是 JpaMetamodelEntityInformation
的方法
@Override public boolean isNew(T entity) { // 如果 versionAttribute 为 null 或者对应的类型是java原生类型 if (versionAttribute == null || versionAttribute.getJavaType().isPrimitive()) { return super.isNew(entity); }
BeanWrapper wrapper <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">DirectFieldAccessFallbackBeanWrapper</span><span class="token punctuation">(</span>entity<span class="token punctuation">)</span><span class="token punctuation">;</span> Object versionValue <span class="token operator">=</span> wrapper<span class="token punctuation">.</span><span class="token function">getPropertyValue</span><span class="token punctuation">(</span>versionAttribute<span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 返回版本号信息是否为null</span> <span class="token keyword">return</span> versionValue <span class="token operator">==</span> null<span class="token punctuation">;</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
super.isNew()
上文中的 super.isNew()
-> AbstractEntityInformation
public boolean isNew(T entity) {
// 获取entity的主键属性
ID id = getId(entity);
Class<ID> idType = getIdType();
// 如果是原生类型 integer Long ...
if (!idType.isPrimitive()) {
// 返回是否为空
return id == null;
}
// 如果实现 Number 接口 BigDecimal,BigInteger ...
if (id instanceof Number) {
// 就看值是不是为 0
return ((Number) id).longValue() == 0L;
}
// 都不是就抛出异常
throw new IllegalArgumentException(String.format("Unsupported primitive id type %s!", idType));
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
return em.merge(entity)
合并数据,根据注释阅读是进行将上下文数据进行持久态的转变的转变,将上下文中数据更新
AbstractEntityManagerImpl
具体执行的方法
@Override
@SuppressWarnings("unchecked")
public <A> A merge(A entity) {
checkOpen();
try {
// 拆开来阅读源码
// 1. internalGetSession() 返回一个数据库的session并且应该是没有添加校验的
// Return a Session without any validation checks.
// 2. session.merge( entity )
/**
* Copy the state of the given object onto the persistent object with the same
* identifier. If there is no persistent instance currently associated with
* the session, it will be loaded. Return the persistent instance. If the
* given instance is unsaved, save a copy of and return it as a newly persistent
* instance. The given instance does not become associated with the session.
* This operation cascades to associated instances if the association is mapped
* with {@code cascade="merge"}
* The semantics of this method are defined by JSR-220.
* <p/>
* /
/**
*将给定对象的状态复制到具有相同对象的持久对象上
*标识符。 如果当前没有与之关联的持久性实例
*会话,它将被加载。 返回持久化实例。 如果
*给定实例未保存,保存副本并将其作为新持久化返回
*实例。 给定的实例不会与会话关联。
*如果映射关联,此操作会级联到关联的实例
*与{@code cascade =“merge”}
* <p />
*此方法的语义由JSR-220定义。
*在下段代码将阅读一下 session.merge(entity)
*/
return ( A ) internalGetSession().merge( entity );
}
catch ( ObjectDeletedException sse ) {
throw convert( new IllegalArgumentException( sse ) );
}
catch ( MappingException e ) {
throw convert( new IllegalArgumentException( e.getMessage(), e ) );
}
catch ( RuntimeException e ) {
//including HibernateException
throw convert( e );
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
session.merge(entity)
具体的merge方法
SessionImpl
merge(entity)
// 入口
@Override
public Object merge(Object object) throws HibernateException {
return merge( null, object );
}
// 2ed
@Override
public Object merge(String entityName, Object object) throws HibernateException {
return fireMerge( new MergeEvent( entityName, object, this ) );
}
// 3th
private Object fireMerge(MergeEvent event) {
//如果session关闭报错
errorIfClosed();
//检查事务的同步的状态
checkTransactionSynchStatus();
//操作前检查没有未解决的操作
checkNoUnresolvedActionsBeforeOperation();
//合并数据的监听器 事件监听模式
for ( MergeEventListener listener : listeners( EventType.MERGE ) ) {
listener.onMerge( event );
}
//操作后检查没有未解决的操作
checkNoUnresolvedActionsAfterOperation();
//直接返回数据事件的数据
return event.getResult();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
onMerge( event )
来看看MergeEventListener
都做了什么鬼事情 DefaultMergeEventListener
大体上来说就真正在上下文中进行数据值更新持久太转换的逻辑
- 逻辑判断交叉引用
- 级联关系
- 上下文数据赋值
public void onMerge(MergeEvent event) throws HibernateException {
final EntityCopyObserver entityCopyObserver = createEntityCopyObserver( event.getSession().getFactory() );
final MergeContext mergeContext = new MergeContext( event.getSession(), entityCopyObserver );
try {
onMerge( event, mergeContext );
entityCopyObserver.topLevelMergeComplete( event.getSession() );
}
finally {
entityCopyObserver.clear();
mergeContext.clear();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
public void onMerge(MergeEvent event, Map copiedAlready) throws HibernateException { // 持久化的上下文 final MergeContext copyCache = (MergeContext) copiedAlready; // 事件源 final EventSource source = event.getSession(); // 事件目标 此时就是 待执行 save(持久化) 方法的对象 final Object original = event.getOriginal();
<span class="token keyword">if</span> <span class="token punctuation">(</span> original <span class="token operator">!=</span> null <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">final</span> Object entity<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> original <span class="token keyword">instanceof</span> <span class="token class-name">HibernateProxy</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> LazyInitializer li <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span>HibernateProxy<span class="token punctuation">)</span> original <span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getHibernateLazyInitializer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> li<span class="token punctuation">.</span><span class="token function">isUninitialized</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> LOG<span class="token punctuation">.</span><span class="token function">trace</span><span class="token punctuation">(</span> <span class="token string">"Ignoring uninitialized proxy"</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> event<span class="token punctuation">.</span><span class="token function">setResult</span><span class="token punctuation">(</span> source<span class="token punctuation">.</span><span class="token function">load</span><span class="token punctuation">(</span> li<span class="token punctuation">.</span><span class="token function">getEntityName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> li<span class="token punctuation">.</span><span class="token function">getIdentifier</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token comment">//EARLY EXIT!</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> entity <span class="token operator">=</span> li<span class="token punctuation">.</span><span class="token function">getImplementation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> entity <span class="token operator">=</span> original<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">//copyCache.containsKey( entity )</span> <span class="token comment">//如果此MergeContext包含指定合并实体的交叉引用,则返回true 到管理实体的结果。</span> <span class="token comment">//copyCache.isOperatedOn( entity )</span> <span class="token comment">//如果侦听器正在对指定的合并实体执行合并操作,则返回true。</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> copyCache<span class="token punctuation">.</span><span class="token function">containsKey</span><span class="token punctuation">(</span> entity <span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token punctuation">(</span> copyCache<span class="token punctuation">.</span><span class="token function">isOperatedOn</span><span class="token punctuation">(</span> entity <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> LOG<span class="token punctuation">.</span><span class="token function">trace</span><span class="token punctuation">(</span> <span class="token string">"Already in merge process"</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> event<span class="token punctuation">.</span><span class="token function">setResult</span><span class="token punctuation">(</span> entity <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> copyCache<span class="token punctuation">.</span><span class="token function">containsKey</span><span class="token punctuation">(</span> entity <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> LOG<span class="token punctuation">.</span><span class="token function">trace</span><span class="token punctuation">(</span> <span class="token string">"Already in copyCache; setting in merge process"</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> copyCache<span class="token punctuation">.</span><span class="token function">setOperatedOn</span><span class="token punctuation">(</span> entity<span class="token punctuation">,</span> <span class="token boolean">true</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> event<span class="token punctuation">.</span><span class="token function">setEntity</span><span class="token punctuation">(</span> entity <span class="token punctuation">)</span><span class="token punctuation">;</span> EntityState entityState <span class="token operator">=</span> null<span class="token punctuation">;</span> <span class="token comment">// Check the persistence context for an entry relating to this</span> <span class="token comment">//检查持久性上下文以获取与此相关的条目 </span> <span class="token comment">// entity to be merged... 要合并的实体......</span> <span class="token comment">// 获取上下文中的实体</span> EntityEntry entry <span class="token operator">=</span> source<span class="token punctuation">.</span><span class="token function">getPersistenceContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getEntry</span><span class="token punctuation">(</span> entity <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> entry <span class="token operator">==</span> null <span class="token punctuation">)</span> <span class="token punctuation">{</span> EntityPersister persister <span class="token operator">=</span> source<span class="token punctuation">.</span><span class="token function">getEntityPersister</span><span class="token punctuation">(</span> event<span class="token punctuation">.</span><span class="token function">getEntityName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> entity <span class="token punctuation">)</span><span class="token punctuation">;</span> Serializable id <span class="token operator">=</span> persister<span class="token punctuation">.</span><span class="token function">getIdentifier</span><span class="token punctuation">(</span> entity<span class="token punctuation">,</span> source <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> id <span class="token operator">!=</span> null <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">final</span> EntityKey key <span class="token operator">=</span> source<span class="token punctuation">.</span><span class="token function">generateEntityKey</span><span class="token punctuation">(</span> id<span class="token punctuation">,</span> persister <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">final</span> Object managedEntity <span class="token operator">=</span> source<span class="token punctuation">.</span><span class="token function">getPersistenceContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getEntity</span><span class="token punctuation">(</span> key <span class="token punctuation">)</span><span class="token punctuation">;</span> entry <span class="token operator">=</span> source<span class="token punctuation">.</span><span class="token function">getPersistenceContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getEntry</span><span class="token punctuation">(</span> managedEntity <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> entry <span class="token operator">!=</span> null <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// we have specialized case of a detached entity from the</span> <span class="token comment">// perspective of the merge operation. Specifically, we</span> <span class="token comment">// have an incoming entity instance which has a corresponding</span> <span class="token comment">// entry in the current persistence context, but registered</span> <span class="token comment">// under a different entity instance</span> entityState <span class="token operator">=</span> EntityState<span class="token punctuation">.</span>DETACHED<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
// public static enum EntityState {
// PERSISTENT, TRANSIENT, DETACHED, DELETED
// } 持久,短暂(临时),游离态,删除
<span class="token keyword">if</span> <span class="token punctuation">(</span> entityState <span class="token operator">==</span> null <span class="token punctuation">)</span> <span class="token punctuation">{</span>
entityState <span class="token operator">=</span> <span class="token function">getEntityState</span><span class="token punctuation">(</span> entity<span class="token punctuation">,</span> event<span class="token punctuation">.</span><span class="token function">getEntityName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> entry<span class="token punctuation">,</span> source <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">switch</span> <span class="token punctuation">(</span> entityState <span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">case</span> DETACHED<span class="token operator">:</span>
<span class="token function">entityIsDetached</span><span class="token punctuation">(</span> event<span class="token punctuation">,</span> copyCache <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">break</span><span class="token punctuation">;</span>
<span class="token keyword">case</span> TRANSIENT<span class="token operator">:</span>
<span class="token function">entityIsTransient</span><span class="token punctuation">(</span> event<span class="token punctuation">,</span> copyCache <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">break</span><span class="token punctuation">;</span>
<span class="token keyword">case</span> PERSISTENT<span class="token operator">:</span>
<span class="token comment">// 开始执行 持久化的代码</span>
<span class="token function">entityIsPersistent</span><span class="token punctuation">(</span> event<span class="token punctuation">,</span> copyCache <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">break</span><span class="token punctuation">;</span>
<span class="token keyword">default</span><span class="token operator">:</span> <span class="token comment">//DELETED</span>
<span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">ObjectDeletedException</span><span class="token punctuation">(</span>
<span class="token string">"deleted instance passed to merge"</span><span class="token punctuation">,</span>
null<span class="token punctuation">,</span>
<span class="token function">getLoggableName</span><span class="token punctuation">(</span> event<span class="token punctuation">.</span><span class="token function">getEntityName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> entity <span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
protected void entityIsPersistent(MergeEvent event, Map copyCache) {
LOG.trace( "Ignoring persistent instance" );
//TODO: check that entry.getIdentifier().equals(requestedId)
final Object entity = event.getEntity(); // 待持久化的实体
final EventSource source = event.getSession(); //会话session
// 还不晓得是什么鬼
final EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity );
// 把缓存中的数据与待缓存的数据进行合并 考虑交叉引用
//(真的好复杂,就这样效率感觉效率会很低啊,这应该就是传说中的一级缓存了)
( (MergeContext) copyCache ).put( entity, entity, true ); //before cascade!
//执行作为此复制事件的一部分所需的任何级联。 (级联? 暂不考虑了)
cascadeOnMerge( source, persister, entity, copyCache );
//把值重新赋值一下? 源码上没有注释 (实在是看不懂了)
copyValues( persister, entity, entity, source, copyCache );
//往事件中设置结果值
//(搞了半天 save 方法就是在操作对象的上下文数据 一级缓存内部的合并 交叉引用)
event.setResult( entity );
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
结论
目前源码一路debug下来发现
hibernate
的save
方法基本就是在操作hibernate
的session
和entity
的上下文,没有涉及数据的交互。在debug过程也发现真的的数据提交(执行sql语句)将sql发送给数据服务器是在事务提交或者主动flush
才会执行的。
其中不得不服hibernate
开发团队开发的心思缜密,在更新一级缓存时做了很多的工作
- 当前对象是否new出来的->执行
update
或者insert
- 考虑到了对象的交叉引用(因为debug的原因没有去比较深入的阅读源码)
- 考虑到了数据的级联更新(项目中没有使用暂时没有去了解)
- 事件监听
- 上下文数据赋值(hibernate 中是 merge 合并 状态更改 游离态,持久态…)
flush()
AbstractEntityManagerImpl
依旧使用的是这个类中方法
@Override public void flush() { //检查是否打开 session 暂不深入 checkOpen(); //事务相关检查 暂不深入 checkTransactionNeeded();
<span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token comment">// 分成两个部分 internalGetSession() 和 session.flush()</span> <span class="token comment">// 其中 internalGetSession() 返回一个session对象 具体在上一部分中已经介绍过了</span> <span class="token comment">// session.flush() 核心 下文将继续了解</span> <span class="token function">internalGetSession</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">flush</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> RuntimeException e <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token function">convert</span><span class="token punctuation">(</span> e <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
session.flush()
session的刷新算是一个入口吧,做一些判断
- session开启判断
- 事务状态判断
- 级联深度判断,hibernate认为存在级联时直接刷新有可能会有风险,目前对这个级联深度没有进一步的了解
- 创建一个flush事件,具有处理逻辑都在监听器里面实现了
貌似全是这样的看的有点难受,还是没能理解hibernate的设计哲学,强行逻辑搞成事件监听器模式,而且debug过程中往往发现监听器只有一个,是不是理论上我可以往hibernate的上下文添加监听器来处理搞事情,后续有机会会继续往这方面发展考虑
SessionImpl
@Override public void flush() throws HibernateException { // 顾名思义 session 关闭报错 errorIfClosed(); // 检查事务同步状态 checkTransactionSynchStatus();
<span class="token comment">// persistenceContext.getCascadeLevel()</span> <span class="token comment">// How deep are we cascaded? 获取级联的级别? </span> <span class="token keyword">if</span> <span class="token punctuation">(</span> persistenceContext<span class="token punctuation">.</span><span class="token function">getCascadeLevel</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">0</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 缓存级别大于0</span> <span class="token comment">// 级联冲洗是危险的 -> Flush during cascade is dangerous</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">HibernateException</span><span class="token punctuation">(</span> <span class="token string">"Flush during cascade is dangerous"</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 创建一个刷新的事件</span> FlushEvent flushEvent <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FlushEvent</span><span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span> FlushEventListener listener <span class="token operator">:</span> <span class="token function">listeners</span><span class="token punctuation">(</span> EventType<span class="token punctuation">.</span>FLUSH <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 事件监听 触发事件 核心(根据debug只有一个监听器)</span> listener<span class="token punctuation">.</span><span class="token function">onFlush</span><span class="token punctuation">(</span> flushEvent <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">delayedAfterCompletion</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
listener.onFlush( flushEvent )
监听器中的核心方法的处理逻辑,算是一个核心处理的入口吧
主要的关注点如下
- source.getEventListenerManager().flushStart()
- 通过前置条件为上下文刷新作出准备,然而源码阅读后发现并没有什么卵用
- flushEverythingToExecutions( event );
- 真正的上下文层面的刷新数据,数据库交互做准备,
- 寻找上下中待处理的数据,entity,集合等
- 做了同步处理,处理并发问题,其中一个设置flushing可能是一个锁设置
- 将待处理数据进行转换,生成SQL语句,参数等
- 真正的上下文层面的刷新数据,数据库交互做准备,
- performExecutions( source );
- 真正我们希望的事情,以一个特殊的SQL执行顺序以保证级联更新数据的正确性
- 更加上下文信息将待持久化的数据封装成preparedStatement
- 执行preparedStatement.executeUpdate() 最基本的jdbc
- 真正我们希望的事情,以一个特殊的SQL执行顺序以保证级联更新数据的正确性
DefaultFlushEventListener
public void onFlush(FlushEvent event) throws HibernateException { // session final EventSource source = event.getSession(); // 持久化上下文 final PersistenceContext persistenceContext = source.getPersistenceContext(); // 获取受管实体数量 || 获取集合条目 -> 待持久化的数据是否大于0 if ( persistenceContext.getNumberOfManagedEntities() > 0 || persistenceContext.getCollectionEntries().size() > 0 ) {
<span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token comment">// 事件源尝试开始刷新</span> <span class="token comment">// source.getEventListenerManager() -> SessionEventListenerManagerImpl</span> <span class="token comment">// SessionEventListenerManager.flushStart()</span> source<span class="token punctuation">.</span><span class="token function">getEventListenerManager</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">flushStart</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 应该是比较核心的方法了 但是 </span> <span class="token comment">//事与愿违 在执行完这个方法之后 并未进行数据库约束匹配</span> <span class="token function">flushEverythingToExecutions</span><span class="token punctuation">(</span> event <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 这是 performExecutions( source ) 的 源注释</span> <span class="token comment">//Execute all SQL (and second-level cache updates) in a special order </span> <span class="token comment">//so that foreign-key constraints cannot</span> <span class="token comment">// 以特殊的顺序执行所有sql的语句(和耳机缓存的更新)以便于处理级联和外键关联</span> <span class="token comment">// 目测应该就是这个方法了</span> <span class="token function">performExecutions</span><span class="token punctuation">(</span> source <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">postFlush</span><span class="token punctuation">(</span> source <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span> source<span class="token punctuation">.</span><span class="token function">getEventListenerManager</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">flushEnd</span><span class="token punctuation">(</span> event<span class="token punctuation">.</span><span class="token function">getNumberOfEntitiesProcessed</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> event<span class="token punctuation">.</span><span class="token function">getNumberOfCollectionsProcessed</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">postPostFlush</span><span class="token punctuation">(</span> source <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> source<span class="token punctuation">.</span><span class="token function">getFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getStatistics</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">isStatisticsEnabled</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> source<span class="token punctuation">.</span><span class="token function">getFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getStatisticsImplementor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">flush</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
SessionEventListenerManager.flushStart()
感觉没有什么营养
SessionEventListenerManager
-> (只有一个实现类) SessionEventListenerManagerImpl
private List<SessionEventListener> listenerList;
@Override
public void flushStart() {
// 默认情况下 listenerList = null
if ( listenerList == null ) {
return;
}
<span class="token keyword">for</span> <span class="token punctuation">(</span> SessionEventListener listener <span class="token operator">:</span> listenerList <span class="token punctuation">)</span> <span class="token punctuation">{</span>
listener<span class="token punctuation">.</span><span class="token function">flushStart</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
flushEverythingToExecutions( event )
真正的上下文层面的刷新数据,数据库交互做准备
AbstractFlushingEventListener
/** * Coordinates the processing necessary to get things ready for executions * as db calls by preping the session caches and moving the appropriate * entities and collections to their respective execution queues. * * @param event The flush event. * @throws HibernateException Error flushing caches to execution queues. * *协调处理准备就绪所需的处理 *作为db调用,通过预先设置会话缓存并移动相应的缓存 *实体和集合到各自的执行队列。 */ protected void flushEverythingToExecutions(FlushEvent event) throws HibernateException {
LOG<span class="token punctuation">.</span><span class="token function">trace</span><span class="token punctuation">(</span> <span class="token string">"Flushing session"</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> EventSource session <span class="token operator">=</span> event<span class="token punctuation">.</span><span class="token function">getSession</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">final</span> PersistenceContext persistenceContext <span class="token operator">=</span> session<span class="token punctuation">.</span><span class="token function">getPersistenceContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 获取刷新之前的拦截器,执行预刷新逻辑方法</span> session<span class="token punctuation">.</span><span class="token function">getInterceptor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">preFlush</span><span class="token punctuation">(</span> <span class="token keyword">new</span> <span class="token class-name">LazyIterator</span><span class="token punctuation">(</span> persistenceContext<span class="token punctuation">.</span><span class="token function">getEntitiesByKey</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 实体预刷新上下文</span> <span class="token function">prepareEntityFlushes</span><span class="token punctuation">(</span> session<span class="token punctuation">,</span> persistenceContext <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// we could move this inside if we wanted to</span> <span class="token comment">// tolerate collection initializations during</span> <span class="token comment">// collection dirty checking:</span> <span class="token comment">// 集合预刷新上下文</span> <span class="token function">prepareCollectionFlushes</span><span class="token punctuation">(</span> persistenceContext <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// now, any collections that are initialized</span> <span class="token comment">// inside this block do not get updated - they</span> <span class="token comment">// are ignored until the next flush</span> <span class="token comment">// 持久化上下午处于刷新过程中 (可能与同步多线程有关)</span> persistenceContext<span class="token punctuation">.</span><span class="token function">setFlushing</span><span class="token punctuation">(</span> <span class="token boolean">true</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token comment">// 刷新实体</span> <span class="token keyword">int</span> entityCount <span class="token operator">=</span> <span class="token function">flushEntities</span><span class="token punctuation">(</span> event<span class="token punctuation">,</span> persistenceContext <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">int</span> collectionCount <span class="token operator">=</span> <span class="token function">flushCollections</span><span class="token punctuation">(</span> session<span class="token punctuation">,</span> persistenceContext <span class="token punctuation">)</span><span class="token punctuation">;</span> event<span class="token punctuation">.</span><span class="token function">setNumberOfEntitiesProcessed</span><span class="token punctuation">(</span> entityCount <span class="token punctuation">)</span><span class="token punctuation">;</span> event<span class="token punctuation">.</span><span class="token function">setNumberOfCollectionsProcessed</span><span class="token punctuation">(</span> collectionCount <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span> persistenceContext<span class="token punctuation">.</span><span class="token function">setFlushing</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">//some statistics</span> <span class="token function">logFlushResults</span><span class="token punctuation">(</span> event <span class="token punctuation">)</span><span class="token punctuation">;</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
flushEntities( event, persistenceContext )
查看对于单个实体是如何进行flush的
-
检测任何脏实体
-
安排需要更新的实体
-
搜索任何可到达的集合 -> (个人猜测)级联更新
-
以后hibernate封装的线程安全的方式获取待flush的对象
-
进行对象hibernate上下状态判断及处理(持久态,游离态…)
- 一些核心逻辑以监听器的方式执行(不说了,难受 ?)
AbstractFlushingEventListener
/** * 1. detect any dirty entities 检测任何脏实体 * 2. schedule any entity updates 安排任何实体更新 * 3. search out any reachable collections 搜索任何可到达的集合 */ private int flushEntities(final FlushEvent event, final PersistenceContext persistenceContext) throws HibernateException {
LOG<span class="token punctuation">.</span><span class="token function">trace</span><span class="token punctuation">(</span> <span class="token string">"Flushing entities and processing referenced collections"</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">final</span> EventSource source <span class="token operator">=</span> event<span class="token punctuation">.</span><span class="token function">getSession</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 又来了获取所有的刷新时执行的监听器 -> (debug时只有一个 JpaFlushEntityEventListener)</span> <span class="token keyword">final</span> Iterable<span class="token generics function"><span class="token punctuation"><</span>FlushEntityEventListener<span class="token punctuation">></span></span> flushListeners <span class="token operator">=</span> source<span class="token punctuation">.</span><span class="token function">getFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getServiceRegistry</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">getService</span><span class="token punctuation">(</span> EventListenerRegistry<span class="token punctuation">.</span><span class="token keyword">class</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">getEventListenerGroup</span><span class="token punctuation">(</span> EventType<span class="token punctuation">.</span>FLUSH_ENTITY <span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">listeners</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Among other things, updateReachables() will recursively load all</span> <span class="token comment">// collections that are moving roles. This might cause entities to</span> <span class="token comment">// be loaded.</span> <span class="token comment">// So this needs to be safe from concurrent modification problems.</span> <span class="token comment">//除其他外,updateReachables()将递归加载所有</span> <span class="token comment">//正在移动角色的集合。 这可能会导致实体</span> <span class="token comment">//被加载</span> <span class="token comment">//因此,这需要避免并发修改问题。</span> <span class="token comment">//以一种线程安全可重入的方式获取 Map 的底层存取数组</span> <span class="token keyword">final</span> Map<span class="token punctuation">.</span>Entry<span class="token generics function"><span class="token punctuation"><</span>Object<span class="token punctuation">,</span>EntityEntry<span class="token punctuation">></span></span><span class="token punctuation">[</span><span class="token punctuation">]</span> entityEntries <span class="token operator">=</span> persistenceContext<span class="token punctuation">.</span><span class="token function">reentrantSafeEntityEntries</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">final</span> <span class="token keyword">int</span> count <span class="token operator">=</span> entityEntries<span class="token punctuation">.</span>length<span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span> Map<span class="token punctuation">.</span>Entry<span class="token generics function"><span class="token punctuation"><</span>Object<span class="token punctuation">,</span>EntityEntry<span class="token punctuation">></span></span> me <span class="token operator">:</span> entityEntries <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Update the status of the object and if necessary, schedule an update</span> EntityEntry entry <span class="token operator">=</span> me<span class="token punctuation">.</span><span class="token function">getValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
/**
public enum Status {
MANAGED, 管理
READ_ONLY, 只读
DELETED, 删除
GONE, 游离态?
LOADING, 加载中
SAVING 持久户过程中?
}
*/
// debug 时处于 MANAGED
Status status = entry.getStatus();
<span class="token keyword">if</span> <span class="token punctuation">(</span> status <span class="token operator">!=</span> Status<span class="token punctuation">.</span>LOADING <span class="token operator">&&</span> status <span class="token operator">!=</span> Status<span class="token punctuation">.</span>GONE <span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">final</span> FlushEntityEvent entityEvent <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FlushEntityEvent</span><span class="token punctuation">(</span> source<span class="token punctuation">,</span> me<span class="token punctuation">.</span><span class="token function">getKey</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> entry <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span> FlushEntityEventListener listener <span class="token operator">:</span> flushListeners <span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// 目前debug也只有一个实现类 DefaultFlushEntityEventListener的方法</span>
listener<span class="token punctuation">.</span><span class="token function">onFlushEntity</span><span class="token punctuation">(</span> entityEvent <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment">// 对更新进行排序?</span>
source<span class="token punctuation">.</span><span class="token function">getActionQueue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">sortActions</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> count<span class="token punctuation">;</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
listener.onFlushEntity( entityEvent )
处理单个实体flush的核心逻辑
通过调度将单个实体的状态刷新到数据库必要时更新操作 这个是方法的文档说明
- 校验检查脏数据
- 获取到SQL执行的参数和参数类型
- 校验是否必要的更新 (具体不懂,不求甚解了)
- 搜索可达性集合(貌似与级联有关)已经删除的上下文数据不进行处理
DefaultFlushEntityEventListener
/** * Flushes a single entity's state to the database, by scheduling * an update action, if necessary * 通过调度将单个实体的状态刷新到数据库必要时更新操作 */ public void onFlushEntity(FlushEntityEvent event) throws HibernateException { final Object entity = event.getEntity(); final EntityEntry entry = event.getEntityEntry(); final EventSource session = event.getSession(); final EntityPersister persister = entry.getPersister(); final Status status = entry.getStatus(); final Type[] types = persister.getPropertyTypes();
<span class="token comment">//表示实体可能是脏的并且脏检查</span> <span class="token keyword">final</span> <span class="token keyword">boolean</span> mightBeDirty <span class="token operator">=</span> entry<span class="token punctuation">.</span><span class="token function">requiresDirtyCheck</span><span class="token punctuation">(</span> entity <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//通过debug得知values应该是sql语句执行的参数 下面有截图</span> <span class="token keyword">final</span> Object<span class="token punctuation">[</span><span class="token punctuation">]</span> values <span class="token operator">=</span> <span class="token function">getValues</span><span class="token punctuation">(</span> entity<span class="token punctuation">,</span> entry<span class="token punctuation">,</span> mightBeDirty<span class="token punctuation">,</span> session <span class="token punctuation">)</span><span class="token punctuation">;</span> event<span class="token punctuation">.</span><span class="token function">setPropertyValues</span><span class="token punctuation">(</span> values <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//TODO: avoid this for non-new instances where mightBeDirty==false</span> <span class="token comment">// 第一次false</span> <span class="token keyword">boolean</span> substitute <span class="token operator">=</span> <span class="token function">wrapCollections</span><span class="token punctuation">(</span> session<span class="token punctuation">,</span> persister<span class="token punctuation">,</span> types<span class="token punctuation">,</span> values <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//是必要的更新? 看不下去源码了 目前debug时是必要的</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token function">isUpdateNecessary</span><span class="token punctuation">(</span> event<span class="token punctuation">,</span> mightBeDirty <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 此时true</span> substitute <span class="token operator">=</span> <span class="token function">scheduleUpdate</span><span class="token punctuation">(</span> event <span class="token punctuation">)</span> <span class="token operator">||</span> substitute<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 目前status = MANAGED</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> status <span class="token operator">!=</span> Status<span class="token punctuation">.</span>DELETED <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// now update the object .. has to be outside the main if block above (because of collections)</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> substitute <span class="token punctuation">)</span> <span class="token punctuation">{</span> persister<span class="token punctuation">.</span><span class="token function">setPropertyValues</span><span class="token punctuation">(</span> entity<span class="token punctuation">,</span> values <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Search for collections by reachability, updating their role.</span> <span class="token comment">// We don't want to touch collections reachable from a deleted object</span> <span class="token comment">//按可达性搜索集合,更新其角色。</span> <span class="token comment">//我们不想触摸从已删除对象可到达的集合</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> persister<span class="token punctuation">.</span><span class="token function">hasCollections</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">new</span> <span class="token class-name">FlushVisitor</span><span class="token punctuation">(</span> session<span class="token punctuation">,</span> entity <span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">processEntityPropertyValues</span><span class="token punctuation">(</span> values<span class="token punctuation">,</span> types <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
上文中 values 截图为证
至此 flushEntities
才刚结束 就暂时不看 flushCollections
了太复杂了
小结
DefaultFlushEventListener onFlush
try {
source.getEventListenerManager().flushStart();
flushEverythingToExecutions( event );
// 在这个方法执行之前,只是刷新了刷新了上下文数据,为执行SQL语句做了准备
// 上下文级联对象管理,将数据实体进行包装得到SQL执行的参数等
// 根据注释 performExecutions( source )这里应该是执行sql的调用
performExecutions( source );
postFlush( source );
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
performExecutions( source )
真正开始要发送SQL语句执行存储
- 上下文同步处理
- 将此次session要处理的数据排序放进队列,上文介绍的保护级联数据更新的安全
- 执行session中待处理的队列
session.getActionQueue().executeActions();
DefaultFlushEventListener
protected void performExecutions(EventSource session) { LOG.trace( "Executing flush" );
<span class="token comment">// IMPL NOTE : here we alter the flushing flag of the persistence context to allow</span> <span class="token comment">// during-flush callbacks more leniency in regards to initializing proxies and</span> <span class="token comment">// lazy collections during their processing.</span> <span class="token comment">// For more information, see HHH-2763</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> session<span class="token punctuation">.</span><span class="token function">getJdbcCoordinator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">flushBeginning</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 设置持久化上下文正在刷新(同步处理?)</span> session<span class="token punctuation">.</span><span class="token function">getPersistenceContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setFlushing</span><span class="token punctuation">(</span> <span class="token boolean">true</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// we need to lock the collection caches before executing entity inserts/updates in order to</span> <span class="token comment">// account for bi-directional associations</span> <span class="token comment">// 准备内部操作队列以执行。</span> session<span class="token punctuation">.</span><span class="token function">getActionQueue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">prepareActions</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 执行所有当前排队的操作。</span> session<span class="token punctuation">.</span><span class="token function">getActionQueue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">executeActions</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span> <span class="token comment">// session持久化上下文结束刷新</span> session<span class="token punctuation">.</span><span class="token function">getPersistenceContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setFlushing</span><span class="token punctuation">(</span> <span class="token boolean">false</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> session<span class="token punctuation">.</span><span class="token function">getJdbcCoordinator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">flushEnding</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
session.getActionQueue().executeActions()
执行SQL真正的逻辑还是在监听器中执行
ActionQueue
/** * Perform all currently queued actions. * 执行所有当前排队的操作。 * @throws HibernateException error executing queued actions. */ public void executeActions() throws HibernateException { // 是否有未解析的实体插入操作依赖于与可暂时实体的非可空关联 // 所以有级联数据和多线程时是不是手动flush会导致上下文执行异常? if ( hasUnresolvedEntityInsertActions() ) { throw new IllegalStateException( "About to execute actions, but there are unresolved entity insert actions." ); }
<span class="token keyword">for</span> <span class="token punctuation">(</span> ListProvider listProvider <span class="token operator">:</span> EXECUTABLE_LISTS_MAP<span class="token punctuation">.</span><span class="token function">values</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> ExecutableList<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span> l <span class="token operator">=</span> listProvider<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> l <span class="token operator">!=</span> null <span class="token operator">&&</span> <span class="token operator">!</span>l<span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 开始执行sql 出现违反数据约束时抛出异常了</span> <span class="token function">executeActions</span><span class="token punctuation">(</span> l <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
executeActions( l )
监听器要执行的核心方法
- 个人猜测这里也是使用到了一个命令模式
private <E extends Executable & Comparable<?> & Serializable> void executeActions(ExecutableList<E> list) throws HibernateException { // todo : consider ways to improve the double iteration of Executables here: // 1) we explicitly iterate list here to perform Executable#execute() // 2) ExecutableList#getQuerySpaces also iterates the Executables to collect query spaces. try { for ( E e : list ) { try { e.execute(); } finally { if( e.getBeforeTransactionCompletionProcess() != null ) { if( beforeTransactionProcesses == null ) { beforeTransactionProcesses = new BeforeTransactionCompletionProcessQueue( session ); } beforeTransactionProcesses.register(e.getBeforeTransactionCompletionProcess()); } if( e.getAfterTransactionCompletionProcess() != null ) { if( afterTransactionProcesses == null ) { afterTransactionProcesses = new AfterTransactionCompletionProcessQueue( session ); } afterTransactionProcesses.register(e.getAfterTransactionCompletionProcess()); } } } } finally { if ( session.getFactory().getSessionFactoryOptions().isQueryCacheEnabled() ) { // Strictly speaking, only a subset of the list may have been processed if a RuntimeException occurs. // We still invalidate all spaces. I don't see this as a big deal - after all, RuntimeExceptions are // unexpected. Set<Serializable> propertySpaces = list.getQuerySpaces(); invalidateSpaces( propertySpaces.toArray( new Serializable[propertySpaces.size()] ) ); } }
list<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> session<span class="token punctuation">.</span><span class="token function">getJdbcCoordinator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">executeBatch</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
顺便看一下EntityInsertAction
的execute()
方法
@Override public void execute() throws HibernateException { nullifyTransientReferencesIfNotAlready();
<span class="token keyword">final</span> EntityPersister persister <span class="token operator">=</span> <span class="token function">getPersister</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">final</span> SessionImplementor session <span class="token operator">=</span> <span class="token function">getSession</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">final</span> Object instance <span class="token operator">=</span> <span class="token function">getInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">final</span> Serializable id <span class="token operator">=</span> <span class="token function">getId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">final</span> <span class="token keyword">boolean</span> veto <span class="token operator">=</span> <span class="token function">preInsert</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Don't need to lock the cache here, since if someone</span> <span class="token comment">// else inserted the same pk first, the insert would fail</span> <span class="token comment">//这里不需要锁定缓存,因为如果有人</span> <span class="token comment">//否则先插入相同的pk,插入会失败 </span> <span class="token comment">//-> 是不是在hibernate的事件不进行相关的约束判断,把约束校验规则交给数据去做</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span>veto <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 应该开始真正的执行了sql数据库方言转换等操作吧</span> persister<span class="token punctuation">.</span><span class="token function">insert</span><span class="token punctuation">(</span> id<span class="token punctuation">,</span> <span class="token function">getState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> instance<span class="token punctuation">,</span> session <span class="token punctuation">)</span><span class="token punctuation">;</span> PersistenceContext persistenceContext <span class="token operator">=</span> session<span class="token punctuation">.</span><span class="token function">getPersistenceContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">final</span> EntityEntry entry <span class="token operator">=</span> persistenceContext<span class="token punctuation">.</span><span class="token function">getEntry</span><span class="token punctuation">(</span> instance <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> entry <span class="token operator">==</span> null <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">AssertionFailure</span><span class="token punctuation">(</span> <span class="token string">"possible non-threadsafe access to session"</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> entry<span class="token punctuation">.</span><span class="token function">postInsert</span><span class="token punctuation">(</span> <span class="token function">getState</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
感觉经过不断的 debug 找到了重点 AbstractEntityPersister
的insert
方法
/** * Perform an SQL INSERT. 执行一条SQL语句 SQL 语句作为参数传入 * <p/> * This for is used for all non-root tables as well as the root table * in cases where the identifier value is known before the insert occurs. */ protected void insert( final Serializable id, final Object[] fields, final boolean[] notNull, final int j, final String sql, final Object object, final SessionImplementor session) throws HibernateException {
<span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token function">isInverseTable</span><span class="token punctuation">(</span> j <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">//note: it is conceptually possible that a UserType could map null to</span> <span class="token comment">// a non-null value, so the following is arguable:</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token function">isNullableTable</span><span class="token punctuation">(</span> j <span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token function">isAllNull</span><span class="token punctuation">(</span> fields<span class="token punctuation">,</span> j <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> LOG<span class="token punctuation">.</span><span class="token function">isTraceEnabled</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> LOG<span class="token punctuation">.</span><span class="token function">tracev</span><span class="token punctuation">(</span> <span class="token string">"Inserting entity: {0}"</span><span class="token punctuation">,</span> MessageHelper<span class="token punctuation">.</span><span class="token function">infoString</span><span class="token punctuation">(</span> <span class="token keyword">this</span><span class="token punctuation">,</span> id<span class="token punctuation">,</span> <span class="token function">getFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> j <span class="token operator">==</span> <span class="token number">0</span> <span class="token operator">&&</span> <span class="token function">isVersioned</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> LOG<span class="token punctuation">.</span><span class="token function">tracev</span><span class="token punctuation">(</span> <span class="token string">"Version: {0}"</span><span class="token punctuation">,</span> Versioning<span class="token punctuation">.</span><span class="token function">getVersion</span><span class="token punctuation">(</span> fields<span class="token punctuation">,</span> <span class="token keyword">this</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// TODO : shouldn't inserts be Expectations.NONE?</span> <span class="token keyword">final</span> Expectation expectation <span class="token operator">=</span> Expectations<span class="token punctuation">.</span><span class="token function">appropriateExpectation</span><span class="token punctuation">(</span> insertResultCheckStyles<span class="token punctuation">[</span>j<span class="token punctuation">]</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// we can't batch joined inserts, *especially* not if it is an identity insert;</span> <span class="token comment">// nor can we batch statements where the expectation is based on an output param</span> <span class="token keyword">final</span> <span class="token keyword">boolean</span> useBatch <span class="token operator">=</span> j <span class="token operator">==</span> <span class="token number">0</span> <span class="token operator">&&</span> expectation<span class="token punctuation">.</span><span class="token function">canBeBatched</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> useBatch <span class="token operator">&&</span> inserBatchKey <span class="token operator">==</span> null <span class="token punctuation">)</span> <span class="token punctuation">{</span> inserBatchKey <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BasicBatchKey</span><span class="token punctuation">(</span> <span class="token function">getEntityName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"#INSERT"</span><span class="token punctuation">,</span> expectation <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">final</span> <span class="token keyword">boolean</span> callable <span class="token operator">=</span> <span class="token function">isInsertCallable</span><span class="token punctuation">(</span> j <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token comment">// Render the SQL query</span> <span class="token comment">// 由此可知这个是预编译的 而且还是批处理的想想应该也是</span> <span class="token keyword">final</span> PreparedStatement insert<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> useBatch <span class="token punctuation">)</span> <span class="token punctuation">{</span> insert <span class="token operator">=</span> session <span class="token punctuation">.</span><span class="token function">getJdbcCoordinator</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">getBatch</span><span class="token punctuation">(</span> inserBatchKey <span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">getBatchStatement</span><span class="token punctuation">(</span> sql<span class="token punctuation">,</span> callable <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> insert <span class="token operator">=</span> session <span class="token punctuation">.</span><span class="token function">getJdbcCoordinator</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">getStatementPreparer</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">prepareStatement</span><span class="token punctuation">(</span> sql<span class="token punctuation">,</span> callable <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">int</span> index <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> index <span class="token operator">+=</span> expectation<span class="token punctuation">.</span><span class="token function">prepare</span><span class="token punctuation">(</span> insert <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Write the values of fields onto the prepared statement - we MUST use the state at the time the</span> <span class="token comment">// insert was issued (cos of foreign key constraints). Not necessarily the object's current state</span> <span class="token function">dehydrate</span><span class="token punctuation">(</span> id<span class="token punctuation">,</span> fields<span class="token punctuation">,</span> null<span class="token punctuation">,</span> notNull<span class="token punctuation">,</span> propertyColumnInsertable<span class="token punctuation">,</span> j<span class="token punctuation">,</span> insert<span class="token punctuation">,</span> session<span class="token punctuation">,</span> index<span class="token punctuation">,</span> <span class="token boolean">false</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> useBatch <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">//貌似在这里进行批处理开始匹配数据库的约束,在这里报错了session.getJdbcCoordinator().getBatch( inserBatchKey ).addToBatch();</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> expectation<span class="token punctuation">.</span><span class="token function">verifyOutcome</span><span class="token punctuation">(</span> session<span class="token punctuation">.</span><span class="token function">getJdbcCoordinator</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">getResultSetReturn</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">executeUpdate</span><span class="token punctuation">(</span> insert <span class="token punctuation">)</span><span class="token punctuation">,</span> insert<span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">1</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">SQLException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> useBatch <span class="token punctuation">)</span> <span class="token punctuation">{</span> session<span class="token punctuation">.</span><span class="token function">getJdbcCoordinator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">abortBatch</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">throw</span> e<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span>useBatch <span class="token punctuation">)</span> <span class="token punctuation">{</span> session<span class="token punctuation">.</span><span class="token function">getJdbcCoordinator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getResourceRegistry</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">release</span><span class="token punctuation">(</span> insert <span class="token punctuation">)</span><span class="token punctuation">;</span> session<span class="token punctuation">.</span><span class="token function">getJdbcCoordinator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">afterStatementExecution</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">SQLException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token function">getFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getSQLExceptionHelper</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">convert</span><span class="token punctuation">(</span> e<span class="token punctuation">,</span> <span class="token string">"could not insert: "</span> <span class="token operator">+</span> MessageHelper<span class="token punctuation">.</span><span class="token function">infoString</span><span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> sql <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
报错的截图如下
根据报错的日志可以看出都是hibernate的报错,会不会是hibernate在内存中进行的判断,然后接下来感觉就打脸了
ResultSetReturnImpl
//最终一次save最终的目的地就是executeUpdate方法 用一个预编译的PreparedStatement去执行
@Override
public int executeUpdate(PreparedStatement statement) {
try {
jdbcExecuteStatementStart();
return statement.executeUpdate();
}
catch (SQLException e) {
//然后就开始报错了
throw sqlExceptionHelper.convert( e, "could not execute statement" );
}
finally {
jdbcExecuteStatementEnd();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
小结
flush 的方法主要就是将hibernate上下文刷新,将上下文刷新应该有这么几个步骤
- 准备阶段 进行一些上下文刷新的准备 一些判断执行
- 上下文数据刷新阶段 更新数据状态,级联数据查询,生成SQL参数
- 上下文持久化刷新阶段,开始真的的执行SQL交互
真正的数据约束交互是在最后一步执行的
saveAndFlush
这就感觉不需要多说了直接上代码
先save再flush
@Transactional public <S extends T> S saveAndFlush(S entity) {
S result <span class="token operator">=</span> <span class="token function">save</span><span class="token punctuation">(</span>entity<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">flush</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> result<span class="token punctuation">;</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
</div><div data-report-view="{"mod":"1585297308_001","dest":"https://blog.csdn.net/cmmchenmm/article/details/82774448","extend1":"pc","ab":"new"}"><div></div></div>
<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-e0530931f4.css" rel="stylesheet">
</div>
</article>