1

私は、サードパーティ システムへの顧客のプロビジョニングを担当する典型的なエンタープライズ アプリケーションを開発しました。このシステムには、特定の顧客に対して 1 つのスレッドしか動作できないという制限があります。そのため、現在進行中の customerIds@Singletonを含む単純なロック メカニズムを追加しました。Setプロビジョニングの新しいリクエストが来るたびに、最初にこれをチェックしますSet。cusotomerId が存在する場合は待機し、存在しない場合はそれを に追加してSet処理に入ります。

最近、このアプリケーションをクラスタにデプロイすることが決定されました。これは、このロック アプローチが有効ではなくなったことを意味します。ロックに DB を使用するソリューションを考え出しました。customerIds を含む単一の列を持つテーブルを作成しました (一意の制約もあります)。新しいプロビジョニング要求が来ると、トランザクションを開始し、customerId で行をロックしようとしますSELECT FOR UPDATE(customerId がまだ存在しない場合は挿入します)。その後、顧客のプロビジョニングを開始し、終了したらトランザクションをコミットします。コンセプトは機能しますが、トランザクションに問題があります。現在、 と からの customerId の追加と削除を処理するとメソッドを含むクラスCustomerLockがあります。このクラスを、Bean 管理のトランザクションを持つステートレス EJB に変換したいと考えました。add()remove()Setadd()メソッドはトランザクションを開始して行をロックし、remove()メソッドはトランザクションをコミットして行をロック解除します。しかし、トランザクションの開始と終了は同じ方法で行わなければならないようです。私が説明したアプローチを使用する方法はありますか、またはトランザクションが同じ方法で開始および終了するようにロジックを変更する必要がありますか?

カスタマーロック クラス:

@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class CustomerLock {

    @Resource
    private UserTransaction tx;

    public void add(String customerId) throws Exception {
        try {
            tx.begin();
            dsApi.lock()
        } catch (Exception e) {
            throw e;
        }
    }

    public void remove(String customerId) throws Exception {
        try {
            tx.commit();
        } catch (Exception e) {
            throw e
        }
    }
}

CustomerProvisioner クラスの抜粋:

public abstract class CustomerProvisioner {

    ...

    public void execute(String customerId) {
        try {
            customerLock.add(customerId);

            processing....

            customerLock.remove(customerId);
        } catch (Exception e) {
            logger.error("Error", e);
        }
    }

    ...

}

StandardCustomerProvisioner クラス:

@Stateless
public class StandardCustomerProvisioner extends CustomerProvisioner {

    ...

    public void provision(String customerId) {
        // do some business logic
        super.execute(customerId);
    }
}
4

1 に答える 1

1

@Gimby が指摘したように、コンテナ管理のトランザクションと Bean 管理のトランザクションを混在させないでください。StandardCustomerProvisioner には "@TransactionManagement(TransactionManagementType.BEAN)" のような注釈がないため、コンテナ管理のトランザクションを使用し、デフォルトで必須です。

動作させるには 2 つのオプションがあります。

1) UserTransaction 呼び出しで「@TransactionManagement(TransactionManagementType.BEAN)」を削除し、CMT を実行するには

2) この注釈 ("@TransactionManagement(TransactionManagementType.BEAN)") を StandardCustomerProvisioner に追加し、このメソッドからのトランザクション マークアップ呼び出しを使用して、呼び出されたすべてのメソッドが同じトランザクション コンテキストを使用するようにします。とにかく、CustomerLock からのマークアップ呼び出しは削除する必要があります。

于 2017-01-06T09:55:49.180 に答える