From: Michael D. <mik...@us...> - 2004-09-14 17:50:05
|
Update of /cvsroot/nhibernate/nhibernate/src/NHibernate/Impl In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv11256/Impl Modified Files: BatcherImpl.cs EnumerableImpl.cs NonBatchingBatcher.cs SessionImpl.cs Removed Files: PreparerImpl.cs Log Message: Major refactoring with NDataReader and Batcher. NHibernate now uses an IDataReader returned from the Driver for as long as possible before converting to a NDataReader. This has nice perf gains on the simple performance test in the Test Fixtures. Index: SessionImpl.cs =================================================================== RCS file: /cvsroot/nhibernate/nhibernate/src/NHibernate/Impl/SessionImpl.cs,v retrieving revision 1.40 retrieving revision 1.41 diff -C2 -d -r1.40 -r1.41 *** SessionImpl.cs 28 Aug 2004 04:19:22 -0000 1.40 --- SessionImpl.cs 14 Sep 2004 17:49:55 -0000 1.41 *************** *** 92,98 **** [NonSerialized] private IBatcher batcher; - - [NonSerialized] private IPreparer preparer; - /// <summary> --- 92,95 ---- *************** *** 525,537 **** } - public IPreparer Preparer - { - get - { - if (preparer == null) preparer = new PreparerImpl(factory, this); - return preparer; - } - } - public ISessionFactoryImplementor Factory { --- 522,525 ---- Index: EnumerableImpl.cs =================================================================== RCS file: /cvsroot/nhibernate/nhibernate/src/NHibernate/Impl/EnumerableImpl.cs,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** EnumerableImpl.cs 2 Sep 2004 13:04:58 -0000 1.5 --- EnumerableImpl.cs 14 Sep 2004 17:49:55 -0000 1.6 *************** *** 37,41 **** /// </summary> /// <param name="reader">The <see cref="IDataReader"/> to enumerate over.</param> ! /// <param name="cmd">TODO: is this needed?</param> /// <param name="sess">The <see cref="ISession"/> to use to load objects.</param> /// <param name="types">The <see cref="IType"/>s contained in the <see cref="IDataReader"/>.</param> --- 37,41 ---- /// </summary> /// <param name="reader">The <see cref="IDataReader"/> to enumerate over.</param> ! /// <param name="cmd">The <see cref="IDbCommand"/> used to create the <see cref="IDataReader"/>.</param> /// <param name="sess">The <see cref="ISession"/> to use to load objects.</param> /// <param name="types">The <see cref="IType"/>s contained in the <see cref="IDataReader"/>.</param> *************** *** 70,76 **** log.Debug("exhausted results"); _currentResults = null; ! _reader.Close(); ! //TODO: H2.0.3 code to synch here to close the QueryStatement ! //sess.Batcher.CloseQueryStatement( cmd, rs ); } else --- 70,74 ---- log.Debug("exhausted results"); _currentResults = null; ! _sess.Batcher.CloseQueryCommand( _cmd, _reader ); } else --- PreparerImpl.cs DELETED --- Index: BatcherImpl.cs =================================================================== RCS file: /cvsroot/nhibernate/nhibernate/src/NHibernate/Impl/BatcherImpl.cs,v retrieving revision 1.4 retrieving revision 1.5 diff -C2 -d -r1.4 -r1.5 *** BatcherImpl.cs 31 Aug 2004 13:14:25 -0000 1.4 --- BatcherImpl.cs 14 Sep 2004 17:49:55 -0000 1.5 *************** *** 3,6 **** --- 3,7 ---- using System.Data; + using NHibernate.Driver; using NHibernate.Engine; using NHibernate.SqlCommand; *************** *** 70,81 **** return batchCommand; } ! public IDbCommand PrepareCommand(SqlString sql) { ExecuteBatch(); LogOpenPreparedCommands(); ! return session.Preparer.PrepareCommand(sql); } --- 71,190 ---- return batchCommand; } + + public IDbCommand Generate(SqlString sqlString) + { + IDbCommand cmd = factory.ConnectionProvider.Driver.GenerateCommand(factory.Dialect, sqlString); + if(log.IsDebugEnabled) + { + log.Debug( "Building an IDbCommand object for the SqlString: " + sqlString.ToString() ); + } + return cmd; + + } ! /// <summary> ! /// Prepares the <see cref="IDbCommand"/> for execution in the database. ! /// </summary> ! /// <param name="command"></param> ! /// <remarks> ! /// This takes care of hooking the <see cref="IDbCommand"/> up to an <see cref="IDbConnection"/> ! /// and <see cref="IDbTransaction"/> if one exists. It will call <c>Prepare</c> if the Driver ! /// supports preparing commands. ! /// </remarks> ! private void Prepare(IDbCommand command) { + try + { + if(log.IsInfoEnabled) + { + log.Info("Preparing " + command.CommandText); + } + + command.Connection = session.Connection; + JoinTransaction( command ); + if( factory.ConnectionProvider.Driver.SupportsPreparingCommands ) + { + command.Prepare(); + } + } + catch(Exception e) + { + throw new ApplicationException( + "While preparing " + command.CommandText + " an error occurred" + , e); + } + } + + /// <summary> + /// Joins the Command to the Transaction and ensures that the Session and IDbCommand are in + /// the same Transaction. + /// </summary> + /// <param name="command">The command to setup the Transaction on.</param> + /// <returns>A IDbCommand with a valid Transaction property.</returns> + /// TODO: move this into ITransaction and let the Transaction figure out how to + /// get the Command to be a part of it. When .net 2.0 and the new transaction interface + /// comes out it will probably have its own strategy... + private void JoinTransaction(IDbCommand command) + { + IDbTransaction sessionAdoTrx = null; + + // at this point in the code if the Transaction is not null then we know we + // have a Transaction object that has the .AdoTransaction property. In the future + // we will have a seperate object to represent an AdoTransaction and won't have a + // generic Transaction class - the existing Transaction class will become Abstract. + if(this.session.Transaction!=null) sessionAdoTrx = ((Transaction.Transaction)session.Transaction).AdoTransaction; + + + // if the sessionAdoTrx is null then we don't want the command to be a part of + // any Transaction - so lets set the command trx to null + if(sessionAdoTrx==null) + { + if(command.Transaction!=null) + { + log.Warn("set a nonnull IDbCommand.Transaction to null because the Session had no Transaction"); + } + command.Transaction = null; + } + + // make sure these are the same transaction - I don't know why we would have a command + // in a different Transaction than the Session, but I don't understand all of the code + // well enough yet to verify that. + else if (sessionAdoTrx!=command.Transaction) + { + + // got into here because the command was being initialized and had a null Transaction - probably + // don't need to be confused by that - just a normal part of initialization... + if(command.Transaction!=null) + { + log.Warn("The IDbCommand had a different Transaction than the Session. This can occur when " + + "Disconnecting and Reconnecting Sessions because the PreparedCommand Cache is Session specific."); + } + + command.Transaction = sessionAdoTrx; + } + + } + + public IDbCommand PrepareBatchCommand(SqlString sql) + { + if ( !sql.Equals(batchCommandSql) ) + { + batchCommand = PrepareCommand(sql); // calls ExecuteBatch() + batchCommandSql=sql; + } + return batchCommand; + } + + public IDbCommand PrepareCommand(SqlString sql) + { ExecuteBatch(); LogOpenPreparedCommands(); ! // do not actually prepare the Command here - instead just generate it because ! // if the command is associated with an ADO.NET Transaction/Connection while ! // another open one Command is doing something then an exception will be ! // thrown. ! return Generate( sql ); } *************** *** 85,89 **** // to ado.net since DataReader is forward only LogOpenPreparedCommands(); ! IDbCommand command = session.Preparer.PrepareCommand(sql); // not sure if this is needed because fetch size doesn't apply --- 194,203 ---- // to ado.net since DataReader is forward only LogOpenPreparedCommands(); ! ! // do not actually prepare the Command here - instead just generate it because ! // if the command is associated with an ADO.NET Transaction/Connection while ! // another open one Command is doing something then an exception will be ! // thrown. ! IDbCommand command = Generate( sql ); // session.Preparer.BuildCommand(sql); // not sure if this is needed because fetch size doesn't apply *************** *** 103,109 **** } ! public IDataReader GetDataReader(IDbCommand cmd) { ! IDataReader reader = cmd.ExecuteReader(); readersToClose.Add(reader); LogOpenReaders(); --- 217,248 ---- } ! public int ExecuteNonQuery(IDbCommand cmd) { ! int rowsAffected = 0; ! ! CheckReaders(); ! ! Prepare( cmd ); ! rowsAffected = cmd.ExecuteNonQuery(); ! ! return rowsAffected; ! } ! ! public IDataReader ExecuteReader(IDbCommand cmd) ! { ! CheckReaders(); ! ! Prepare( cmd );; ! ! IDataReader reader; ! if( factory.ConnectionProvider.Driver.SupportsMultipleOpenReaders==false ) ! { ! reader = new NHybridDataReader( cmd.ExecuteReader() ); ! } ! else ! { ! reader = cmd.ExecuteReader(); ! } ! readersToClose.Add(reader); LogOpenReaders(); *************** *** 111,167 **** } ! public void CloseQueryCommand(IDbCommand st, IDataReader reader) { ! commandsToClose.Remove(st); ! if( reader!=null ) { ! readersToClose.Remove(reader); } ! try ! { ! if( reader!=null) ! { ! LogCloseReaders(); ! reader.Close(); ! } ! } ! finally { ! CloseQueryCommand(st); } } ! public IDbCommand PrepareBatchCommand(SqlString sql) { ! if ( !sql.Equals(batchCommandSql) ) ! { ! batchCommand = PrepareCommand(sql); // calls ExecuteBatch() ! batchCommandSql=sql; ! } ! return batchCommand; } ! public void ExecuteBatch() { ! if ( batchCommand!=null ) { - IDbCommand ps = batchCommand; - batchCommand = null; - batchCommandSql = null; try { ! DoExecuteBatch(ps); } ! finally { ! CloseCommand(ps); } } ! } ! public void CloseCommand(IDbCommand cmd) ! { ! LogClosePreparedCommands(); } --- 250,309 ---- } ! /// <summary> ! /// Ensures that the Driver's rules for Multiple Open DataReaders are being followed. ! /// </summary> ! private void CheckReaders() { ! // early exit because we don't need to move an open IDataReader into memory ! // since the Driver supports mult open readers. ! if( factory.ConnectionProvider.Driver.SupportsMultipleOpenReaders ) { ! return; } ! for( int i=0; i<readersToClose.Count; i++ ) { ! ((NHybridDataReader)readersToClose[i]).ReadIntoMemory(); } + } ! public void CloseCommand(IDbCommand cmd, IDataReader reader) { ! //TODO: fix this up a little bit - don't like it having the same name and just ! // turning around and calling a diff method. ! CloseQueryCommand( cmd, reader ); ! //LogClosePreparedCommands(); } ! public void CloseCommands() { ! foreach( IDataReader reader in readersToClose ) { try { ! LogCloseReaders(); ! reader.Close(); } ! catch( Exception e ) { ! log.Warn( "Could not close IDataReader", e ); } } ! readersToClose.Clear(); ! foreach( IDbCommand cmd in commandsToClose ) ! { ! try ! { ! CloseQueryCommand(cmd); ! } ! catch(Exception e) ! { ! // no big deal ! log.Warn("Could not close a JDBC statement", e); ! } ! } ! commandsToClose.Clear(); } *************** *** 179,214 **** } ! CloseCommand( cmd ); } ! public void CloseCommands() { ! foreach( IDataReader reader in readersToClose ) { ! try { LogCloseReaders(); reader.Close(); } - catch( Exception e ) - { - log.Warn( "Could not close IDataReader", e ); - } } ! readersToClose.Clear(); ! foreach( IDbCommand cmd in commandsToClose ) { try { ! CloseQueryCommand(cmd); ! } ! catch(Exception e) { ! // no big deal ! log.Warn("Could not close a JDBC statement", e); } } - commandsToClose.Clear(); } --- 321,365 ---- } ! // CloseCommand( cmd, null ); } ! public void CloseQueryCommand(IDbCommand st, IDataReader reader) { ! commandsToClose.Remove(st); ! if( reader!=null ) { ! readersToClose.Remove(reader); ! } ! ! try ! { ! if( reader!=null) { LogCloseReaders(); reader.Close(); } } ! finally ! { ! CloseQueryCommand(st); ! } ! } ! public void ExecuteBatch() ! { ! if ( batchCommand!=null ) { + IDbCommand ps = batchCommand; + batchCommand = null; + batchCommandSql = null; try { ! DoExecuteBatch(ps); ! } ! finally { ! CloseCommand(ps, null); } } } *************** *** 231,243 **** { log.Debug( "about to open: " + openCommandCount + " open IDbCommands, " + openReaderCount + " open DataReaders" ); - openCommandCount++; } } private static void LogClosePreparedCommands() { if ( log.IsDebugEnabled ) { - openCommandCount--; log.Debug( "done closing: " + openCommandCount + " open IDbCommands, " + openReaderCount + " open DataReaders" ); } --- 382,395 ---- { log.Debug( "about to open: " + openCommandCount + " open IDbCommands, " + openReaderCount + " open DataReaders" ); } + openCommandCount++; } private static void LogClosePreparedCommands() { + openCommandCount--; + if ( log.IsDebugEnabled ) { log.Debug( "done closing: " + openCommandCount + " open IDbCommands, " + openReaderCount + " open DataReaders" ); } *************** *** 246,262 **** private static void LogOpenReaders() { ! if( log.IsDebugEnabled ) ! { ! openReaderCount++; ! } } private static void LogCloseReaders() { ! if( log.IsDebugEnabled ) ! { ! openReaderCount--; ! } } } } --- 398,410 ---- private static void LogOpenReaders() { ! openReaderCount++; } private static void LogCloseReaders() { ! openReaderCount--; } + + } } Index: NonBatchingBatcher.cs =================================================================== RCS file: /cvsroot/nhibernate/nhibernate/src/NHibernate/Impl/NonBatchingBatcher.cs,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** NonBatchingBatcher.cs 28 Aug 2004 04:18:40 -0000 1.5 --- NonBatchingBatcher.cs 14 Sep 2004 17:49:55 -0000 1.6 *************** *** 16,20 **** public override void AddToBatch(int expectedRowCount) { ! int rowCount = GetCommand().ExecuteNonQuery(); //negative expected row count means we don't know how many rows to expect --- 16,20 ---- public override void AddToBatch(int expectedRowCount) { ! int rowCount = this.ExecuteNonQuery( this.GetCommand() ); //negative expected row count means we don't know how many rows to expect |