SmartPersistenceLayer
提出问题
数据并发问题不是新问题了。当两个用户都读取了同一条记录后,A用户进行了更新,B用户在更新时,有可能会覆盖A用户的修改,这就是典型的并发问题。
这里有一篇MS的关于并发问题的文章:介绍 ADO.NET 中的数据并发[转]
我个人感觉最好的解决并发问题的方式是采用“时间戳TimeStamp”,这是由Sql Server数据库自行维护的,可以减少程序的管控,而且效率也相当高。
采用时间戳的简单步骤:
在表中字义一个字段类型为TimeStamp即可,如字段TS
然后在更新与删除时进行TS的比较:
Update tablename set …. Where key=@key and ts=@ts
Delete from tablename Where key=@key and ts=@ts
然后根据Command执行返回的影响条数判断是否成功。
采用TimeStamp的好处就是此TimeStamp的值不需要程序维护,在Insert时会自动插入新值,在Update时,也会自动更新,因此从这一点上,TimeStamp是非常可取的。
但这个时间戳字段不是所有的数据库都有的。比如Access或是其他的,就算有的数据库也有时间戳,可能使用规则与Sql Server也不同,因此,如果系统是建在ORM的开发模式上,系统的异构数据库移植就会存在问题,因此,对于系统来说,要有应付所有数据库并发问题的机制。
分析问题
为了寻找一个通用数据库都能采纳的并发处理机制,我们可以为表专门建一个字段,用来控制数据的并发:
在Insert时,插入新值
在Update时,进行旧值的比较,如果成功,也要更新此字段值
在Delete时,进行旧值比较,如果不同,则不成功
这样的控制方式跟SQL Server的差不多,但需要手动维护此字段值,而这个操作,我们就可以交给ORM的持久层来完成。
关于此值采用什么样的值?
此字段值要求具有唯一性,在任何情况下都不能产生相同的值,或者说几乎不可能产生相同的值,自然会想到GUID,但我觉得GUID数值太大,不利于数值比较,所以决定采用System.DateTime.Now.Ticks这是一个12位的数字, 此属性的值为自
解决问题
SPL(SmartPersistenceLayer)为了.NET持久层,在
SmartPersistenceLayer的原理:
给表中定义一个字段为“时间戳”。
在Insert时会自动根据System.DateTime.Now.Ticks产生新值,插入到新记录中
在Update时,比较旧值是否相等,如果相等,则生成新的Ticks更新“时间戳”字段值,因此可以根据Update影响的条数来判断是否有并发错误。
在Delete时,比较旧值是否等,也可以通过影响条数来判断。
在事务处理时,有个属性叫“是否强行执行”
If(强行执行)
{
则在处理过程中,就算遇到“影响条数为零”也会提交事务
}else
{
如果遇到“影响条数为零”,则回滚事务
}
SmartPersistenceLayer的使用:
1. 如果要对某个表进行并发控制,则给此表定义一个字符型的字段,由于采用Ticks的值,所以字符长度建议20个字符以上,比如叫TS。
2. 在ClassMap.xml中的字段属性上添加“timestamp="true"”,以此标明是“时间戳”字段,如:
<attribute name="ts" column="ts" type="String" timestamp="true"/>
3. 在实体Save()时,用“影响条数”来判断是否存在并发操作:
Student.Id=1;
Student.Retrieve();
If(Student.IsPersistent)
{
Student.Name=…
…. //属性赋值,不需要给TS赋值
Try
{
If(Student.Save()>0)
{
//操作成功
}else
{
//存在并发错误,提示用户重新操作。
}
}catch
{
//保存数据发生异常
}
}
从上面的代码可以看出,遇到保存异常与并发操作是两回事,在我们进行“更新”都是建议进行一次Retrieve()操作的。这样可以减少发生并发错误的可能性。
其他的Delete()同上面的类似,也采用If(Student.Delete()>0)来进行判断,这也可以方便显示“影响条数”:
int affect=Student.Delete(); //affect为删除的记录数
这是对于单个实体进行的并发控制,由于目前无法进行“批量更新”的并发控制,所以没有对UpdateCriteria进行并发控制,讨论文章:再谈数据的并发处理
事务中对并发的控制
在SPL中,对事务也进行了并发控制,事务中定义了一个属性IsForceCommit:是否强行执行。
如果IsForceCommit=false(此为默认值),则在事务中,如果遇到并发错误,整个事务都将Roolback
如果IsForceCommit=true,则在事务中,就算遇到并发错误,也会Commit此事务
这里所说的并发错误,其实就是指Update()与Delete()是否影响条数。
t.IsForceCommit=true; //可选,默认为false
t.AddSaveObject(Student);
t.AddSaveObject(Student2);
….
t.Process(); //根据IsForceCommit的值进行事务提交
到此为止,在SPL可以轻松实现分布式异构数据库的事务提交,这可以对SPL支持数据库的任意组合提供并发处理。
PS:SPL3.1版将不久发布。
听棠