Jpa save 与 flush debug源码过程

版本: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">&amp;&amp;</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下来发现hibernatesave方法基本就是在操作hibernatesessionentity的上下文,没有涉及数据的交互。在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">&gt;</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">// 级联冲洗是危险的 -&gt; 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

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() -&gt; 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">// 又来了获取所有的刷新时执行的监听器 -&gt; (debug时只有一个 JpaFlushEntityEventListener)</span>
<span class="token keyword">final</span> Iterable<span class="token generics function"><span class="token punctuation">&lt;</span>FlushEntityEventListener<span class="token punctuation">&gt;</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">&lt;</span>Object<span class="token punctuation">,</span>EntityEntry<span class="token punctuation">&gt;</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">&lt;</span>Object<span class="token punctuation">,</span>EntityEntry<span class="token punctuation">&gt;</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">&amp;&amp;</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">&lt;</span><span class="token operator">?</span><span class="token operator">&gt;</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">&amp;&amp;</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


顺便看一下EntityInsertActionexecute()方法

@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">//-&gt; 是不是在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 找到了重点 AbstractEntityPersisterinsert 方法

/**
 * 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">&amp;&amp;</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">&amp;&amp;</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">&amp;&amp;</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">&amp;&amp;</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="{&quot;mod&quot;:&quot;1585297308_001&quot;,&quot;dest&quot;:&quot;https://blog.csdn.net/cmmchenmm/article/details/82774448&quot;,&quot;extend1&quot;:&quot;pc&quot;,&quot;ab&quot;:&quot;new&quot;}"><div></div></div>
            <link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-e0530931f4.css" rel="stylesheet">
                            </div>
        </article>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值