SmartPersistenceLayer对事务处理进行了升级了,实现了“实时事务”功能,这可以解决以往处理的几种事务情况,将在下面详细讲解。
SmartPersistenceLayer的新版本
提出问题
在
假设student、dc、uc分别为已经实例化的EntityObject、DeleteCriteria、UpdateCriteria
t.AddSaveObject(student): //要新增或修改的实体
t.AddDeleteObject(student); //要删除的实体
t.AddDeleteCriteria(dc); //添加一个DeleteCriteria
t.AddUpdateCriteria(uc); //添加一个UpdateCriteria
t.AddSqlString(sqlString,dbName); //添加一个SQL执行语句
t.Process(); //执行此事务
这要的事务处理可以满足大部分的情况,但也因此遇到了一些特殊情况:
1、 后续事务对象要用到前面的<事务操作>
这种情况,最典型的例子是:
订单主档表的ID采用的是自动增长,而订单明细表要引用主档表的ID。在这种情况下,采用上面的事务处理时,由于没能及时提交<事务操作>,而导致订单明细对象无法取到主档添加进数据库时自动生成的ID值。
PS:其实我对采用自动增长主键是非常不提倡的,具体的原因可以参考我的文章 :数据库主键设计之思考
2、 <事务操作>中对同一对象的多次调用
这种情况,最典型的例子是:
在入库单进行入库时,入库单的明细里可能会存在同一物料入库,程序员肯定会对明细进行循环:
For(物料明细条数)
{
从数据库中取得物料的库存对象INVEntity;
INVEntity.Quantity +=本明细数量;
把此库存对象放到事务中;
}
因此对于同一物料库存,将会多次被实例化,由于事务没有及时提交,导致后一次取到的同一对象没有包含上一次的数量更新,结果是,同一物料的库存只进行了最后一次数量的累加。
对于上面两种情况的发生,根源就是没有<事务操作>进行实时的提交,SPL
分析问题
其实上面的问题都是由于没有及时对<事务操作>进行提交,因此,只要在SPL中,当有新的<事务操作>时,让事务及时提交到数据库,这样在下面的事务处理时就可以取到上次事务的处理结果了。有的朋友说:这不就是ADO.NET的事务机制吗?为何还要封装呢,直接把事务公开不就行了吗?
不是的,因为SPL是考虑多异构数据库的,也就是事务对象可以不是同一数据库的,更者可能是异构数据库的,比如一个是SQL Server的,另一个是Oracle的。因此SPL要完成这些异构数据库的事务提交,不能直接公开ADO.NET的事务实例。
由于事务是实时提交,那么比如获取(select)实例的操作,也需要添加到事务中,因为当第一个<事务操作>执行后,那表就会被事务立即锁定,后续<事务操作>要进行此表的查询时,此查询也必须在此事务中执行,当事务提交或者回滚后,才可以进行其他的数据操作。因此SPL事务中添加了获取对象的<事务操作>。
解决问题
SPL
新机制的使用范例:
t.DoDeleteCriteria(dc) ;//执行删除标准DeleteCriteria
t.DoSaveObject(student); //执行保存对象操作
t.Commit(); //提交或者t.RollBack()回滚
我把原先的”Add”开头都用”Do”来替换了,这体现了Do的实时特性,由于在”Do”操作时会自动打开”自己数据库” 的连接(因为支持多数据库,所以每个被添加的<事务操作>都允许有自己的数据库源),因此事务的提交与回滚必须要通过手动完成,而且必须得完成,比如:t.Commit()或t.RollBack()。如果没有提交或回滚,那么很有可能会锁定<事务操作>的表,这就可能会导致其他此表的处理超过。
运用现在的这种机制可以很方便的解决前面提到的两个典型问题:
1、订单主档表的ID采用的是自动增长
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、 同一物料库存的累加
只介绍物料循环里的操作即可:
{
A:new一个库存对象Inv1,把物料值赋上,在事务中获取:t.DoRetrieveObject(Inv1);
B:这样就获取了实时的库存值,Inv1.Quantity +=本次数量;
C:把此库存对象添加到事务执行:t.DoSaveObject(Inv1);
}
上面假设的Inv对象的主键就是物料ID,在使用DoRetrieveCrteria时,是通过对象的主键去Retrieve对象的,因此假设库存对象的主键不是物料ID,则需要通过RetrieveCriteria进行多条件查找到对象,然后跟上面的操作一样了。
{
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,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升级-加强了事务的“实时提交”功能