并发控制、更新、版本控制

1、并发累计(累加)

1.1 单条批量累计+1

比如要扣钱什么的,或者数字叠加,这种就需要通过  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=值

注意:这种更新方式只适合明确主键的更新,条件形态的建议用下面的锁

1.2批量更新并且字段+1

  var result67 =
          db.Updateable(updateObjs)
          //批量更新单独处理num列 set num=num+1
          .PublicSetColumns(it => it.Num, it => it.Num+ 1)
          .ExecuteCommand();

1.3批量更新并且字段+list中对应的值

   db.Updateable(list)
   PublicSetColumns(it => it.Price, "+") //set price=price+list[i].price
   .ExecuteCommand();


2、防止提交覆盖,重复提交(乐观锁)

使用乐观锁需要满足2个条件:

1、用户要打开编辑界面,然将数据绑定到编辑界面

2、用户在界面填写完在点修改保存

原理:打开编辑框太久别人已经修改了这条记录,我在提交就可能把别人更新数据清空掉,我们需要一个时间字段去和数据库中的Version比对

重现步骤:开2个浏览器 ,编辑同一条记录, A浏览器先保存,然后B浏览器在保存就能重现

因为是同一条记录本身并不存在并发,因为编辑界面打开一天也不会刷新数据,点保存一样覆盖

2.1 不依赖库同步 (新功能

新项目推荐用新功能,使用方便有问题及时沟通

    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

2.2 依赖库同步(老功能)

例如:SqlServer中的时间戳类型的字段,会在这条记录变更后会自动更新,如果没这种机质的字段你也可以用触发器实现这种机质

有这种机质我们只要打个特性就能实现并发控制,代码如下:

数据库数据库实体
SQLSERVERtimestamp(默认自动更新)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.3 一对多提交

只处理主表数据就行了,从表不需要考虑,因为是一起提交


3、悲观锁的用法

悲观锁适合用于  读取和插入在同一个步骤下的操作,并且禁止2个请求同时操作

https://www.donet5.com/Home/Doc?typeId=2416

关闭
果糖网