6

ビジネスロジックを統合して明確にすることを期待して、Spring Statemachine を既存のプロジェクトに導入しています。相互接続された状態を持つさまざまな JPA エンティティがあり、既存の状態マシンの現在の状態として永続化された状態を設定するのに問題があります。

StateMachineFactory を使用して、エンティティ インスタンスごとに新しい StateMachine インスタンスを作成しています。StateMachine の現在の状態を別のフィールドに保存して、Hibernate を永続化します。理想的には、永続化されたフィールドの値を StateMachine と同期する必要があります。私の質問は、これが通常 Spring Statemachine でどのように達成されるべきかについてです。

@Entity
@EntityListeners(MyEntityListener.class)
public class MyEntity {

    @Column
    private MyState internalState; // Using AttributeConverter

    @Transient
    private StateMachine<MyState, Event> stateMachine;

}
public class MyEntityListener {

    @PostLoad
    public void postLoad(MyEntity entity) {
        // TODO Set StateMachine's current state to entity's internal state
    );

}
  1. 1 つの方法として、ローカル遷移を定義して初期状態を永続状態に移行する方法があります。その後、条件付きチェックを実行して、ソース状態をターゲット状態に移動させるローカル遷移に関連付けられたイベントを見つけることができます。これは少し厄介に思えます。ステート マシンの構成をできるだけクリーンに保ちたいと考えています。

  2. StateMachine の現在の状態を、トランジションを経由せずにパブリック API を介して設定する方法がわかりません。そのため、私が検討した別のアプローチは、StateMachine インスタンスをラップして次のメソッドを公開することです (便利なデフォルトスコープであるため)。

package org.springframework.statemachine.support;

public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSupport<S, E> implements StateMachine<S, E>, StateMachineAccess<S, E> {

    void setCurrentState(State<S, E> state, Message<E> message, Transition<S, E> transition, boolean exit, StateMachine<S, E> stateMachine)

}
package org.springframework.statemachine.support;

public class MyStateMachineWrapper<S, E> {

    private AbstractStateMachine<S, E> stateMachine;

    public MyStateMachineWrapper(StateMachine<S, E> stateMachine) {
        if (stateMachine instanceof AbstractStateMachine) {
            this.stateMachine = (AbstractStateMachine<S, E>)stateMachine;
        } else {
            throw new IllegalArgumentException("Provided StateMachine is not a valid type");
        }
    }

    public void setCurrentState(S status) {
        stateMachine.setCurrentState(findState(status), null, null, false, stateMachine);
    }

    private State<S, E> findState(S status) {
        for (State<S, E> state : stateMachine.getStates()) {
            if (state.getId() == status) {
                return state;
            }
        }

        throw new IllegalArgumentException("Specified status does not equate to valid State");
    }
}

次に、次のコードをMyEntityListener.postLoadにスローします。

MyStateMachineWrapper<MyState, Event> myStateMachineWrapper = new MyStateMachineWrapper<>(entity.getStateMachine());
myStateMachineWrapper.setCurrentState(entity.getInternalState());

上記のアプローチはうまく機能しているように見えますが、これがどのように機能すると想定されていたかは想像できません。確かにこれを達成するためのよりクリーンな方法がありますか、それともプロジェクトが十分に成熟しておらず、この機能がまだ含まれていないのでしょうか?

ご意見やご感想をお寄せいただきありがとうございます。

4

1 に答える 1

2

上記のオプション 2 をクリーンアップし、ラッパー クラスを utils クラスに変更しました。明確にするために、このアプローチは、デフォルトのアクセサーを持つ setCurrentState メソッドを利用するため、脆弱なソリューションになる可能性があります。

package org.springframework.statemachine.support;

public abstract class MyStateMachineUtils extends StateMachineUtils {

    public static <S, E> void setCurrentState(StateMachine<S, E> stateMachine, S state) {
        if (stateMachine instanceof AbstractStateMachine) {
            setCurrentState((AbstractStateMachine<S, E>)stateMachine, state);
        } else {
            throw new IllegalArgumentException("Provided StateMachine is not a valid type");
        }
    }

    public static <S, E> void setCurrentState(AbstractStateMachine<S, E> stateMachine, S state) {
        stateMachine.setCurrentState(findState(stateMachine, state), null, null, false, stateMachine);
    }

    private static <S, E> State<S, E> findState(AbstractStateMachine<S, E> stateMachine, S stateId) {
        for (State<S, E> state : stateMachine.getStates()) {
            if (state.getId() == stateId) {
                return state;
            }
        }

        throw new IllegalArgumentException("Specified State ID is not valid");
    }
}

これは、次のように非常にうまく使用できます。

MyStateMachineUtils.setCurrentState(entity.getStateMachine(), entity.getInternalState());
于 2015-06-02T16:54:05.453 に答える