hibernate 源码学习 多出来的update语句 之一

 xml 代码

  1. <class name="A" entity-name="A" table="aaa">           
  2.     <id name="id">  
  3.         <generator class="native"/>  
  4.     <id>   部分,
  5. 它加了一个CollectionRecreateAction。这就为update 语句埋下了伏笔
  6.     <array name="bs" cascade="all" fetch="join"  >  
  7.         <key column="a_id"/>  
  8.         <list-index column="idx"/>  
  9.         <one-to-many class="B"/>  
  10.     <array>  
  11. <class>  
  12. <class name="B" lazy="true" table="bbb">  
  13.     <id name="id">  
  14.         <generator class="native"/>  
  15.     <id>  
  16. <class>  

这是一个hibernate自带的例子,

  1. Session s;   
  2. Transaction tx;   
  3. s = openSession();   
  4. tx = s.beginTransaction();   
  5. A a = new A();   
  6. B b = new B();   
  7. a.setBs( new B[] {b} );   
  8. s.persist("A",a);   
  9. tx.commit();   
  10. s.close();   
java 代码:

Session s;   

Transaction tx;   

s = openSession();   

tx = s.beginTransaction();   

A a = new A();   

B b = new B();   

a.setBs( new B[] {b} );   

s.persist("A",a);   

tx.commit();   

s.close();   

 

 

会发现生成的sql语句有三条.

insert       into       aaa        (id)     values       (?)   

insert     into      bbb        (id)     values       (?)   

update       bbb     set       a_id=?,        idx=?     where      id=?  

 

 

  当然一般来说,你应该配置双向关联,在A的这一方设置reverse为true。但是很多人会有这样的疑问,我的顺序不就是先插入A,再插入B,当我不设置reverse的时候,究竟发生了什么,不禁想到hibernate源码中探个究竟。

通过调试,发现在flush的时候,先执行两条insert语句,然后产生一条update语句,并在下面的方法里面,初始化CollectionAction对象,这个对象会被 AbstractCollectionPersister 的recreate方法调用,为update的那一个 preparement 赋参数。(sql server) ,在oracle的环境下,由于在flush之前的persist动作的时候,就会调    select  hibernate_sequence.nextval    from  dual

取出主键,所以初始化CollectionAction对象这个方法,发生在insert sql 语句之前

java 代码

public CollectionAction(   

        final CollectionPersister persister,    

        final PersistentCollection collection,    

        final Serializable key,    

        final SessionImplementor session)   

