事务

事务为保证数据的完整性,数据的一致性和程序语义的正确性提供了一种机制.事务机制中主要包括提交模式,事务隔离级别和保存点三部分.神通数据库和神通数据库 .NET DATA PROVIDER 驱动程序,对这三部分给出了较好的实现方法.在本章中,我们将依次介绍它们的特性和使用方法.

对于缺省情况下,DML操作(INSERT,UPDATE,DELETE)一旦被执行,将自动被提交.当然可以自己来改变 commit 模式,比如需要执行多条语句,这些语句要求在一个事务里面,那么这时候就必须创建一个 OscarTransaction 对象.可以通过调用Connection 对象的 BeginTransaction() 方法来获得 OscarTransaction 对象.创建一个事务 Transaction,然后就可以提交 Commit 或者回滚 Rollback 多条语句.

事务处理格式

- 创建事务对象(transaction = connect.BeginTransaction())
...
- 执行操作
...
- 保存到数据库 (transaction.Commit())
...
- 放弃操作 (transaction.Rollback())
在执行事务中,抛出异常自动回滚.

示例

using (var connect = new OscarConnection("Server=10.1.1.66;Port=2003;User Id=SYSDBA;Password=szoscar55;Database=OSRDB;"))
{
    connect.Open();
    using (var transaction = connect.BeginTransaction())
    {
        try
        {
            using (var command = connect.CreateCommand())
            {
                command.CommandText = "CREATE TABLE MY_TABLE (ID INT, NAME VARCHAR(16), AGE INT, SEX BOOLEAN, MARK TIMESTAMP)";
                command.ExecuteNonQuery();

                command.CommandText = "INSERT INTO MY_TABLE VALUES (@id,@name,@age,@sex,@mark)";
                command.Parameters.Add(new OscarParameter("id", OscarDbType.Int) { Value = 1 });
                command.Parameters.Add(new OscarParameter("name", OscarDbType.VarChar) { Value = "小张" });
                command.Parameters.Add(new OscarParameter("age", OscarDbType.Int) { Value = 20 });
                command.Parameters.Add(new OscarParameter("sex", OscarDbType.Boolean) { Value = true });
                command.Parameters.Add(new OscarParameter("mark", OscarDbType.TimeStamp) { Value = DateTime.Now });
                var result = command.ExecuteNonQuery();
                command.Parameters.Clear();

                transaction.Commit();
                // transaction.Rollback();

                command.CommandText = "SELECT * FROM MY_TABLE";
                using (OscarDataReader reader = command.ExecuteReader())
                {
                    Console.WriteLine("ID    NAME     AGE     SEX     MARK");
                    while (reader.Read())
                    {
                        var vals = new object[5];
                        reader.GetValues(vals);
                        Console.WriteLine("{0}     {1}     {2}      {3}    {4}", vals);
                    }
                }

                command.CommandText = "DROP TABLE MY_TABLE";
                command.ExecuteNonQuery();
            }
        }
        catch (Exception ex)
        {
            transaction.Rollback();
            Console.WriteLine(ex.Message);
        }
        finally
        {
        }
    }
}

提交模式

应用程序创建一个连接时,该连接默认事务自动提交,即每执行一次SQL语句,该SQL语句就被认为是一个事务,而被自动提交了.如果用户需要将多条语句包含在一个事务中,那么用户就要改变自动提交模式.用户可以在 Connection 对象中开始一个事务,并且可以提供事务提交模式.

OscarConnection conn = new OscarConnection(ConnectionString);
OscarTransaction trans = conn.BeginTransaction(IsolationLevel.Value);
...some code
trans.Commit(); // 或者是回滚事务 trans.Rollback();
如果开始了一个事务,则得到一个事务对象 OscarTransaction 对象.在事务结束时,应该调用事务的 Commit 方法将事务提交或者 Rollback 方法将事务回滚.
OscarConnectionBeginTransaction 方法可以不带参数,则默认的事务隔离级别为 IsolationLevel.ReadCommitted ; 也可以在 BeginTransaction 方法时作为参数给出.

