从数据库读取配置时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
相关产品推荐
相关产品推荐

