有一天gemfield去银行给capucivar的账户上转500元,他开始进行操作,相应的sql语句是这样的:”update zhuanzhang set money=money-500 where username=’gemfield’”,此时系统正常运行,执行了该sql语句之后,gemfield账户上的现金少了500元,然后执行 sql语句”update zhanzhang set money=money+500 where username=’capucivar’”,让capucivar账户上的现金增加500元。但是有时候“天不遂人愿”,当gemfield账户上的现金减少了500元之后,还没来得及给capucivar的账户上添加500元数据库突然就断电了。这样的话,gemfield账户上的现金少了500元,但是capucivar账户上的现金却没有多出500元来。这可怎么办,总不能让这500元就凭空消失吧。所以就引出了“事务处理”。
什么是事务处理?
可以这样说,每一条语句都独立构成一个事务。在上面的事例中,我们希望两个语句在都执行并没有发生异常之后,再提交结果。那么就将这两个语句组成一个事务。
接下来要将上面的例子用事务处理的方法做出来,如何写呢?需要注意的是capucivar以前写的Db.cs类即数据处理类,此处与以前所写的不同。下面就来写这个数据处理的类Db.cs:
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.OleDb;
using System.Collections;
namespace Db//命名空间
{
public class DbConn
{
OleDbConnection conn = null;//声明连接数据库对象
public DbConn()//构造方法
{
if (conn == null)
{
conn = new OleDbConnection();//实例化对象
conn.ConnectionString = "provider=sqloledb.1;data source=.;initial catalog=capucivar;user id=sa;pwd=";//连接数据库
}
if (conn.State == ConnectionState.Closed)
{
conn.Open();//打开连接
}
}
public DataTable QuerySql(string sql)
{
DataTable dt = new DataTable();//实例化一个DataTable对象
OleDbDataAdapter da = new OleDbDataAdapter(sql, conn);//实例化一个适配器对象
da.Fill(dt);//将数据填充到DataTable中
conn_close();//调用conn_close()方法关闭连接
return dt;//返回结果集
}
public int Update(ArrayList al)
{
OleDbTransaction tran = null;//OleDbTransaction表示要在数据源执行的SQL事物
tran = conn.BeginTransaction();//BeginTransaction()表示开始数据库事务
OleDbCommand oc = new OleDbCommand();//OleDbCommand类表示要对数据源执行的 SQL 语句或存储过程。
oc.Connection = conn;//指定数据库连接
oc.Transaction = tran;//指定开始的事务
int x = -1;
try
{
foreach (string sql in al)//用foreach遍历ArrayList,ArrayList里的每一个元素都是string类型的sql语句
{
oc.CommandText = sql;//设置要对数据源执行的SQL命令文本
x = oc.ExecuteNonQuery();//执行SQL并返回所受影响行数
if(x<=0)//如果x<=0就说明该Sql语句没有成功执行,那么就让它回滚,回滚至没有更新数据之前
{ tran.Rollback();//让事物回滚 }
}
tran.Commit();//提交事物
}
catch//如果发生异常了,就将事务回滚
{
x = -1;
tran.Rollback();//让事物回滚
}
conn_close();//调用conn_close()方法关闭数据库连接
return x;//返回受影响的行数用以判断操作是否成功
}
public void conn_close()
{
if (conn.State == ConnectionState.Open)
{
conn.Close();//关闭连接
}
} } }
上面的类写好了,下面来做图形用户界面:
在写图形用户界面之前,要提到“C#的三层模式”:
三层模式:
1、UI:显示层也就是图形用户界面(user interface),它是一个windows窗体应用程序
2、BLL:业务逻辑层(business logic layer)。它是一个类库,在该层里经常是些sql语句的和封装字段的。封装字段的代码经常写在一个VO文件夹下 VO是value object的所写。
3、DAL:数据访问层(data access layer)。同样是类库。该层就是连接数据库,和执行sql语句的。
使用三层模式要注意:这三层是毫无联系的三个项目,如何让它们联系起来?首先重新生成DAL层--à将DAL层添加引用到BLL层--àBLL层重新生成--à将BLL层添加引用到UI层,这样就会将三个毫无联系的三个项目通过添加引用联系起来。
三层模式也就这些了,下面该来写上面例子的BLL层了,BLL层下有一个文件夹叫VO,在该文件夹下写一个类:
using System;
using System.Collections.Generic;
using System.Text;
namespace Bll.VO//命名空间
{
public class Person
{
//下面是将字段封装通过get和set来取值或赋值
private string yourname = string.Empty;//自己的名字
public string Yourname
{
get { return yourname; }
set { yourname = value; }
}
private string yourhao = string.Empty//自己的帐号;
public string Yourhao
{
get { return yourhao; }
set { yourhao = value; }
}
private string hishao = string.Empty;//对方的帐号
public string Hishao
{
get { return hishao; }
set { hishao = value; }
}
private int money = -1;//一个用户的现金
public int Money
{
get { return money; }
set { money = value; }
} } }
封装成一个类之后,来写业务逻辑层:
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Data;
namespace Bll//命名空间
{
public class Sql
{
public bool exeSql(VO.Person ps)
{
string sql1 = string.Format("update zhuanzhang set money=money-{0} where zhanghao='{1}'",ps.Money,ps.Yourhao);
string sql2 = string.Format("update zhuanzhang set money=money+{0} where zhanghao='{1}'", ps.Money, ps.Hishao);//拼写Sql语句
ArrayList al = new ArrayList();//ArrayList对象al
al.Add(sql1);
al.Add(sql2)//为ArrayList添加值;
int x = new Db.DbConn().Update(al);//执行Db.DbConn().Update()放法之后将返回的受影响行数赋给x
if (x > 0)//判断x,如果x大于0那么就执行成功,返回true,否则的话返回false
return true;
return false;
}
//下面是查询所有信息,将信息显示出来,以方便观察执行是否成功
public DataTable select()
{
string sql = "select username,zhanghao,money from zhuanzhang";//拼写sql语句
return new Db.DbConn().QuerySql(sql);//返回查询结果
}
}
}
最后就剩下UI层了:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace zhuanzhang
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//下面是点击按钮后发生
private void submit_but_Click(object sender, EventArgs e)
{
//首先分别得到文本框里的值
string yName = name_text.Text;
string yNum = zhanghao_text.Text;
string hNum = hishao_text.Text;
int money = Convert.ToInt32(money_text.Text);
//将上面的字段通过Bll.VO.Person进行封装
Bll.VO.Person ps = new Bll.VO.Person();
ps.Yourname = yName;
ps.Yourhao = yNum;
ps.Hishao = hNum;
ps.Money = money;
bool b = new Bll.Sql().exeSql(ps);//通过返回值判断转账是否成功
if (b)
{
MessageBox.Show("转账成功!");
}
else
{
MessageBox.Show("转账失败!");
}
refurbish();//调用refurbish()方法将下面表格中的数据从数据库中重新查找一次再显示出来
}
private void Form1_Load(object sender, EventArgs e)
{
refurbish();
}
public void refurbish()
{
DataTable dt = new Bll.Sql().select();//得到结果集
this.dataGridView1.DataSource = dt;//将dataGridView1的数据源指定为dt
}
}
}
事务处理使用“三层模式”写完了,可以来看看结果:
先输入一个错误的对方帐号,显示“转账失败”,点击“确定”之后,可以看到数据库中的数据并没有改变。
输入正确的:它显示“转账成功”的对话框,点击“确定”之后,可以发现“现金”这一列中的现金数已经改变了。
上面的结果正是我们想要的。
对于上面capucivar提到的“三层模式”,建议大家以后经常使用。因为使用三层模式会增强代码的逻辑性,并且使你的思路很清楚。也许刚开始不会觉着这些好处,那是因为手生,练习的多了,就会非常熟练的掌握它了。