throws CacheException {   

 

  1. public void recreate(PersistentCollection collection, Serializable id, SessionImplementor session)   
  2.         throws HibernateException {   
  3.   
  4.     if ( !isInverse && isRowInsertEnabled() ) {   
  5.   
  6.         if ( log.isDebugEnabled() ) {   
  7.             log.debug(    
  8.                     "Inserting collection: " +    
  9.                     MessageHelper.collectionInfoString( this, id, getFactory() )    
  10.                 );   
  11.         }   
  12.   
  13.         try {   
  14.             //create all the new entries   
  15.             Iterator entries = collection.entries(this);   
  16.             if ( entries.hasNext() ) {   
  17.                 collection.preInsert( this );   
  18.                 int i = 0;   
  19.                 int count = 0;   
  20.                 while ( entries.hasNext() ) {   
  21.   
  22.                     final Object entry = entries.next();   
  23.                     if ( collection.entryExists( entry, i ) ) {   
  24.                         int offset = 1;   
  25.                         PreparedStatement st = null;   
  26.                         Expectation expectation = Expectations.appropriateExpectation( getInsertCheckStyle() );   
  27.                         boolean callable = isInsertCallable();   
  28.                         boolean useBatch = expectation.canBeBatched();   
  29.                         String sql = getSQLInsertRowString();   
  30.   
  31.                         if ( useBatch ) {   
  32.                             if ( callable ) {   
  33.                                 st = session.getBatcher().prepareBatchCallableStatement( sql );   
  34.                             }   
  35.                             else {   
  36.                                 st = session.getBatcher().prepareBatchStatement( sql );   
  37.                             }   
  38.                         }   
  39.                         else {   
  40.                             if ( callable ) {   
  41.                                 st = session.getBatcher().prepareCallableStatement( sql );   
  42.                             }   
  43.                             else {   
  44.                                 st = session.getBatcher().prepareStatement( sql );   
  45.                             }   
  46.                         }   
  47.   
  48.   
  49.                         try {   
  50.                             offset+= expectation.prepare( st );   
  51.   
  52.                             //TODO: copy/paste from insertRows()   
  53.                             int loc = writeKey( st, id, offset, session );   
  54.                             if ( hasIdentifier ) {   
  55.                                 loc = writeIdentifier( st, collection.getIdentifier(entry, i), loc, session );   
  56.                             }   
  57.                             if ( hasIndex /*&& !indexIsFormula*/ ) {   
  58.                                 loc = writeIndex( st, collection.getIndex(entry, i, this), loc, session );   
  59.                             }   
  60.                             loc = writeElement(st, collection.getElement(entry), loc, session );   
  61.   
  62.                             if ( useBatch ) {   
  63.                                 session.getBatcher().addToBatch( expectation );   
  64.                             }   
  65.                             else {   
  66.                                 expectation.verifyOutcome( st.executeUpdate(), st, -1 );   
  67.                             }   
  68.   
  69.                             collection.afterRowInsert( this, entry, i );   
  70.                             count++;   
  71.                         }   
  72.                         catch ( SQLException sqle ) {   
  73.                             if ( useBatch ) {   
  74.                                 session.getBatcher().abortBatch( sqle );   
  75.                             }   
  76.                             throw sqle;   
  77.                         }   
  78.                         finally {   
  79.                             if ( !useBatch ) {   
  80.                                 session.getBatcher().closeStatement( st );   
  81.                             }   
  82.                         }   
  83.   
  84.                     }   
  85.                     i++;   
  86.                 }   
  87.   
  88.                 if ( log.isDebugEnabled() ) {   
  89.                     log.debug( "done inserting collection: " + count + " rows inserted" );   
  90.                 }   
  91.   
  92.             }   
  93.             else {   
  94.                 if ( log.isDebugEnabled() ) {   
  95.                     log.debug( "collection was empty" );   
  96.                 }   
  97.             }   
  98.         }   
  99.         catch ( SQLException sqle ) {   
  100.             throw JDBCExceptionHelper.convert(   
  101.                     sqlExceptionConverter,   
  102.                     sqle,   
  103.                     "could not insert collection: " +    
  104.                     MessageHelper.collectionInfoString( this, id, getFactory() ),   
  105.                     getSQLInsertRowString()   
  106.                 );   
  107.         }   
  108.     }   
  109. }  

 

以上是AbstractCollectionPersister类的一个方法。     那么这里我知道了是什么时候在为update prepament 赋值,但是我还是不知道为什么要分成三个sql语句执行。首先我们来看 update 语句产生的来龙去脉。

java 代码
  1. /**  
  2.  * process any unreferenced collections and then inspect all known collections,  
  3.  * scheduling creates/removes/updates  
  4.  */  
  5. private void flushCollections(EventSource session) throws HibernateException {   
  6.   
  7.     log.trace("Processing unreferenced collections");   
  8.   
  9.     List list = IdentityMap.entries( session.getPersistenceContext().getCollectionEntries() );   
  10.     int size = list.size();   
  11.     for ( int i = 0; i < size; i++ ) {   
  12.         Map.Entry me = ( Map.Entry ) list.get( i );   
  13.         CollectionEntry ce = (CollectionEntry) me.getValue();   
  14.         if ( !ce.isReached() && !ce.isIgnore() ) {   
  15.             Collections.processUnreachableCollection( (PersistentCollection) me.getKey(), session );   
  16.         }   
  17.     }   
  18.   
  19.     // Schedule updates to collections:   
  20.   
  21.     log.trace( "Scheduling collection removes/(re)creates/updates" );   
  22.   
  23.     list = IdentityMap.entries( session.getPersistenceContext().getCollectionEntries() );   
  24.     size = list.size();   
  25.     ActionQueue actionQueue = session.getActionQueue();   
  26.     for ( int i = 0; i < size; i++ ) {   
  27.         Map.Entry me = (Map.Entry) list.get(i);   
  28.         PersistentCollection coll = (PersistentCollection) me.getKey();   
  29.         CollectionEntry ce = (CollectionEntry) me.getValue();   
  30.   
  31.         if ( ce.isDorecreate() ) {   
  32.             session.getInterceptor().onCollectionRecreate( coll, ce.getCurrentKey() );   
  33.             actionQueue.addAction(   
  34.                     new CollectionRecreateAction(    
  35.                             coll,    
  36.                             ce.getCurrentPersister(),    
  37.                             ce.getCurrentKey(),    
  38.                             session    
  39.                         )   
  40.                 );   
  41.         }   
  42.         if ( ce.isDoremove() ) {   
  43.             session.getInterceptor().onCollectionRemove( coll, ce.getLoadedKey() );   
  44.             actionQueue.addAction(   
  45.                     new CollectionRemoveAction(    
  46.                             coll,    
  47.                             ce.getLoadedPersister(),    
  48.                             ce.getLoadedKey(),    
  49.                             ce.isSnapshotEmpty(coll),    
  50.                             session    
  51.                         )   
  52.                 );   
  53.         }   
  54.         if ( ce.isDoupdate() ) {   
  55.             session.getInterceptor().onCollectionUpdate( coll, ce.getLoadedKey() );   
  56.             actionQueue.addAction(   
  57.                     new CollectionUpdateAction(    
  58.                             coll,    
  59.                             ce.getLoadedPersister(),    
  60.                             ce.getLoadedKey(),    
  61.                             ce.isSnapshotEmpty(coll),    
  62.                             session    
  63.                         )   
  64.                 );   
  65.         }   
  66.   
  67.     }   
  68.   
  69.     actionQueue.sortCollectionActions();   
  70.        
  71. }  

 

在flush的时候,会调用上面的方法,注意

java 代码
  1. actionQueue.addAction(   
  2.         new CollectionRecreateAction(    
  3.                 coll,    
  4.                 ce.getCurrentPersister(),    
  5.                 ce.getCurrentKey(),    
  6.                 session    
  7.             )   
  8.     );  

 

如果你把AbstractFlushingEventListener 的日志设成debug,可以看到如下日志:

11:56:10,257 DEBUG AbstractFlushingEventListener:85 - Flushed: 2 insertions, 0 updates, 0 deletions to 2 objects
11:57:11,445 DEBUG AbstractFlushingEventListener:91 - Flushed: 1 (re)creations, 0 updates, 0 removals to 1 collections
11:57:16,753 DEBUG Printer:83 - listing entities:
11:57:16,753 DEBUG Printer:90 - org.hibernate.test.array.B{id=2}
11:57:16,753 DEBUG Printer:90 - org.hibernate.test.array.A

着色的部分,显示将会有一个1 (re)creations,实际上这就是那条update语句。

而在ActionQueue

java 代码
  1. /**  
  2.  * Perform all currently queued actions.  
  3.  *  
  4.  * @throws HibernateException error executing queued actions.  
  5.  */  
  6. public void executeActions() throws HibernateException {   
  7.     executeActions( insertions );   
  8.     executeActions( updates );   
  9.     executeActions( collectionRemovals );   
  10.     executeActions( collectionUpdates );   
  11.     executeActions( collectionCreations );   
  12.     executeActions( deletions );   
  13. }  

 

当executeActions( collectionCreations );这一句被调用时,就产生了哪一条update sql语句被调用。

其实到这里我就大致明白了。A作为主动方,它的处理方式就是产生一个update语句,得再对比一下inverse=true的情况。

而inverse=true的时候,executeActions( collectionCreations );就不会被调用了。

 

结论:我们总是习惯性的问为什么,其实它就是这么做的,种什么花,结什么果。

   this.persister = persister;   

   this.session = session;   

  this.key = key;   

  this.collectionRole = persister.getRole();   

  this.collection = collection;   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值