什么是事务?
作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。
为什么要用事务?
确保非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。
什么情况下可以使用事务?
满足ACID属性
原子性:
一个事务是一个不可分割的工作单位,对于其数据修改,要么全部执行,要么全部不执行。
一致性:
事务在完成时,必须使所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于对事务的修改以保持所有数据的完整性。
隔离性:
一个事务的执行不能被其它事务干扰。即一个事务内部的操作及使用的数据对并发的其它事务是隔离的,并发执行的各个事务之间不能相互干扰。
持久性
也称为永久性,指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其有任何影响。
事务并发引发的后果
- 丢失更新
两个事务同时更新一行数据,最后一个事务的更新会覆盖掉第一个事务的更新,从而导致第一个事务更新的数据丢失,这是由于没有加锁造成的。
第一类丢失更新:撤销一个事务时,把其它事务已提交的更新数据覆盖
第二类丢失更新:是不可重复读的特殊情况。如果两个事务都读取同一行,然后两个都进行写操作,并提交,第一事务所做的改变就会丢失。
- 脏读
一个事务读取到另一个事务未提交的更新数据;当事务读取未提交的数据时,就会发生这种情况。
- 幻读也叫虚读:
一个事务在执行过程中读取了另一个事务已提交的插入数据;即在第一个事务开始时读取到一批数据,但此后另一个事务有插入了新数据并提交,此时第一个事务又读取这批数据但发现多了一条,就好像发生了幻觉一样。
- 不可重复读:
一个事务两次读取同一行的数据,结果得到不同状态的结果,中间正好另一个事务更新了该数据,两次结果相异,不可被信任。
事务在订餐系统中的应用:
CardBll
/// <summary>
/// 充值 添加事务,实现填写充值记录和更新余额两者同时成功才写入数据库
/// </summary>
/// <param name="cardinfo"></param>
/// <returns></returns>
public bool rechargebycardided(CardAddLog cardinfo)
{
DBTransactionBLL DbTran = new DBTransactionBLL();
//获得连接
SqlConnection conn = DbTran.GetConnection();
//开启事务
SqlTransaction trans = DbTran.GetTransaction(conn);
try
{
//填写充值记录
if (carddal.rechargebycardided(cardinfo, conn, trans))
{
}
else
{
return false;
}
//更新余额
if (carddal.updateCashByCardIded(cardinfo, conn, trans))
{
}
else
{
return false;
}
//提交事务
DbTran.Commit(trans);
return true;
}
catch (Exception e)
{
//事务回滚
DbTran.Rollback(trans);
return false;
}
finally
{
//关闭连接
DbTran.Close(conn);
}
}
DBTransactionBLL
#region 获取事务 王朋波 2016年2月12日 10:38:34
/// <summary>
/// 获取事务
/// </summary>
/// <param name="conn"></param>
/// <returns></returns>
public SqlTransaction GetTransaction(SqlConnection conn)
{
return conn.BeginTransaction();
}
#endregion
#region 提交事务 王朋波 2016年2月12日 14:04:31
/// <summary>
/// 提交事务
/// </summary>
public void Commit(SqlTransaction sqlTransaction)
{
sqlTransaction.Commit();
}
#endregion
#region 回滚事务 王朋波 2016年2月12日 14:04:24
/// <summary>
/// 回滚事务
/// </summary>
public void Rollback(SqlTransaction sqlTransaction)
{
sqlTransaction.Rollback();
}
#endregion
cardDal
/// <summary>
/// 注册卡
/// </summary>
/// <param name="card">卡信息</param>
/// <returns></returns>
/// 修改原因:添加事务机制
/// 修改人:王朋波
/// 修改日期 2016年2月12日 15:24:08
public bool InsertCard(Card card, SqlConnection conn, SqlTransaction trans)
{
bool flag = false;
string sql = "update t_Card set password=@password,ownerName=@ownerName,cash=@cash,cardTypeId=@cardTypeId,status=@status,groupId=@groupId,owerTel=@owerTel,owerEmail=@owerEmail,cardLevel=@cardLevel where mainCardId=@mainCardId";
SqlParameter[] paras = new SqlParameter[]{
new SqlParameter ("@mainCardId",card.mainCardId),
new SqlParameter ("@cardId",card.cardId ),
new SqlParameter ("@password",card.password),
new SqlParameter("@ownerName",card.ownerName),
new SqlParameter("@cash",card.cash ),
new SqlParameter("@cardTypeId",card.cardTypeId),
new SqlParameter("@status",card.status),
new SqlParameter("@groupId",card.groupId),
new SqlParameter("@owerTel",card.owerTel),
new SqlParameter("@owerEmail",card.owerEmail),
new SqlParameter("@cardLevel",card.cardLevel),
};
int res = SQLHelper.ExecuteSqlRow(sql, conn, trans, paras);
//int res = helper.ExecuteNonQuery(sql, paras, CommandType.Text);
if (res > 0)
{
flag = true;
}
return flag;
}
DBTransactionDAL:
//用于数据库事务管理
public class DBTransactionDAL
{
private SQLHelper helper = null;
#region 构造方法 王朋波 2016年2月12日 10:32:12
/// <summary>
/// 构造方法
/// </summary>
public DBTransactionDAL()
{
helper = new SQLHelper();
}
#endregion
#region 获取数据库连接 王朋波 2016年2月12日 14:07:06
/// <summary>
/// 获取数据库连接
/// </summary>
/// <returns></returns>
public SqlConnection GetConnection()
{
return helper.GetConn ();
}
#endregion
}