事务处理过程
事务的处理过程大体分为几个步骤:1.事务的组装;2.相应数据集合的加锁;3.通知数据集合备份;4.顺序执行每一个原子操作;5.为每一个原子操作的成功位做标示;6存储结果返回到结果池中
事务的组装在客户端需要自行完成,因为每个客户端提交的任务不相同,服务器都做统一的处理。事务组装的代码如下:
Boolean success = false;
#region 组装成一个事务,并返回结果
TransactionEntity transactionEntity = new TransactionEntity();//准备提交的参数
Hashtable ResultHashTable = new Hashtable();//准备返回的参数
transactionEntity.TransactionID.Add("14");//编号21:预订酒店
transactionEntity.TransactionParam.Add(new object[] { locationString, hotelRemoteService });//参数
transactionEntity.TransactionID.Add("19");//编号19:添加预订信息
transactionEntity.TransactionParam.Add(new object[] { CustNameString, 2, reservationRemoteService });//参数
queueTransactionService.intTransactionID += 1;//事务编号
int intTransactionID = queueTransactionService.intTransactionID;
queueTransactionService.globalControlService = globalControlService;//分配新的事务编号
queueTransactionService.AddEntity(intTransactionID.ToString(), transactionEntity);//把需要处理的事务装在hashtable队列里面,等待程序自动处理
while (true)//循环检查结果队列,查找自己的事务结果
{
if (queueTransactionService.TransactionResult.ContainsKey(intTransactionID.ToString()))//在结果列表中查找到了自己需要的数据,跳出无限等待
break;
}
ResultHashTable = (Hashtable)queueTransactionService.TransactionResult[intTransactionID.ToString()];//接受从服务器来的结果
success = (Boolean)ResultHashTable["14"];
if (success == true)
{
MessageBox.Show("酒店预定成功,地点:" + locationString);
}
else
{
MessageBox.Show("酒店预定失败!");
}
#endregion
事务的组装首先需要实例化事务实体类(TransactionEntiy)和结果接受类(Hashtable)。下一步需要填充TransactionEntiy,整个TransactionEntiy只有三个参数,两个需在这里填充,只有每个操作是否成功需要在客户端填充并且处理检测。添加好事务的序号后,客户端需要做的就是把事务丢进事务池(Transaction Pool)中等待程序区运行。这时候客户端需要族的就是去结果池(Result Pool)中等待并且寻找自己的结果。示例图如下:
事务池和结果池
在执行这个事务之前,需要对其所涉及的所有数据集合进行加锁,即修改其hasControl标志段的值。在对其加锁的同时,也通知该数据集合进行备份,以便事务撤销的时候不能恢复到原来的状态。代码片段如下:
/// <summary>
/// 执行一个事务,把结果存储在Hashtable中,此过程会自动加锁,对数据集合进行备份
/// </summary>
/// <param name="TansactionHashTable">需要执行的事务,包括每一个操作的编号和所需要的参数</param>
/// <returns>返回结果,按照每一个操作得到的结果存储在Hashtable里面</returns>
public Hashtable ProcessTransaction()
{
for (int i = 0; i < TransactionEntity.TransactionID.Count; i++)
{
//对所需要的数据集合进行加锁
LockByID(TransactionEntity.TransactionID[i]);
//开始这个事务,通知相关数据集合服务器做相应的处理
BeginByID(TransactionEntity.TransactionID[i], xid);
}
循环遍历事务中的每一个操作,找到其编号,根据编号执行相应的操作,并把结果放在相应的集合中。
Hashtable TransactionResult = new Hashtable();
for (int i = 0; i < TransactionEntity.TransactionID.Count; i++)
{
try
{
//把方法编号和方法返回结果存储到hashtable里面,用于返回给客户端
object ObjResultTmp = GetDataByID(TransactionEntity.TransactionID[i], TransactionEntity.TransactionParam[i]);
if (ObjResultTmp == null)//如果该返回值为空,则说明没有取到数据或者操作失败,则执行失败
TransactionEntity.Success.Add(false);
else
TransactionEntity.Success.Add(true);
TransactionResult.Add(TransactionEntity.TransactionID[i], ObjResultTmp);
}
catch (Exception e)
{
TransactionEntity.Success.Add(false);
}
}
一次事务的执行并不一定能成功,如果有一个操作失败了,那么就意味着整个事务都必须要回滚到初始状态,所以在每一个操作执行完毕后,都会为其标志位进行赋值。标志为失败的情况主要分为几个情况:1.返回结果为空,说明执行失败;2.执行存在错误跑到Catch区域了。这两种情况都视为操作失败。
int flag = 1;//标记为,1为全部成功,0为有失败
//通过循环来查看是否有那个执行没有成功
for (int i = 0; i < TransactionEntity.Success.Count; i++)
{
if (TransactionEntity.Success[i].Equals(false))//如果有失败的操作,那么将整个事务回滚,具体操作为还原相应的数据集合
{
flag = 0;//有失败
break;
}
}
无论此事务成功或者失败,都需要给数据集合一个交代。事务执行的最后,需要做的就是判断此事务是需要提交还是回滚。
//判断是否进行回滚
if (flag == 0)//到此,失败,回滚
for (int i = 0; i < TransactionEntity.TransactionID.Count; i++)
{
RollBackByID(TransactionEntity.TransactionID[i], xid);
}
else
for (int i = 0; i < TransactionEntity.TransactionID.Count; i++)
{
//到此,说明整个事务执行成功
CommiteByID(TransactionEntity.TransactionID[i], xid);
}
//执行结束,解锁
for (int i = 0; i < TransactionEntity.TransactionID.Count; i++)
{
UnlockByID(TransactionEntity.TransactionID[i]);
}
最后无论回滚还是执行,都需要释放掉所占用的资源,保证其他事务能够正常运行。