没有字段更改时如何使用hibernate的乐观锁

时间:2013-05-28 03:41:24

标签: hibernate versioning optimistic-locking

我通过在hbm.xml中设置"<version />"元素来更新实体时使用乐观策略。当我更新单个实体时,它工作正常。但是在处理这种情况时,这种策略失败了:

public class SalesPlan {
    //omitted fields
    private Resource resource;
    private DateRange dateRange;
}


public class Resource {
    //omitted fields
    private int version = 1;
}

并且存在一个约束:资源不应该具有重叠日期的 SalesPlan 。例如:

鉴于资源已命名为&#34; Hippoom度假村&#34;

2013年11月1日至2013年11月2日 SalesPlan

当我想添加从2013年11月2日到2013年11月2日的 SalesPlan

然后重叠的日期范围应该失败

我必须在Java中实现它,因为数据库唯一键在此&#34;范围内不起作用&#34; case。代码如下:

@Transactional
@Override
public SalesPlan handle(CreateSalesPlanCommand command) {
    Resource resource = resourceRepository.findBy(command.getResourceId());
    SalesPlan salesPlan = //omitted init codes

    DuplicateSalesPlanSpecification spec = aDuplicateSpec();

    if (spec.isSatisfiedBy(salesPlan)) {
        throw new DuplicateSalesPlanException(salesPlan);
    }

    salesPlanRepository.store(salesPlan);
    resourceRepository.store(salesPlan.getResource());
    return salesPlan;
}

我从 DuplicateSalesPlanSpecification 中的数据库中获取所有已存在的 SalesPlan ,以检查新的 SalesPlan 是否违反了约束。我想更新在并发操作的情况下,最后一步中的资源(检查资源中的版本号)。但我注意到没有更新sql,因为资源不脏。

------ --------修订

select
    resource0_.RESOURCE_ID as RESOURCE1_0_0_,
    resource0_.version as version0_0_,
    //omitted columns
from
    T_IRS_RESOURCE resource0_ 
where
    resource0_.RESOURCE_ID=?


select
    this_.SALES_PLAN_ID as SALES1_1_0_,
    this_.version as version1_0_,
    this_.RESOURCE_ID as RESOURCE3_1_0_,
    this_.DATE_RANGE_START as DATE4_1_0_,
    this_.DATE_RANGE_END as DATE5_1_0_,
    //omitted columns
from
    T_IRS_SALES_PLAN this_ 
where
    this_.RESOURCE_ID=? 
order by
    this_.DATE_RANGE_START desc,
    this_.SALES_PLAN_ID desc


insert into T_IRS_SALES_PLAN//omitted columns


update T_IRS_RESOURCE set version = version + 1
where RESOURCE_ID = ?
and VERSION = ?            //this sql missed 

在使用乐观策略时,如果有人在没有最后一个sql的情况下在另一个事务中插入新的SalesPlan,则SalesPlan获取sql可能是陈旧的

|  the first transaction started               |
|                                              |  the second transaction started
|  select resource                             |
|                                              |  select resource 
|  select all salesplans                       |
|                                              |  select all salesplans
|  validate base on all committed salesplans   |
|                                              |  validate base on all committed salesplans
|  insert salesplan                            |
|                                              |  insert salesplan
|  update resource to check version            |
|                                              |  update resource to check version
|  commit txn                                  |
|                                              |  rolls back because version is dirty

------ --------修订

Hibernate版本是3.6.10.FINAL。我有什么可能解决这个问题吗?

2 个答案:

答案 0 :(得分:0)

我找到了一个临时解决方案。我在Resource上添加了一个布尔字段,如:

public class Resource {
    //omitted fields
    private int version = 1;

    private boolean dirty = false;

    public void alwaysMakeDirty() {
        this.dirty = !dirty;
    }
}

此字段在域中没有任何意义,但在更新资源之前,我可以使用alwaysMakeDirty()来触发更新sql。

我知道这很棘手,但确实有效。

答案 1 :(得分:0)

使用

尝试em.Lock(class,primaryKey,LockModeType)

LockModeType.OPTIMISTIC - 如果某人在中间更改了该版本并最终回滚或 LockModeType.OPTIMISTIC_FORCE_INCREMENT ,则检查该事务结束时的版本 - 这个总是在事务结束时更新并检查版本并在必要时回滚