听棠.NET

用积极乐观的心态,面对压力
posts - 307, comments - 10812, trackbacks - 112, articles - 5
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

公告

SmartPersistenceLayer3.1讲解(10)---高级事务处理篇

Posted on 2005-05-25 17:53 听棠.NET 阅读(...) 评论(...) 编辑 收藏
         SmartPersistenceLayer 3.1(10) ---高级事务处理篇

SmartPersistenceLayer对事务处理进行了升级了,实现了实时事务功能,这可以解决以往处理的几种事务情况,将在下面详细讲解。
  SmartPersistenceLayer
的新版本3.1.0.5才开始支持此实时事务功能,因此对于以前的版本,只要下载新版本后,直接覆盖原先的PersistenceLayer.Dll即可。

提出问题
 
3.1.0.5之前,SPL 的事务处理是采用“Add<事务操作>后,一次性提交,也即:
 
假设studentdcuc分别为已经实例化的EntityObjectDeleteCriteriaUpdateCriteria  

Transaction t=new Transaction();
  t.AddSaveObject(student):           
//要新增或修改的实体 
  t.AddDeleteObject(student);       //要删除的实体 
  t.AddDeleteCriteria(dc);          //添加一个DeleteCriteria 
  t.AddUpdateCriteria(uc);           //添加一个UpdateCriteria 
  t.AddSqlString(sqlString,dbName);       //添加一个SQL执行语句 
  t.Process();                          //执行此事务 

 从上面的方式就可以看出,先是把要进行事务处理的<操作>加到事务中,然后使用Process一次性事务提交的。关于原事务处理的方法,请参考:SmartPersistenceLayer 2.0(5) ---事务处理篇 
  这要的事务处理可以满足大部分的情况,但也因此遇到了一些特殊情况:
   1、  后续事务对象要用到前面的<事务操作>
这种情况,最典型的例子是:
订单主档表的ID采用的是自动增长,而订单明细表要引用主档表的ID。在这种情况下,采用上面的事务处理时,由于没能及时提交<事务操作>,而导致订单明细对象无法取到主档添加进数据库时自动生成的ID值。
  PS:其实我对采用自动增长主键是非常不提倡的,具体的原因可以参考我的文章 数据库主键设计之思考 
  
2、  <事务操作>中对同一对象的多次调用
       这种情况,最典型的例子是:
       在入库单进行入库时,入库单的明细里可能会存在同一物料入库,程序员肯定会对明细进行循环:
       For(物料明细条数)
       {
                从数据库中取得物料的库存对象INVEntity; 
            
    INVEntity.Quantity +=
本明细数量;
               
把此库存对象放到事务中;
        }
    因此对于同一物料库存,将会多次被实例化,由于事务没有及时提交,导致后一次取到的同一对象没有包含上一次的数量更新,结果是,同一物料的库存只进行了最后一次数量的累加。
    对于上面两种情况的发生,根源就是没有<事务操作>进行实时的提交,SPL3.1.0.5在原有事务机制兼容的情况下,扩展了新的“实时提交”事务处理机制。 

分析问题

     其实上面的问题都是由于没有及时对<事务操作>进行提交,因此,只要在SPL中,当有新的<事务操作>时,让事务及时提交到数据库,这样在下面的事务处理时就可以取到上次事务的处理结果了。有的朋友说:这不就是ADO.NET的事务机制吗?为何还要封装呢,直接把事务公开不就行了吗?

      不是的,因为SPL是考虑多异构数据库的,也就是事务对象可以不是同一数据库的,更者可能是异构数据库的,比如一个是SQL Server的,另一个是Oracle的。因此SPL要完成这些异构数据库的事务提交,不能直接公开ADO.NET的事务实例。

      由于事务是实时提交,那么比如获取(select)实例的操作,也需要添加到事务中,因为当第一个<事务操作>执行后,那表就会被事务立即锁定,后续<事务操作>要进行此表的查询时,此查询也必须在此事务中执行,当事务提交或者回滚后,才可以进行其他的数据操作。因此SPL事务中添加了获取对象的<事务操作>

 

解决问题

SPL3.0.1.5版本为此扩展了一种新的事务机制“实时提交”,原来的事务处理方式完全兼容,这不仅方便SPL的升级更新,原来的Process的事务处理也是有它的优点的,因此在对于没有“及时提交”的情况下,完全可以使用旧的处理机制。
新机制的使用范例:

Transaction t=new Transaction();  //实例化事务
 t.DoDeleteCriteria(dc) ;//执行删除标准DeleteCriteria
 t.DoSaveObject(student);  //执行保存对象操作
 t.Commit();            //提交或者t.RollBack()回滚