在非自动提交模式下,用户在一个事务执行完成之后需要显式调用 OscarTransaction 对象的 commit() 方法来提交事务.

方法如下: trans.Commit();

当该事务需要回滚时,则需要显式调用 OscarTransaction 对象的 Rollback() 方法.

trans.Rollback();

当事务执行完被提交或回滚后,驱动程序将自动地释放事务对象,如果要再开启一个事务,则需要重复上面的步骤.如果事务对象没有显示的调用Commit方法或者Rollback方法,在该Connection关闭的时候(Close方法的时候),会自动调用事务对象的Commit方法进行提交.

隔离级别

神通数据库支持的事务隔离级别

  • TRANSACTION_READUNCOMMITTED(以TRANSACTION_READCOMMITTED来支持该级别)
  • TRANSACTION_READCOMMITTED
  • TRANSACTION_REPEATABLE_READ(以TRANSACTION_SERIALIZABLE来支持该级别)
  • TRANSACTION_SERIALIZABLE

默认的事务隔离级别为TRANSACTION_READ_COMMITTED.用户可以通过通过 Connection 接口中的 BeginTransaction 方法来开始一个事务,并设置事务隔离级别.由于事务隔离级别升高时,数据库系统为了保证语义的正确性,会加入更多的锁.当锁加得过多时,会降低程序的性能.因此在确定事务隔离级别的时候,用户应充分考虑数据一致性和性能要求之间的平衡.

保存点(SAVEPOINT)

保存点(Savepoint)提供了对事务更小粒度的控制方法,它代表一个逻辑事务点.在非自动提交模式下,一个事务中可以设置多个保存点,这样在回滚的时候,可以回滚到指定的保存点,从事务开始到该保存点之间的操作依然有效.这就为事务处理提供了更多的灵活性.保存点方法为事务对象的Save方法.

保存点方法必须为保存点命名。下面的例子演示了保存点的使用:

// Rollback方法用于回滚当前事务,Rollback方法既可以回滚整个事务,也可以回滚到某个savepoint。

// 使用rollback方法回滚整个事务

conn = new OscarConnection(connStr);
conn.Open();
txn = conn.BeginTransaction();
txn.Rollback();
txn.Dispose();
// 使用rollback方法回滚到某个特定的savepoint
txn = conn.BeginTransaction();
txn.Save("SavePoint1");
txn.Save("SavePoint2");
txn.Rollback("SavePoint2");
txn.Rollback("SavePoint1");
txn.Rollback();
txn.Dispose();

// 离开活动事务范围后调用Rollback会报错

txn = conn.BeginTransaction();
txn.Rollback();
gotIt = false;
try {
txn.Rollback();

}catch(OscarException e){
gotIt = true;
}

// 这里gotIt的值会为true

//Assert.IsTrue(gotIt);

Console.WriteLine(gotlt.ToString());
txn.Dispose();

// 指定错误的savepoint,后台报错

txn = conn.BeginTransaction();
txn.Save("SavePoint3");
gotIt = false;
try {
txn.Rollback("SavePoint4");
}catch(OscarException e){
gotIt = true;
}

//Assert.IsTrue(gotIt);

Console.WriteLine(gotlt.ToString());
txn.Dispose();
conn.Close();

这里应用的保存点和 Rollback 方法(以保存点名为参数)配合使用,可以使事务回滚到某一个点上.

用户也可以调用 Rollback 方法(无参数)来回滚全事务(无视保存点的存在).一旦回滚,事务对象立即变为null,所有保存点也将无效,试图引用也会导致异常.这里要注意另外一种情况,比如按顺序设置了保存点sp1,sp2,sp3,那么当用户回滚到sp2时,那么sp3保存点将不复存在,对sp3的释放或回滚操作将导致PROVIDER驱动程序抛出异常.

引用