1

"<version />"hbm.xml s で要素を設定してエンティティを更新するときは、楽観的な戦略を使用しています。単一のエンティティを更新すると正常に動作します。しかし、このシナリオを扱う場合、この戦略は失敗します。

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


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

制約があります: Resourceには、重複する dateRange を持つSalesPlanがあってはなりません。例:

「Hippoom リゾート」という名前のリソースがあるとします。

また 、2013 年 11 月 1 日から 2013 年 11 月 2 日の範囲のSalesPlanがあります。

2013 年 11 月 2 日から 2013 年 11 月 2 日までのSalesPlanを追加したい場合

次に、重複する日付範囲では失敗するはずです

この「範囲」の場合、データベースの一意のキーが機能しないため、これを Java で実装する必要があります。コードは次のようになります。

@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はありません。

—————改訂済み————————</p>

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 fetch sql が古くなる可能性があります

| | 最初の取引開始 |
| | | | 2番目のトランザクションが開始されました
| | リソースを選択 |
| | | | リソースを選択
| | すべてのセールスプランを選択 |
| | | | すべての販売プランを選択
| | コミットされたすべての販売計画に基づいて検証する |
| | | | コミットされたすべての販売計画に基づいて検証する
| | 販売計画を挿入 |
| | | | 販売計画を挿入
| | リソースを更新してバージョンを確認 |
| | | | リソースを更新してバージョンを確認する
| | トランザクションをコミット |
| | | | バージョンがダーティであるため、ロールバックします

—————改訂済み————————</p>

Hibernate のバージョンは 3.6.10.FINAL です。これを修正できる可能性はありますか?

4

2 に答える 2

0

em.Lock (class, primaryKey, LockModeType) を試してください

LockModeType.OPTIMISTIC - これは誰かが途中でバージョンを変更して最終的にロールバックした場合にトランザクションの最後にバージョンをチェックするか、またはLockModeType.OPTIMISTIC_FORCE_INCREMENT - これは常にトランザクションとロールバックの最後にバージョンを更新してチェックします必然的に

于 2014-02-04T18:40:10.303 に答える
0

私は暫定的な解決策を見つけます。次のように Resource にブール値フィールドを追加します。

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

    private boolean dirty = false;

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

このフィールドはドメイン内では何の意味もありませんが、リソースを更新する前に、alwaysMakeDirty() を使用して update sql を起動できます。

私はそれがかなりトリッキーであることを知っていますが、うまくいきます。

于 2013-05-31T06:37:18.607 に答える