C#回顾学习笔记三十九:事务

1)事务是什么?
事务是保证多个操作全部成功时才认为是一次有效操作,当有一个操作失败时就会认为全部操作无效,并且回到执行操作之前的状态只有数据改变时(增加、修改、删除)时才会引发事务,查询不会引发事务。如果在写入一个记录时出现失败,则事务会让其他已经写入的数据回滚,让数据恢复到修改前的状态。事务的作用就是:要么让任务都执行成功,要么让任务都执行失败。事务的四大特点有:原子性、一致性、隔离性、持久性。

2)为什么使用事务?
假设有一张用户账号余额表,用户A向用户B转账100,则数据库代码可以这样执行:
update GongZiBiao set money=money-100 where name='用户A'
update GongZiBiao set money=money+100 where name='用户B'
然而这样做有个缺点:假设执行了第一条语句后出现什么异常而导致第二条语句无法执行,则意味着用户A扣掉了100,而用户B没有得到该有的100元。因此引用事务来解决这种问题,有了事务后,若执行一连串数据库语句时出现错误,则前面的语句执行都无效,必须让所有语句都没有错误,才算真正的执行成功。事务保证了这种重要的数据处理变得合理与安全,对金融和抢票系统来说很重要。

3)如何使用ADO.NET操作事务?
这次的练习就使用SQLserver和VS2013的一个控制台应用程序来做演示。
第1步,新建控制台应用程序,在App.config中配置数据库连接字符串。在<configuration>标签里配置如下所示的代码:
<connectionStrings>
    <add name="testConn" connectionString="server=.;database=IsNothing;uid=sa;pwd=145124"/>
  </connectionStrings>
其中name属性的值可以任意设置。server表示数据库地址,小数点代表就是本地。database表示数据库名,这里是IsNothing。uid是数据库用户名。pwd是数据库密码。
第2步,新建数据库,名字就是IsNothing。新建表UserInfo,表字段设计和表内数据如下图:

第3步:现在让Id为1的用户把年龄转让1岁给Id为2的用户,也就是Id为1的用户年龄-1,Id为2的用户年龄+1。必须让两次数据操作都执行成功,如果其中任何一个失败,则操作无效。主函数代码如下:

class Program
    {
        static void Main(string[] args)
        {
            //1.读取数据库连接字符串,"testConn"与App.config中的name属性值保持一致
            string connStr = ConfigurationManager.ConnectionStrings["testConn"].ConnectionString;


            //2.声明连接数据库的对象,并打开数据库连接
            using (SqlConnection conn = new SqlConnection(connStr))
            {
                conn.Open();
                using (SqlTransaction tx = conn.BeginTransaction())//3.声明事务
                {
                    using (SqlCommand cmd = new SqlCommand())
                    {
                        cmd.Connection = conn;
                        cmd.Transaction = tx;
                        try
                        {
                            cmd.CommandText = "update UserInfo set Age=Age-1 where Id=1";
                            int row1 = cmd.ExecuteNonQuery();
                            cmd.CommandText = "update UserInfo set Age=Age+1 where Id=2";
                            int row2 = cmd.ExecuteNonQuery();
                            tx.Commit();//注意这里,如果上面两句sql语句都执行成功,这里才完成提交。
                            Console.WriteLine("执行语句1影响的行数是{0}", row1);
                            Console.WriteLine("执行语句2影响的行数是{0}", row2);
                        }
                        catch (Exception ex)
                        {
                            tx.Rollback();//try内部代码发生异常,就执行回滚让数据回到原始状态
                            Console.WriteLine("发生异常,异常信息:", ex.ToString());
                    }
                }
            }
            Console.ReadKey();
        }
    }

解释:在上面代码中,最重要的代码是注释3开始,从这里声明事务并使得里面所有代码都属于事务管理范围,这样只要数据操作中途出现异常,则事务让数据回滚。
第4步,运行程序。如果代码没有问题,则会出现如下图所示的情况:

第5步,以上操作似乎还是看不出事务的好处是什么,因为就算不使用事务,这样的数据操作似乎也能执行成功。那么,为了验证事务的可靠性,先人为地制造一个异常。在如下图所示的位置添加一句代码,这句代码绝对会抛出异常。运行程序,然后观察看数据库的值是否改变。如果没问题,则此时数据库的值依然跟上面第4步的执行结果一样。因为在抛出异常时,事务就让数据回滚到修改前的状态了。

第6步,现在把上面代码做一下修改,去掉跟事务相关的代码然后再运行试一试:

class Program
    {
        static void Main(string[] args)
        {
            string connStr = ConfigurationManager.ConnectionStrings["testConn"].ConnectionString;


            using (SqlConnection conn = new SqlConnection(connStr))
            {
                conn.Open();
                using (SqlCommand cmd = new SqlCommand())
                {
                    cmd.Connection = conn;
                    try
                    {
                        cmd.CommandText = "update UserInfo set Age=Age-1 where Id=1";
                        int row1 = cmd.ExecuteNonQuery();
                        int exp = Convert.ToInt32("abc");//注意这里,故意制造一个异常
                        cmd.CommandText = "update UserInfo set Age=Age+1 where Id=2";
                        int row2 = cmd.ExecuteNonQuery();
                        Console.WriteLine("执行语句1影响的行数是{0}", row1);
                        Console.WriteLine("执行语句2影响的行数是{0}", row2);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("发生异常,异常信息:", ex.ToString());
                    }
                }
            }
            Console.ReadKey();
        }
    }

上面的代码会在执行成功第一个sql语句后抛出异常,这样就造成第二个sql语句无法执行。所以观察数据库可以发现,第一个用户的年龄减了1岁,而第二个用户的年龄依然没变。这就是没有事务时的坏处,如果是金融转账,没有事务控制的话就会造成亏损。

4)如何使用SQLserver处理事务

SQLserver里自带的语句也可以完成事务处理,就拿上面的练习来说,在SQLserver里可以这么做:

begin try
begin transaction--设置反悔点,开启事务(出错时可以回到这里),可以简写为tran
delete from T_Persons where Id=8
delete from T_Classes
commit transaction--提交事务,不反悔(没问题,直接走的意思)
end try
begin catch
rollback transaction--回滚事务,如果出错就回到反悔点(反悔了要回去起点的意思)
end catch

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值