您的位置:首页 > 其它

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

2009-11-03 09:46 267 查看
xml 代码

<class name="A" entity-name="A" table="aaa">

<id name="id">

<generator class="native"/>

<id> 部分,

它加了一个CollectionRecreateAction。这就为update 语句埋下了伏笔。

<array name="bs" cascade="all" fetch="join" >

<key column="a_id"/>

<list-index column="idx"/>

<one-to-many class="B"/>

<array>

<class>

<class name="B" lazy="true" table="bbb">

<id name="id">

<generator class="native"/>

<id>

<class>

这是一个hibernate自带的例子,

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();

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 {

public void recreate(PersistentCollection collection, Serializable id, SessionImplementor session)

throws HibernateException {

if ( !isInverse && isRowInsertEnabled() ) {

if ( log.isDebugEnabled() ) {

log.debug(

"Inserting collection: " +

MessageHelper.collectionInfoString( this, id, getFactory() )

);

}

try {

//create all the new entries

Iterator entries = collection.entries(this);

if ( entries.hasNext() ) {

collection.preInsert( this );

int i = 0;

int count = 0;

while ( entries.hasNext() ) {

final Object entry = entries.next();

if ( collection.entryExists( entry, i ) ) {

int offset = 1;

PreparedStatement st = null;

Expectation expectation = Expectations.appropriateExpectation( getInsertCheckStyle() );

boolean callable = isInsertCallable();

boolean useBatch = expectation.canBeBatched();

String sql = getSQLInsertRowString();

if ( useBatch ) {

if ( callable ) {

st = session.getBatcher().prepareBatchCallableStatement( sql );

}

else {

st = session.getBatcher().prepareBatchStatement( sql );

}

}

else {

if ( callable ) {

st = session.getBatcher().prepareCallableStatement( sql );

}

else {

st = session.getBatcher().prepareStatement( sql );

}

}

try {

offset+= expectation.prepare( st );

//TODO: copy/paste from insertRows()

int loc = writeKey( st, id, offset, session );

if ( hasIdentifier ) {

loc = writeIdentifier( st, collection.getIdentifier(entry, i), loc, session );

}

if ( hasIndex /*&& !indexIsFormula*/ ) {

loc = writeIndex( st, collection.getIndex(entry, i, this), loc, session );

}

loc = writeElement(st, collection.getElement(entry), loc, session );

if ( useBatch ) {

session.getBatcher().addToBatch( expectation );

}

else {

expectation.verifyOutcome( st.executeUpdate(), st, -1 );

}

collection.afterRowInsert( this, entry, i );

count++;

}

catch ( SQLException sqle ) {

if ( useBatch ) {

session.getBatcher().abortBatch( sqle );

}

throw sqle;

}

finally {

if ( !useBatch ) {

session.getBatcher().closeStatement( st );

}

}

}

i++;

}

if ( log.isDebugEnabled() ) {

log.debug( "done inserting collection: " + count + " rows inserted" );

}

}

else {

if ( log.isDebugEnabled() ) {

log.debug( "collection was empty" );

}

}

}

catch ( SQLException sqle ) {

throw JDBCExceptionHelper.convert(

sqlExceptionConverter,

sqle,

"could not insert collection: " +

MessageHelper.collectionInfoString( this, id, getFactory() ),

getSQLInsertRowString()

);

}

}

}

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

java 代码

/**

* process any unreferenced collections and then inspect all known collections,

* scheduling creates/removes/updates

*/

private void flushCollections(EventSource session) throws HibernateException {

log.trace("Processing unreferenced collections");

List list = IdentityMap.entries( session.getPersistenceContext().getCollectionEntries() );

int size = list.size();

for ( int i = 0; i < size; i++ ) {

Map.Entry me = ( Map.Entry ) list.get( i );

CollectionEntry ce = (CollectionEntry) me.getValue();

if ( !ce.isReached() && !ce.isIgnore() ) {

Collections.processUnreachableCollection( (PersistentCollection) me.getKey(), session );

}

}

// Schedule updates to collections:

log.trace( "Scheduling collection removes/(re)creates/updates" );

list = IdentityMap.entries( session.getPersistenceContext().getCollectionEntries() );

size = list.size();

ActionQueue actionQueue = session.getActionQueue();

for ( int i = 0; i < size; i++ ) {

Map.Entry me = (Map.Entry) list.get(i);

PersistentCollection coll = (PersistentCollection) me.getKey();

CollectionEntry ce = (CollectionEntry) me.getValue();

if ( ce.isDorecreate() ) {

session.getInterceptor().onCollectionRecreate( coll, ce.getCurrentKey() );

actionQueue.addAction(

new CollectionRecreateAction(

coll,

ce.getCurrentPersister(),

ce.getCurrentKey(),

session

)

);

}

if ( ce.isDoremove() ) {

session.getInterceptor().onCollectionRemove( coll, ce.getLoadedKey() );

actionQueue.addAction(

new CollectionRemoveAction(

coll,

ce.getLoadedPersister(),

ce.getLoadedKey(),

ce.isSnapshotEmpty(coll),

session

)

);

}

if ( ce.isDoupdate() ) {

session.getInterceptor().onCollectionUpdate( coll, ce.getLoadedKey() );

actionQueue.addAction(

new CollectionUpdateAction(

coll,

ce.getLoadedPersister(),

ce.getLoadedKey(),

ce.isSnapshotEmpty(coll),

session

)

);

}

}

actionQueue.sortCollectionActions();

}

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

java 代码

actionQueue.addAction(

new CollectionRecreateAction(

coll,

ce.getCurrentPersister(),

ce.getCurrentKey(),

session

)

);

如果你把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 代码

/**

* Perform all currently queued actions.

*

* @throws HibernateException error executing queued actions.

*/

public void executeActions() throws HibernateException {

executeActions( insertions );

executeActions( updates );

executeActions( collectionRemovals );

executeActions( collectionUpdates );

executeActions( collectionCreations );

executeActions( deletions );

}

当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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: