關於ActionQueue
在hibernate中,Action可以理解爲對數據庫的操作。實體或集合的增、刪、改都有對應的Action。ActionQueue會對這些Action進行分類保存。
public class ActionQueue {
private ExecutableList<AbstractEntityInsertAction> insertions;
private ExecutableList<EntityDeleteAction> deletions;
private ExecutableList<EntityUpdateAction> updates;
private ExecutableList<CollectionRecreateAction> collectionCreations;
private ExecutableList<CollectionUpdateAction> collectionUpdates;
private ExecutableList<QueuedOperationCollectionAction> collectionQueuedOps;
private ExecutableList<CollectionRemoveAction> collectionRemovals;
.......
}
Session.flush
在瞭解ActionQueue之後,可以來看Session.flush了
public void flush() throws HibernateException {
checkOpen();//檢查Ssession是否關閉,如已關閉則rollback
doFlush();
}
熟悉的操作:生成event通知listener
private void doFlush() {
......//一些事務的校驗
try {
......//禁止在級聯過程中flush
FlushEvent flushEvent = new FlushEvent( this );
for ( FlushEventListener listener : listeners( EventType.FLUSH ) ) {
listener.onFlush( flushEvent );//通知listener
}
......
}
catch ( RuntimeException e ) {
throw exceptionConverter.convert( e );
}
}
- DefaultFlushEventListener#onFlush
監聽器中主要是這三個方法
1.flushEverythingToExecutions 掃描實體和集合,生成Action,保存到ActionQueue
2.performExecutions 執行ActionQueue,生成SQL語句
3.postFlush 重新加載集合
public void onFlush(FlushEvent event) throws HibernateException {
final EventSource source = event.getSession();
final PersistenceContext persistenceContext = source.getPersistenceContext();
if ( persistenceContext.getNumberOfManagedEntities() > 0 ||
persistenceContext.getCollectionEntries().size() > 0 ) {
try {
......//flush前的一些預處理
flushEverythingToExecutions( event );//1.生成Action,保存到ActinQueue
performExecutions( source );//2.執行ActionQueue,生成SQL語句
postFlush( source );//3.重新加載集合
}
finally {
......
}
......//flush後的一些處理
}
}
1.生成Action,保存到ActionQueue
主要做了這四件事
1.prepareEntityFlushes 檢查級聯的對象,刪除孤兒對象
2.prepareCollectionFlushes 檢查待操作的集合是否有髒數據
3.flushEntities 找出髒實體、準備實體的UpdateAction、找出有效的集合
4.flushCollections 找出沒有被引用的集合、掃描所有集合、準備集合的創建/刪除/更新
protected void flushEverythingToExecutions(FlushEvent event) throws HibernateException {
......
EventSource session = event.getSession();
final PersistenceContext persistenceContext = session.getPersistenceContext();
......
prepareEntityFlushes( session, persistenceContext );
// we could move this inside if we wanted to
// tolerate collection initializations during
// collection dirty checking:
prepareCollectionFlushes( persistenceContext );
// now, any collections that are initialized
// inside this block do not get updated - they
// are ignored until the next flush
try {
int entityCount = flushEntities( event, persistenceContext );
int collectionCount = flushCollections( session, persistenceContext );
event.setNumberOfEntitiesProcessed( entityCount );
event.setNumberOfCollectionsProcessed( collectionCount );
}
......
}
1.1.prepareEntityFlushes 檢查級聯的對象,刪除孤兒對象
比較複雜,看不太懂源碼,佔個坑先。
1.2.prepareCollectionFlushes 檢查待操作的集合是否有髒數據
是否檢查集合和集合元素是否可變,檢查集合和是否含有髒數據
1.3.flushEntities 找出髒實體、準備實體的UpdateAction、找出有效的集合
爲每個實體構建FlushEntityEvent事件,觸發FlushEntityEventListener,創建EntityUpdateAction,放到隊列中
對於實體裏的集合屬性,標記是否刪除/創建/更新(但不放入Action隊列中)、
1.4.flushCollections 找出沒有被引用的集合、掃描所有集合、準備集合的創建/刪除/更新
對所有集合做一次的標記
根據標記創建對應的Action,放入隊列中
private int flushCollections(final EventSource session, final PersistenceContext persistenceContext) throws HibernateException {
......
final Map.Entry<PersistentCollection,CollectionEntry>[] entries = IdentityMap.concurrentEntries(
(Map<PersistentCollection,CollectionEntry>) persistenceContext.getCollectionEntries()
);
final int count = entries.length;
for ( Map.Entry<PersistentCollection,CollectionEntry> me : entries ) {
CollectionEntry ce = me.getValue();
if ( !ce.isReached() && !ce.isIgnore() ) {
//檢查集合並打上標記
Collections.processUnreachableCollection( me.getKey(), session );
}
}
// Schedule updates to collections:
......
ActionQueue actionQueue = session.getActionQueue();
for ( Map.Entry<PersistentCollection,CollectionEntry> me :
IdentityMap.concurrentEntries( (Map<PersistentCollection,CollectionEntry>) persistenceContext.getCollectionEntries() )) {
PersistentCollection coll = me.getKey();
CollectionEntry ce = me.getValue();
//根據標記創建對應的Action,放入隊列中
if ( ce.isDorecreate() ) {
session.getInterceptor().onCollectionRecreate( coll, ce.getCurrentKey() );
actionQueue.addAction(
new CollectionRecreateAction(
coll,
ce.getCurrentPersister(),
ce.getCurrentKey(),
session
)
);
}
/* 後面的依次爲
* ce.isDoremove() 對應CollectionRemoveAction
* ce.isDoupdate() 對應CollectionUpdateAction
*( !coll.wasInitialized() && coll.hasQueuedOperations() ) 對應QueuedOperationCollectionAction
*/
......
}
actionQueue.sortCollectionActions();
return count;
}
2.執行操作隊列,生產SQL語句
以特定的順序執行SQL語句(包括二級緩存的),這個順序是爲了保證不違反外鍵約束
protected void performExecutions(EventSource session) {
LOG.trace( "Executing flush" );
// IMPL NOTE : here we alter the flushing flag of the persistence context to allow
// during-flush callbacks more leniency in regards to initializing proxies and
// lazy collections during their processing.
// For more information, see HHH-2763
try {
session.getJdbcCoordinator().flushBeginning();
session.getPersistenceContext().setFlushing( true );
// we need to lock the collection caches beforeQuery executing entity inserts/updates in order to
// account for bi-directional associations
session.getActionQueue().prepareActions();//給緩存集合加鎖
session.getActionQueue().executeActions();//按順序執行隊列
}
finally {
session.getPersistenceContext().setFlushing( false );
session.getJdbcCoordinator().flushEnding();
}
}
- 按順序執行隊列
1.INSERT
2.UPDATE
3.DELETE 集合
4.INSERT 集合
5.DELETE
利用LinkHashMap可以保持順序的特性,依次取得各個操作隊列的listProvider隊頭,然後遍歷隊內Action元素的execute方法,執行SQL語句。
public void executeActions() throws HibernateException {
if ( hasUnresolvedEntityInsertActions() ) {
throw new IllegalStateException( "About to execute actions, but there are unresolved entity insert actions." );
}
for ( ListProvider listProvider : EXECUTABLE_LISTS_MAP.values() ) {
ExecutableList<?> l = listProvider.get( this );
if ( l != null && !l.isEmpty() ) {
executeActions( l );//最終會調用Action.execute()來執行SQL
}
}
}
EXECUTABLE_LISTS_MAP這個比較有意思,一個LinkHashMap,key使用Action類,value爲ListProvider。可以理解爲隊頭。這個map用來控制隊列的執行順序,從代碼中可以看出更詳細的隊列分類及順序。
private static final LinkedHashMap<Class<? extends Executable>,ListProvider> EXECUTABLE_LISTS_MAP;
static {
EXECUTABLE_LISTS_MAP = new LinkedHashMap<Class<? extends Executable>,ListProvider>( 8 );
EXECUTABLE_LISTS_MAP.put(
OrphanRemovalAction.class,
new ListProvider<OrphanRemovalAction>() {
ExecutableList<OrphanRemovalAction> get(ActionQueue instance) {
return instance.orphanRemovals;
}
ExecutableList<OrphanRemovalAction> init(ActionQueue instance) {
// OrphanRemovalAction executables never require sorting.
return instance.orphanRemovals = new ExecutableList<OrphanRemovalAction>( false );
}
}
);
}
......
/*
* 剩下的依次爲:
* AbstractEntityInsertAction.class
* EntityUpdateAction.class
* QueuedOperationCollectionAction.class
* CollectionRemoveAction.class
* CollectionUpdateAction.class
* CollectionRecreateAction.class
* EntityDeleteAction.class
*/
}
突然發現Session.flush()到目前爲止並沒有提到EntityInsertAction的創建和進隊?其實在Session.save()中已經創建EntityInsertAction並派到隊伍中了。
3.重新加載集合
移除或者更新Session緩存中的集合
protected void postFlush(SessionImplementor session) throws HibernateException {
......
final PersistenceContext persistenceContext = session.getPersistenceContext();
persistenceContext.getCollectionsByKey().clear();
// the database has changed now, so the subselect results need to be invalidated
// the batch fetching queues should also be cleared - especially the collection batch fetching one
persistenceContext.getBatchFetchQueue().clear();
for ( Map.Entry<PersistentCollection, CollectionEntry> me : IdentityMap.concurrentEntries( persistenceContext.getCollectionEntries() ) ) {
CollectionEntry collectionEntry = me.getValue();
PersistentCollection persistentCollection = me.getKey();
collectionEntry.postFlush(persistentCollection);
if ( collectionEntry.getLoadedPersister() == null ) {
//if the collection is dereferenced, unset its session reference and remove from the session cache
//iter.remove(); //does not work, since the entrySet is not backed by the set
persistentCollection.unsetSession( session );
persistenceContext.getCollectionEntries()
.remove(persistentCollection);
}
else {
//otherwise recreate the mapping between the collection and its key
CollectionKey collectionKey = new CollectionKey(
collectionEntry.getLoadedPersister(),
collectionEntry.getLoadedKey()
);
persistenceContext.getCollectionsByKey().put(collectionKey, persistentCollection);
}
}
現在SQL已經被寫入到數據庫中,剩下就等事務提交了。