当前位置:首页>>ASP.NET教程>>Asp.NET综合技巧>>nhibernate源码分析之九: 事务处理
nhibernate源码分析之九: 事务处理
2009/11/8 13:19:18
对数据库的操作是少不了事务处理的,事务能保整数据完整性和有效性。 在nh中,使用Transaction对象对.net的事务对象(实现了IDbTransaction接口的对象)进行了包装。


在nh中,一个典型的事务处理是这样的(见ISession.cs的注释)
ISession sess = factory.OpenSession();
Transaction tx;
try {
   tx = sess.BeginTransaction();
   //do some work
   ...
   tx.Commit();
}
catch (Exception e) {
   if (tx != null) tx.Rollback();
   throw e;
}
finally {
   sess.Close();
}
事务对象由session的BeginTransaction取得,同时事务开始,如果执行顺利则提交事务,否则回滚事务。

先来看看事务对象的创建

public ITransaction BeginTransaction() {
   callAfterTransactionCompletionFromDisconnect = false;
   transaction = factory.TransactionFactory.BeginTransaction(this);
   return transaction;
}
很简单,直接调用事务工厂并开始事务。

public ITransaction BeginTransaction(ISessionImplementor session) {
   Transaction tx = new Transaction(session);
   tx.Begin();
   return tx;
}
构造一个事务对象,并开始事务。

//*** Transaction.cs 34行 ***

public void Begin() {
   try {
      IsolationLevel isolation = session.Factory.Isolation;
      if( isolation==IsolationLevel.Unspecified ) {
         trans = session.Connection.BeginTransaction();
      }
      else {
         trans = session.Connection.BeginTransaction( isolation );
      }
   }
   catch( Exception e ) {
     throw new TransactionException("Begin failed with SQL exception", e);
   }
   begun = true;
}
根据隔离级别启动一个事务,注意是从数据连接开始一个事务哦。
从上面可以看出nh对数据库事务的包装是十分简单的。

在这种典型的事务处理方法中,transaction依赖于一个特定的session对象, 因为session关闭时将断开数据连接,如果没有提交事务,那么事务是提交还是回滚是由具体的数据库决定的, 但无论怎样,事务都会结束。

这样的话就存在一个问题,在面向对象开发的情况下,可能有一个事务操作的多个步骤分散在不同的对象中,为了保证此事务操作的原子性,显然需要将session传递到各个步骤(方法)中,这样才能保证所有步骤都在一个session(事务)中处理。

在nh中,数据库事务的生命周期是可以长于session的生命周期的,就是说数据库事务不必依赖于一个特定的session对象,这看起来不错,我们不用再传递session对象了。注意,我这里指的是数据库事务(IDbTransaction)。

下面就来实现这种事务处理方法,代码如下:

ISessionFactoryImplementor sfe = factory as ISessionFactoryImplementor;
IDbConnection conn = sfe.OpenConnection();
IDbTransaction trans
try
   trans = conn.BeginTransaction(); // 开始事务,这里也可以设置事务隔离级别。

   ISession s = factory.OpenSession(conn);
   // do something
   // ...
   s.Close();

   ISession s2 = factory.OpenSession(conn);
   // do something
   // ...
   s2.Close();

   trans.Commit(); // 提交事务
catch (Exceptin ex) {
   if ( trans != null ) trans.Rollback(); // 回滚事务
   throw ex
}
finally{
   conn.CloseConnection(conn);
}
首先取得数据库连接,然后开始事务,接着创建会话处理业务操作,注意在创建会话的时候把数据加连接也传递了给去,这是必需的。上面代码中最值的注意的就是会话的Close操作了,这个操作在默认情况下是会关闭数据库连接的,在这里之所以没有关闭连接,是因为OpenSession操作有带一个连接的缘故。

下面来看看nh中的相关代码

public ISession OpenSession(IDbConnection connection) {
   return OpenSession(connection, interceptor);
}

public ISession OpenSession(IDbConnection connection, IInterceptor interceptor) {
   return OpenSession( connection, false, long.MinValue, interceptor );
}

private ISession OpenSession(IDbConnection connection, bool autoClose,
       long timestamp, IInterceptor interceptor) {
   return new SessionImpl( connection, this, autoClose, timestamp, interceptor );
}
这里有一个关健的参数autoClose, 它指session是否自动关闭数据库连接。

public IDbConnection Close() {
   try {
      return (connection==null) ? null : Disconnect();
   }
   finally {
      Cleanup();
   }
}

public IDbConnection Disconnect() {
   try {
      if (connect) {
         connect = false;
         return null;
      }
      else {
         if (connection==null) throw new HibernateException("session already disconnected");
         if (batcher!=null) batcher.CloseCommands();
         IDbConnection c = connection;
         connection=null;
         if (autoClose) {
            factory.CloseConnection(c);
            return null;
         }
         else  {
            return c;
         }
      }
   }
   finally {
      if ( callAfterTransactionCompletionFromDisconnect ) {
         AfterTransactionCompletion();
      }
   }
}
从上面的代码中可以看出,如果autoClose为true才会自动关闭连接。

这样就实现了数据库事务的生命周期长于会话生命周期的事务处理方法,至于这种方法有没使用性,那就不好说了,因为要在OpenSession时使用数据库连接,那么如果我们把操作分散于各个对象之中的话,显然必须传递数据库连接或事务,但至少做到了数据库事务与会话无关。

[1]