我把原先的”Add”开头都用”Do”来替换了,这体现了Do的实时特性,由于在”Do”操作时会自动打开自己数据库的连接(因为支持多数据库,所以每个被添加的<事务操作>都允许有自己的数据库源),因此事务的提交与回滚必须要通过手动完成,而且必须得完成,比如:t.Commit()t.RollBack()。如果没有提交或回滚,那么很有可能会锁定<事务操作>的表,这就可能会导致其他此表的处理超过。

运用现在的这种机制可以很方便的解决前面提到的两个典型问题:

 1订单主档表的ID采用的是自动增长   

 A:首先我们创建主档对象并赋上属性的值,如SaleOrder1

B:在事务中执行此操作:t.DoSaveObject(SaleOrder1),因此SPL在处理自动增长列时在保存到数据库后立即返回“生成的值”赋给对象的ID,也就是在此操作时,自动生成的ID值已经被自动赋到对象SaleOrder1的ID属性了。

C:用FOR循环New出明细对象OrderDetail1,OrderDetail2….

D:对这些OrderDetail1,OrderDetail2的SOID(对应订单订档ID)属性赋上

    OrderDetail1.SOID
=SaleOrder1.ID;

E:把OrderDetail1等添加到事务中执行t.DoSaveObject(OrderDetail1)…

F:把事务进行提交t.Commit();

这样就一切OK了。

2、  同一物料库存的累加

只介绍物料循环里的操作即可:

FOR(物料明细循环)

{

  A:new一个库存对象Inv1,把物料值赋上,在事务中获取:t.DoRetrieveObject(Inv1);

  B:这样就获取了实时的库存值,Inv1.Quantity 
+=本次数量;

  C:把此库存对象添加到事务执行:t.DoSaveObject(Inv1);

}

  上面假设的Inv对象的主键就是物料ID,在使用DoRetrieveCrteria时,是通过对象的主键去Retrieve对象的,因此假设库存对象的主键不是物料ID,则需要通过RetrieveCriteria进行多条件查找到对象,然后跟上面的操作一样了。

FOR(物料明细循环)

{

  A:创建一个多条件的RetrieveCriteria对象,在事务中DataTable dt
=t.DoRetrieveCriteria(rc);

  B:new一个库存对象Inv1,把dt中的主键值赋给Inv1,在事务中获取:t.DoRetrieveObject(Inv1);

  C:这样就获取了实时的库存值,Inv1.Quantity 
+=本次数量;

  D:把此库存对象添加到事务执行:t.DoSaveObject(Inv1);

}

 上面的方法看似复杂,其实道理是很简单,RetrieveCritera是非主键获取的一种手段。

 参考

   上面介绍了几种简单的Do方法,下面总结所有相关的Do方法以供参考,使用的方法是很相似的。 

 public void DoDeleteCriteria(DeleteCriteria delete)  //执行删除标准
   public void DoDeleteCriteria(DeleteCriteria delete,bool isForceCommit) //指定是否强制执行删除标准
   对于isForceCommit是否强制执行,默认值为Transaction.IsForceCommit值false:
       
true:强制执行,即就算遇到更新为零的操作,也不会抛出异常
    flase:非强制执行,如果遇到更新为零,则抛出异常PLException,类型为DirthEntity,表示存在并发错误
   
public void DoDeleteObject(EntityObject obj)  //执行实体删除
   public void DoDeleteObject(EntityObject obj,bool isForceCommit) //执行实体删除(isForceCommit同前)
   public void DoSaveObject(EntityObject obj) //执行实体保存
   public void DoSaveObject(EntityObject obj,bool isForceCommit) //执行实体保存(isForceCommit同前)
   public void DoUpdateCriteria(UpdateCriteria update) //执行更新标准
   public void DoUpdateCriteria(UpdateCriteria update,bool isForceCommit) //执行更新标准(isForceCommit同前)
   public void DoSqlNonQueryString(string strSql,string dbName) //执行指定SQL语句
   public void DoSqlNonQueryString(string strSql,string dbName,bool isForceCommit) //同上(isForceCommit同前)
   public DataTable DoRetrieveCriteria(RetrieveCriteria retrieve) //执行获取标准,返加DataTable
   public void DoRetrieveObject(EntityObject obj) //执行对象获取,对象会自动被赋上值
   public DataTable DoSqlQueryString(string strSql,string dbName) //执行查询SQL,返回DataTable

SPL3.1.0.5新版下载,请参考:SPL3.1.0.5升级-加强了事务的“实时提交”功能