Skip to content

Java客户端 乐观锁

He, Jiehui edited this page Jan 17, 2017 · 6 revisions

概述

Since:1.6.0

Version用于乐观锁更新。当更新操作执行的时候,version对应的字段会作为查询条件与主键一起包含在update语句里面。只有当version字段的值与数据库里面version值一致的情况下,更新才能成功

适用范围

适用于单表DAO里面的update和batchUpdate更新操作。

当前不支持携程的SqlServer,因为现在携程的SqlServer是通过SPA/SP3的方式来执行CUD操作。

使用说明

支持通过标注@Version作为表的版本字段。支持整形和时间戳类型。一个entity只能定义一个版本字段。

缺省版本递增

如果没有指定version字段不能更新,则在更新的时候DAL会自动更新版本。整形为加1操作,时间戳为获取数据库的时间戳

@Column(name="last_changed")
@Type(value=Types.TIMESTAMP)
@Version
private Timestamp lastChanged;

数据库触发递增

如果数据库本身设置了更新的触发器能自动更新版本自动,则可以通过指定@Column里面的updatable=false来取消自动更新。

注意

版本为1.6.0,1.6.1的updatable仅仅当Version存在的情况下才有效。1.6.2开始会单独使用.

@Column(name="version", updatable=false)
@Type(value=Types.INTEGER)
@Version
private Integer version;

版本值初始化

如果希望插入的时候也使用数据库的缺省值或触发器,可以通过指定@Column里面的insertable=false来在插入操作中去掉version字段。否则需要用户自己给版本赋值

注意

insertable可以单独使用

@Column(name="last_changed", insertable=false)
@Type(value=Types.INTEGER)
@Version
private Integer version;

例子

第一个是基于时间戳的,第二个是基于int的

@Test
public void testUpdatableWithVersionByDao() throws SQLException {
    DalParser<UpdatableVersionModel> parser = new DalDefaultJpaParser<>(UpdatableVersionModel.class, getDbName());
    DalTableDao<UpdatableVersionModel> dao = new DalTableDao<UpdatableVersionModel>(parser);
    
    DalHints hints = new DalHints();
    
    List<UpdatableVersionModel> pojos = dao.query("1=1", new StatementParameters(), new DalHints());
    for(UpdatableVersionModel model: pojos){
        model.setAddress("1122334455");//Old value is SH INFO
        Timestamp t = model.getLastChanged();
        t.setTime(t.getTime()+100);
        model.setLastChanged(t);
    }
    
    int[] result = dao.batchUpdate(hints, pojos);
    assertArrayEquals(new int[]{0, 0 , 0}, result);
    
    pojos = dao.query("1=1", new StatementParameters(), new DalHints());
    for(UpdatableVersionModel model: pojos)
        assertEquals("SH INFO", model.getAddress());//Still old value because version is incorrect

    // Now the right case
    pojos = dao.query("1=1", new StatementParameters(), new DalHints());
    long[] oldVer = new long[3];
    int i = 0;
    for(UpdatableVersionModel model: pojos){
        model.setAddress("1122334455");
        oldVer[i++] = model.getLastChanged().getTime();
    }
    
    result = dao.batchUpdate(hints, pojos);
    assertArrayEquals(new int[]{1, 1, 1}, result);

    pojos = dao.query("1=1", new StatementParameters(), new DalHints());
    i = 0;
    for(UpdatableVersionModel model: pojos){
        assertEquals("1122334455", model.getAddress());
        Assert.assertTrue(oldVer[i++] <= model.getLastChanged().getTime());
    }
}     

@Test
public void testUpdatableWithIntVersionByDao() throws SQLException {
    DalParser<UpdatableIntVersionModel> parser = new DalDefaultJpaParser<>(UpdatableIntVersionModel.class, getDbName());
    DalTableDao<UpdatableIntVersionModel> dao = new DalTableDao<>(parser);
    
    DalHints hints = new DalHints();
    
    List<UpdatableIntVersionModel> pojos = dao.query("1=1", new StatementParameters(), new DalHints());
    int i = 0;
    for(UpdatableIntVersionModel model: pojos){
        model.setAddress("1122334455");
        model.setTableIndex(-1);// Make the version incorrect
    }
    
    int[] result = dao.batchUpdate(hints, pojos);
    assertArrayEquals(new int[]{0, 0, 0}, result);
    
    pojos = dao.query("1=1", new StatementParameters(), new DalHints());
    for(UpdatableIntVersionModel model: pojos)
        assertEquals("SH INFO", model.getAddress());//Still old value because version is incorrect

    // Now the right case
    Integer[] oldValue = new Integer[3];
    pojos = dao.query("1=1", new StatementParameters(), new DalHints());
    i = 0;
    for(UpdatableIntVersionModel model: pojos){
        model.setAddress("1122334455");
        oldValue[i++] = model.getTableIndex();
    }
    
    result = dao.batchUpdate(hints, pojos);
    assertArrayEquals(new int[]{1, 1, 1}, result);

    pojos = dao.query("1=1", new StatementParameters(), new DalHints());
    i = 0;
    for(UpdatableIntVersionModel model: pojos) {
        assertEquals("1122334455", model.getAddress());
        Assert.assertTrue(oldValue[i++]+1 == model.getTableIndex());
    }
}