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 {
-
- 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 );
-
-
- int loc = writeKey( st, id, offset, session );
- if ( hasIdentifier ) {
- loc = writeIdentifier( st, collection.getIdentifier(entry, i), loc, session );
- }
- if ( hasIndex ) {
- 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 代码
-
-
-
-
- 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 );
- }
- }
-
-
-
- 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 代码
-
-
-
-
-
- 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;
}