You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从数据库读取配置时Spring State Machine动作执行顺序异常问询

问题描述

将Spring StateMachine的StateMachineFactory配置为从数据库仓库读取状态机配置后,创建状态机并发送事件时,动作的执行顺序无法保持稳定。推测原因是数据库查询返回的动作集合没有固定顺序。

相关POC代码

配置类

@Configuration
@EnableStateMachineFactory
public class Config extends StateMachineConfigurerAdapter<String, String> {
    @Autowired
    private StateRepository<? extends RepositoryState> stateRepository;

    @Autowired
    private TransitionRepository<? extends RepositoryTransition> transitionRepository;

    @Override
    public void configure(StateMachineModelConfigurer<String, String> model) throws Exception {
        model
                .withModel()
                .factory(modelFactory());
    }

    @Bean
    public StateMachineModelFactory<String, String> modelFactory() {
        return new RepositoryStateMachineModelFactory(stateRepository, transitionRepository);
    }

    @Bean
    public Action<String, String> action1() {
        return context -> System.out.println("Action1");
    }

    @Bean
    public Action<String, String> action2() {
        return context -> System.out.println("Action2");
    }
}

启动类

@SpringBootApplication
public class Main implements CommandLineRunner {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }

    @Autowired
    StateMachineFactory<String, String> factory;

    @Autowired
    ApplicationContext context;

    @Override
    public void run(String... args) throws Exception {
        JpaStateRepository stateRepository = context.getBean(JpaStateRepository.class);
        JpaTransitionRepository transitionRepository = context.getBean(JpaTransitionRepository.class);
        JpaActionRepository actionRepository = context.getBean(JpaActionRepository.class);

        JpaRepositoryState stateS1 = new JpaRepositoryState("UNPAID", true);
        JpaRepositoryState stateS2 = new JpaRepositoryState("DONE");

        stateRepository.save(stateS1);
        stateRepository.save(stateS2);

        var action1 = new JpaRepositoryAction();
        action1.setName("action1");

        var action2 = new JpaRepositoryAction();
        action2.setName("action2");

        actionRepository.save(action1);
        actionRepository.save(action2);

        JpaRepositoryTransition transitionS1ToS2 = new JpaRepositoryTransition(null, stateS1, stateS1, "TEST", Set.of(action1, action2));
        transitionRepository.save(transitionS1ToS2);

        for (int i = 0; i < 10; i++) {
            var sm = factory.getStateMachine();
            sm.startReactively().block();
            sm.sendEvent("TEST");
        }
    }
}

日志输出

2022-10-12 22:01:19.474 INFO 58704 --- [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 7082 (http) with context path ''
2022-10-12 22:01:19.482 INFO 58704 --- [ restartedMain] com.poc.Main : Started Main in 2.076 seconds (JVM running for 2.288)
Action2
Action1
Action1
Action2
解决方案

要保证动作的执行顺序,核心是让数据库返回的动作集合带有明确排序规则,且在状态机模型构建时遵循该规则。具体可通过以下方式实现:

1. 给动作实体添加排序字段

  • 在JpaRepositoryAction实体中新增整数类型的sortOrder字段,标记每个动作的执行顺序。
  • 保存动作时,为不同动作设置对应的排序值(比如action1设为1,action2设为2)。
  • 在动作查询的Repository方法中,指定按sortOrder升序排序:
    public interface JpaActionRepository extends JpaRepository<JpaRepositoryAction, Long> {
        List<JpaRepositoryAction> findByTransitionId(Long transitionId, Sort sort);
    }
    
    调用时传入Sort.by(Sort.Direction.ASC, "sortOrder"),确保返回有序结果。

2. 修改过渡实体的动作存储结构

  • 将过渡实体中存储动作的Set类型改为List类型(Set天然无序,List可保留顺序)。
  • 在JpaRepositoryTransition的动作字段上添加@OrderColumn(name = "action_order")注解,让数据库自动维护动作顺序:
    @OneToMany
    @OrderColumn(name = "action_order")
    private List<JpaRepositoryAction> actions;
    
    这样保存和查询时,动作的顺序会被持久化和保留。

3. 在状态机模型工厂中手动排序

若无法修改实体结构,可在RepositoryStateMachineModelFactory构建状态机模型时,对数据库读取的动作集合手动排序:

  • 按照动作的排序字段、名称或ID等规则,将无序Set转换为有序List:
    // 构建Transition逻辑中对动作排序
    Set<JpaRepositoryAction> dbActions = transition.getActions();
    List<JpaRepositoryAction> sortedActions = dbActions.stream()
        .sorted(Comparator.comparing(JpaRepositoryAction::getSortOrder))
        .collect(Collectors.toList());
    // 使用sortedActions构建状态机的动作列表
    

内容的提问来源于stack exchange,提问作者pullCommitRun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 14:25:20