• 不得不提的事务处理


       先说一下为什么要说到事务处理,实际上对于数据处理来说,对于数据库来说,整个程序开发发展到现在,任何程序任何项目都离不开数据处理,数据库,事务是一个非常重要的概念,不仅是ASP.NET当中甚至其他的编程语言编程环境当中,事务处理是一个非常重要的问题,也就是说事务处理本身是超脱于ASP.NET这个范围的;
       1-有人问了什么是事务处理?
       事务是一组组合成逻辑工作单元的数据库操作(所以看到这大家应该明白,事务是相对于数据库操作来的,事务本身是因为数据库的出现而出现的,为了对数据库操作过程中出现的问题而提出了事务的概念),虽然系统可能出错,但是事务将控制和维护每个数据库的一致性和完整性,也就是说事务处理的目的是为了维护数据库的一致性和完整性,并且事务是个单元,所谓的单元就是如果没有错误的话就修改成功,这样理解作为事务来说这些基本的操作要么全部成功,要么全部失败,我们知道这是在任何数据库当中都会遇到的这样一个问题;
       2-事务处理的过程是怎样的呢?
       事务处理流程是和数据库没有关系的,也就是说任何的数据库都是遵照这个流程来做的;1:开始一个事务,进入”事务待命“状态;2:在“事务待命”状态,记录事务中改变的数据库记录,注意不能直接改变数据库当中的值,必须先用一个顺序的”事务日志“记录在一边,(把它记下来,要改哪些,把它记在事务日志当中),同时对要改变的原始记录进行加锁,让其他用户无法读写(无法读写就是同一个时刻呢我们只允许一个用户对某个数据进行读写,这样的目的是为了提高保证数据的一致性),如果记录已被其他事务加锁,就报错;3:在“事务待命”状态如果用户给出Commit Transaction 命令,则进入事务拷贝状态,拷贝所有加锁的记录成备份(为什么备份,为了以后出错的时候还能返回);4:上面3执行完了,则进入”事务更新状态“,这是关键的更新状态(不是一上来就更新,首先药记录下来要对哪些值进行更新,然后把要更新的值复制一份作为备份,然后在进行更新),用“事务日志”中的记录一一更新实际的数据库记录;5:上面4执行完则进入“事务结束状态”,释放所有的记录锁,然后抛弃“事务日志”和备份的数据库记录;6:上面作完后删除事务,这是一个常见的事务处理的过程,这个是和数据库没有关系的;但是最为关键的是事务处理必须执行以下过程:一但数据库由于软件或者硬件问题发生故障,重启后,一但有事务没有正常删除,这是事务处理最最核心,最最关键的部分,如果出错怎么办,我们刚才假设总是没有出错,但是作为程序员来说,我们编程的时候是不都要想的全面一些呢,则:7:如果在“事务待命”和“事务结束”状态,则重新从5中结束事务的动作开始执行;8:如果在“事务更新”状态,则重新从4开始更新记录,并向下执行,结果虽然系统崩溃过,但事务仍然能正常提交;
       3-事务处理举例
       有人说你用教条解释了事务处理的流程,不是很明白,对了我本人也是非常讨厌教条式的概念和理论,没有实际生活中来的例子生动形象,好了,那我给大家举得例子,例子如下:
       银行取款的例子,比如:小王拿着一张卡去银行取钱了,他这次的目的是取100元,取款机里面有200元,取钱的流程是:A判断余额-B取钱-C扣除(200-100=100),如果取钱的过程中出错了怎么办,本来已经把100元取走了,还没有扣除,如果在这个时候取款机突然报错,机器死机了,取走100元结果取款机里面还有200元,还是显示200元,如果不作为一个事务来做的话,就会出现这样的情况,银行要赔钱了,所以要作为一个事务来处理,如果发现200元取走了100元取钱的动作已经完成了,当系统重新启动以后,继续执行扣除,最后把你取走的100元扣掉,虽然我们的系统崩溃了,但是系统重新启动以后,继续把没有做完的事务做完ABC则作为一个逻辑单元来存在;在假如甲乙两个人同时取钱的话,就要保证同时某一刻只能有一个人取钱(同时只有一个人做ABC三个动作,为了保证数据的一致性),如果说甲取了100元,但是还没来得及扣除情况下,银行里面显示还是200元,乙要再取的话还能取,实际上这种情况不能再取了,所以我们要保证数据的一致性,保证银行不赔钱,所以我们经常会遇到对数据库处理的时候是不要加锁,讲到这里就明白了,加锁的目的就是让同一时刻对某一个数据只有一个人进行操作,而且作为ABC这三个动作来说呢,把它作为一个事务来处理,要么全部成功,要么全部失败,既然钱取走了,就一定要扣掉,系统虽然崩溃了,重新启动后还要把这个事务作完;
       通过这个银行取钱的例子大家明白了吧;
       4-事务处理的有关事项
       事务处理的关键在于为了防止这样的情况,万一系统崩溃了,数据库再次启动后,仍然保持数据的一致性,逻辑性,也就是事务处理在这个时候发生的,如果说正正常常的,事务处理相对来说它就没有起到作用,就是预防万一出错的情况下,应用中包含的事务应当尽量让他瞬间完成的,也就是说一个事务很快的完成的,否则你说他取钱和扣除的过程,我们上个例子ABC三个动作应该马上立马完成的,如果说他取钱和扣除用上10分钟的话,你想想其他的人没法取钱了,避免在比较忙的时候造成用户进程的互锁,在这种情况下也是为了提高效率,所以作为一个事务来说应该是很快完成的,事务是要加锁的,也就是某一个时刻向下呢一个事务必须执行,其他不能同时访问了,特别是进行更新,我们知道计算机它崩溃的外因是不可预料的,特别是对于银行,用户的保密的数据作好数据的一致性是非常非常重要的,千万不能出任何的差错,所以事务处理是业务系统安全稳定的最后一道防线;
       5-那事务处理有哪些方法呢?
       1:直接写入SQL
       直接写SQL语句,SQL语句本身是可以有事务的,有事务处理的;在存储过程中使用Begin Tran,Commit Tran RollBack Tran。优点:独立于应用程序,拥有运行一个事务的最佳性能(在存储过程写事务处理是性能最好的)。限制:事务处理的上下文仅存在于数据库调用中(事务处理只能在SQL语句中体现,如果在SQL语句之外是无法访问的),数据库代码与数据库系统相关的(因为是SQL语句当中进行的事务处理,不同的数据库写的方法不一样);
       2:通过ADO.NET实现
       我们说在.NET当中呢,通过ADO.NET可以实现事务处理;在ADO.NET中使用Connection和Transacion对象来控制事务,若要执行事务请执行以下操作:调用Connrction对象的BeginTransaction方法标记一个事务的开始(BeginTransaction返回一个Transaction对象),将Transaction对象分配给要执行的Command的Transaction属性(将这个Tranaction对象赋值给Command对象的Transaction属性),调用Transaction对象的Commit方法来完成事务,或调用Rollback来取消事务;优点:简单性,和数据库事务差不多得快,独立于数据库。缺点:不能跨越多个数据库,事务执行在数据库连接层上,所以需要在事务中维护一个数据库连接。
       3:COM+事务(分布式事务)
       在某些特定的情况下,我们要用的;一般的数据库事务控制要求事务里所做的操作必须在同一个数据库内(必须对同一个数据库进行操作),这就存在一个问题,在分布式应用程序中,我们往往要同时操作多个数据库,SQLServer,Oracle,MySQL,你可能要操作两个数据库甚至多个数据库,你也希望这些操作要么同时成功,要么同时失败,也就是他们要做一个事务来处理,这种情况下就要用COM+的分布式事务处理了;

       上面提到的事务处理的核心在于为了防止系统崩溃了,数据库再次启动后,仍然保持数据的一致性,逻辑性;
       不过其实事务还有一点在我们实际的应用中非常重要的一点,比如:有个进销存系统,当采购成功的时候,数据表Order
    (订单表)里插入一条记录,同时OrderInfo(订单明细表)里插入多条这个订单的明细记录,如果成功都成功,如果失败都失败,不可能,有订单的记录,没有订单明细的记录吧,也不可能有订单明细的记录,没有订单的记录吧;
       不过个人认为在目前我们所做的项目中用到COM+事务的情况不多见,因为我们大部分都是操作同一个数据库;

       鉴于有网友提出文章只是讲到纯理论,没有实例,所以特意补上实例以供参考,先说一下我们做事情先明白为什么要这么做,这么做的原因是什么,不这么做会导致什么后果,如果会导致后果那么这个后果发生的概率有多大。而现在很多书本上都是一步步指导你怎样做,怎样开发,却始终未曾提及为什么要这么做;
       好了,我就附上在ADO.NET中实现事务的代码如下:

       strConn是连接字符串,这里就不写明了;

     1  /// <summary>
     2         /// 插入采购单利用事务
     3         /// </summary>
     4         /// <param name="order">采购单头</param>
     5         /// <param name="ls">采购单信息</param>
     6         /// <returns>返回插入的表头id</returns>
     7         public int InsertOrder(OrderData order, List<OrderInfoData> orderinfolist)
     8         {
     9             SqlConnection conn = new SqlConnection(strConn);
    10             conn.Open();
    11             SqlCommand cmd = new SqlCommand("insert into [order](Order_No, PurveyInfo_ID, User_ID, Order_Time, Down, Blank, YiTuiHui, ZaiTu, YiShouHuo) values(@order_no, @purveyinfo_id, @user_id, @order_time, 0, 0, 0, 0, 0)", conn);
    12             SqlTransaction trans;
    13             trans = conn.BeginTransaction();
    14             cmd.Transaction = trans;
    15             List<int> rlist = new List<int>();
    16             try
    17             {
    18                 cmd.Parameters.AddWithValue("@order_no", order.Order_No);
    19                 cmd.Parameters.AddWithValue("@purveyinfo_id", order.PurveyInfo_ID);
    20                 cmd.Parameters.AddWithValue("@user_id", order.User_ID);
    21                 cmd.Parameters.AddWithValue("@order_time", order.Order_Time);
    22                 cmd.ExecuteNonQuery();
    23                 cmd.CommandText = "select @@IDENTITY";
    24                 int i = Convert.ToInt32(cmd.ExecuteScalar());
    25                 foreach (OrderInfoData orderinfo in orderinfolist)
    26                 {
    27                     cmd.CommandText = "insert into orderInfo values(@order_id,@merchandiseinfo_id, @price, @quantity, 0)";
    28                     cmd.Parameters.Clear();
    29                     cmd.Parameters.AddWithValue("@order_id", i);
    30                     cmd.Parameters.AddWithValue("@merchandiseinfo_id", orderinfo.MerchandiseInfo_ID);
    31                     cmd.Parameters.AddWithValue("@price", orderinfo.Price);
    32                     cmd.Parameters.AddWithValue("@quantity", orderinfo.Quantity);
    33                     cmd.ExecuteNonQuery();
    34                 }
    35                 trans.Commit();
    36                 return i;
    37             }
    38             catch (SqlException ex)
    39             {
    40                 trans.Rollback();
    41                 throw ex;
    42             }
    43             finally
    44             {
    45                 conn.Close();
    46             }
    47         }


           以上代码的意思是当采购成功的时候,数据表Order(订单表)里插入一条记录,同时OrderInfo(订单明细表)里插入多条这个订单的明细记录,如果成功都成功,如果失败都失败;不可能,有订单的记录,没有订单明细的记录,也不可能有订单明细的记录,没有订单的记录;
       本文来自CSDN博客,转载请标明出处:
       http://blog.csdn.net/menglin2010/archive/2011/01/19/6151360.aspx

  • 相关阅读:
    【leetcode】319. 灯泡开关
    【leetcode】313. 超级丑数
    【leetcode】316. 去除重复字母
    kpw3 kindle越狱过程总结
    SpringBoot工程application.properties文件不识别问题
    mybatis初始配置及错误说明
    运维相关知识
    面试常见
    Bean method 'jdbcTemplate' not loaded because @ConditionalOnSingleCandidate
    mysql5.7.21安装要点记录
  • 原文地址:https://www.cnblogs.com/menglin2010/p/1938768.html
Copyright © 2020-2023  润新知