【问题标题】:Persisting & Restoring Current State in Spring Statemachine在 Spring Statemachine 中持久化和恢复当前状态
【发布时间】:2015-08-16 05:07:11
【问题描述】:

我将 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. 一种方法可能是定义局部转换以将初始状态移动到持久状态。然后我可以进行条件检查以找到与本地转换相关的事件,这会将源状态移动到目标状态。这对我来说似乎有点乱,我想尽可能保持状态机的配置干净。

  2. 我看不到如何通过公共 API 设置 StateMachine 的当前状态而不通过转换,因此我探索的另一种方法是包装 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());

上述方法似乎工作正常,但我无法想象这是设想的工作方式。肯定有更简洁的方法来实现这一点,或者项目还不够成熟,还没有包含这个功能?

感谢您的任何想法和意见。

【问题讨论】:

    标签: persistence state spring-statemachine


    【解决方案1】:

    我已经清理了上面的选项 #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());
    

    【讨论】:

    • 由于我们缺少一些内部功能,您尝试执行的操作尚无法实现。 github.com/spring-projects/spring-statemachine/issues/34github.com/spring-projects/spring-statemachine/issues/35 直接与这些功能相关。状态机同步需要的不仅仅是设置当前状态。我们正在尝试实现 SPI,分布式状态机可以使用它来同步和保持其当前状态以及正常工作所需的一切。既然第二个里程碑已经结束,我现在正计划开始这项工作。
    • 感谢您澄清这一点。由于目前还没有正确的方法,我将彻底测试上述解决方法,因为我们的用例现在非常有限,希望我们现在可以不用您即将进行的更改。
    • 艾伦,entity.getStateMachine() 给出 null,这是正常的,因为它是瞬态的。如果您在 MyStateMachineUtils.setCurrentState(entity.getStateMachine(), entity.getInternalState()); 中传递 null,这是如何工作的;
    • @Petar,当时实体在其构造函数中从 StateMachineFactory 创建了一个新的 StateMachine 实例,并将初始状态设置为模仿持久状态。从那时起,这个项目已经进行了很多,我认为我的“黑客”解决方案仍然不相关。我将尝试整理一个简单的示例,说明它今天是如何工作的,因为这周我碰巧需要一个状态机来执行不同的项目。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-06
    • 1970-01-01
    • 1970-01-01
    • 2011-01-09
    • 1970-01-01
    • 2019-03-31
    相关资源
    最近更新 更多