比如要扣钱什么的,或者数字叠加,这种就需要通过 set 字段=段字段+1 这种方处理
//正确写法:安全的字段累计 var result= db.Updateable<Student>().SetColumns(it => it.Num== it.Num+1).Where(it => it.Id == 1).ExecuteCommand(); //sql: num=num+1 //错误写法: 在程序中计算是不安全的 var num=data.num+1 var result= db.Updateable<Student>().SetColumns(it => it.Num== num).Where(it => it.Id == 1).ExecuteCommand(); //sql: num=值
注意:这种更新方式只适合明确主键的更新,条件形态的建议用下面的锁
var result67 = db.Updateable(updateObjs) //批量更新单独处理num列 set num=num+1 .PublicSetColumns(it => it.Num, it => it.Num+ 1) .ExecuteCommand();
db.Updateable(list) PublicSetColumns(it => it.Price, "+") //set price=price+list[i].price .ExecuteCommand();
使用乐观锁需要满足2个条件:
1、用户要打开编辑界面,然将数据绑定到编辑界面
2、用户在界面填写完在点修改保存
原理:打开编辑框太久别人已经修改了这条记录,我在提交就可能把别人更新数据清空掉,我们需要一个时间字段去和数据库中的Version比对
重现步骤:开2个浏览器 ,编辑同一条记录, A浏览器先保存,然后B浏览器在保存就能重现
因为是同一条记录本身并不存在并发,因为编辑界面打开一天也不会刷新数据,点保存一样覆盖
新项目推荐用新功能,使用方便有问题及时沟通
public class ULockEntity { [SqlSugar.SugarColumn(IsPrimaryKey = true, IsIdentity = true)] public int Id { get; set; } public string Name { get; set; } [SqlSugar.SugarColumn(IsEnableUpdateVersionValidation = true)]//标识版本字段 public long Ver { get; set; } //支持Guid long string DateTime (不推荐DateTime 时间有精度问题) //推荐用 string guid (使用long要序列化成strting不然前端精度丢失) } //注意:只能是实体更新不能是集合更新 //【用法1:不扔错】 Ver与数据库字段不同不报错返回0 var rows = db.Updateable(new ULockEntity() { Id = id, Name = "newname", Ver = 1551128313597136896//版本字段会自动更新 }).ExecuteCommandWithOptLock(); //【用法2:扔出错误】Ver与数据库字段不同直接扔错出误 var rows = db.Updateable(new ULockEntity() { Id = id, Name = "newname", Ver = 1551128313597136896 //版本字段会自动更新 }).ExecuteCommandWithOptLock(true); //加上true就会扔出错误 //try { ... }catch(VersionExceptions ex){...} //底层 throw new VersionExceptions /*********完整测试用例*********/ //添加测试数据 var db = NewUnitTest.Db; db.CodeFirst.InitTables<ULockEntity>(); db.DbMaintenance.TruncateTable<ULockEntity>(); //第一次插入ver=0 var id = db.Insertable(new ULockEntity(){Name = "oldName" ,Ver=0}).ExecuteReturnIdentity(); //开始用例 var rows= db.Updateable(new ULockEntity() { Id = id, Name = "newname", Ver = 0 //会自动更新版本字段更新后数据库将不在是0 }).ExecuteCommandWithOptLock(); //rows=1 因为数据库ver是0你传的也是0 var rows= db.Updateable(new ULockEntity() { Id = id, Name = "newname2", Ver = 0 }).ExecuteCommandWithOptLock(); //rows=0 失败:数据库ver不等于0
例如:SqlServer中的时间戳类型的字段,会在这条记录变更后会自动更新,如果没这种机质的字段你也可以用触发器实现这种机质
有这种机质我们只要打个特性就能实现并发控制,代码如下:
数据库 | 数据库 | 实体 |
SQLSERVER | timestamp(默认自动更新) | byte[] |
其他数据库 | timestamp(需要配置自动更新) | DateTime |
public class StudentVersion { [SugarColumn(IsPrimaryKey =true,IsIdentity =true)] public int Id { get; set; } public string Name { get; set; } public DateTime CreateTime { get; set; } [SqlSugar.SugarColumn( IsEnableUpdateVersionValidation = true,//标识版本字段 IsOnlyIgnoreInsert=true,//禁止插入 IsOnlyIgnoreUpdate=true,//禁止更新 ColumnDataType="timestamp" //时间戳类型,有些库需要配置或者有差异 )] // SqServer byte[] 其它数据库用 DateTime public byte[] Timestamp { get; set; } } db.Updateable(data).IsEnableUpdateVersionValidation().ExecuteCommand();//更新的时候启用验证
原理:
//查询这条记录 var data = db.Queryable<StudentVersion>().InSingle(id); //成功 (data.Timestamp等于数据库中的 Timestamp字段) db.Updateable(data).IsEnableUpdateVersionValidation().ExecuteCommand();//执行后数据库Timestamp更新 //失败 (data.Timestamp不等于数据库中的 Timestamp字段) db.Updateable(data).IsEnableUpdateVersionValidation().ExecuteCommand();
如何知道是并发错误,还是其他错误
//底层代码 throw new VersionExceptions //捕获异常的时候只要是 VersionExceptions 那就是并发错误
只处理主表数据就行了,从表不需要考虑,因为是一起提交
悲观锁适合用于 读取和插入在同一个步骤下的操作,并且禁止2个请求同时操作
2016 © donet5.comApache Licence 2.0