您的位置:首页 > 数据库

C#数据库编程---事务回滚

2016-11-13 15:31 323 查看
事务的四个特性之一是原子性,其含义是指对于特定操作序列组成的事务,要么全部完成,要么就一件也不做。如果在事务处理的过程中,发生未知的不可预料的错误,如何保证事务的原子性呢?当事务中止时,必须执行回滚操作,以便消除已经执行的操作对数据库的影响。

一般的情况下,在异常处理中使用回滚动作是比较好的想法。前面,我们已经得到了一个更新数据库的程序,并且验证了它的正确性,稍微修改一下,可以得到:

//RollBack.cs

using System;

using System.Data;

using System.Data.SqlClient;

namespace Aspcn

{

 public class DbTran

 {

  file://执行事务处理

  public void DoTran()

  {

   file://建立连接并打开

   SqlConnection myConn=GetConn();

   myConn.Open();

   SqlCommand myComm=new SqlCommand();

   SqlTransaction myTran;

   file://创建一个事务

   myTran=myConn.BeginTransaction();

   file://从此开始,基于该连接的数据操作都被认为是事务的一部分

   file://下面绑定连接和事务对象

   myComm.Connection=myConn;

   myComm.Transaction=myTran;

   try

   {

    file://定位到pubs数据库

    myComm.CommandText="USE pubs";

    myComm.ExecuteNonQuery();

   

    myComm.CommandText="UPDATE roysched SET royalty = royalty * 1.10 WHERE title_id LIKE 'Pc%'";

    myComm.ExecuteNonQuery();

    file://下面使用创建数据库的语句制造一个错误

    myComm.CommandText="Create database testdb";

    myComm.ExecuteNonQuery();

    myComm.CommandText="UPDATE roysched SET royalty = royalty * 1.20 WHERE title_id LIKE 'Ps%'";

    myComm.ExecuteNonQuery();

    file://提交事务

    myTran.Commit();

   }

   catch(Exception err)

   {

    myTran.Rollback();

    Console.Write("事务操作出错,已回滚。系统信息:"+err.Message);

   }

  }

  file://获取数据连接

  private SqlConnection GetConn()

  {

   string strSql="Data Source=localhost;Integrated Security=SSPI;user id=sa;password=";

   SqlConnection myConn=new SqlConnection(strSql);

   return myConn;

  }

 }

 public class Test

 {

  public static void Main()

  {

   DbTran tranTest=new DbTran();

   tranTest.DoTran();

   Console.WriteLine("事务处理已经成功完成。");

   Console.ReadLine();

  }

 }

}

首先,我们在中间人为地制造了一个错误——使用前面讲过的Create database语句。然后,在异常处理的catch块中有如下语句:

myTran.Rollback();

当异常发生时,程序执行流跳转到catch块中,首先执行的就是这条语句,它将当前事务回滚。在这段程序可以看出,在Create database之前,已经有了一个更新数据库的操作——将pubs数据库的roysched表中的所有title_id字段以“PC”开头的书籍的royalty字段的值都增加0.1倍。但是,由于异常发生而导致的回滚使得对于数据库来说什么都没有发生。由此可见,Rollback()方法维护了数据库的一致性及事务的原子性。

  使用存储点

事务只是一种最坏情况下的保障措施,事实上,平时系统的运行可靠性都是相当高的,错误很少发生,因此,在每次事务执行之前都检查其有效性显得代价太高——绝大多数的情况下这种耗时的检查是不必要的。我们不得不想另外一种办法来提高效率。

事务存储点提供了一种机制,用于回滚部分事务。因此,我们可以不必在更新之前检查更新的有效性,而是预设一个存储点,在更新之后,如果没有出现错误,就继续执行,否则回滚到更新之前的存储点。存储点的作用就在于此。要注意的是,更新和回滚代价很大,只有在遇到错误的可能性很小,而且预先检查更新的有效性的代价相对很高的情况下,使用存储点才会非常有效。

使用.net框架编程时,你可以非常简单地定义事务存储点和回滚到特定的存储点。下面的语句定义了一个存储点“NoUpdate”:

myTran.Save("NoUpdate");

当你在程序中创建同名的存储点时,新创建的存储点将替代原有的存储点。

在回滚事务时,只需使用Rollback()方法的一个重载函数即可:

  myTran.Rollback("NoUpdate");

下面这段程序说明了回滚到存储点的方法和时机:

using System;

using System.Data;

using System.Data.SqlClient;

namespace Aspcn

{

 public class DbTran

 { 

  file://执行事务处理

  public void DoTran()

  {

   file://建立连接并打开

   SqlConnection myConn=GetConn();

   myConn.Open();

   SqlCommand myComm=new SqlCommand();

   SqlTransaction myTran;

   file://创建一个事务

   myTran=myConn.BeginTransaction();

   file://从此开始,基于该连接的数据操作都被认为是事务的一部分

   file://下面绑定连接和事务对象

   myComm.Connection=myConn;

   myComm.Transaction=myTran;

   try

   {

    myComm.CommandText="use pubs";

    myComm.ExecuteNonQuery();

    myTran.Save("NoUpdate");

    myComm.CommandText="UPDATE roysched SET royalty = royalty * 1.10 WHERE title_id LIKE 'Pc%'";

    myComm.ExecuteNonQuery();

    file://提交事务

    myTran.Commit();

   }

   catch(Exception err)

   {

    file://更新错误,回滚到指定存储点

    myTran.Rollback("NoUpdate");

    throw new ApplicationException("事务操作出错,系统信息:"+err.Message);

   }

  }

  file://获取数据连接

  private SqlConnection GetConn()

  {

   string strSql="Data Source=localhost;Integrated Security=SSPI;user id=sa;password=";

   SqlConnection myConn=new SqlConnection(strSql);

   return myConn;

  }

 }

 public class Test

 {

  public static void Main()

  {

   DbTran tranTest=new DbTran();

   tranTest.DoTran();

   Console.WriteLine("事务处理已经成功完成。");

   Console.ReadLine();

  }

 }

}

很明显,在这个程序中,更新无效的几率是非常小的,而且在更新前验证其有效性的代价相当高,因此我们无须在更新之前验证其有效性,而是结合事务的存储点机制,提供了数据完整性的保证。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: