javaspringspring-bootspring-data-jpaspring-statemachine

Spring state machine with JPA persistence- Repository usage


I am trying to figure out how to easily use spring state machine including persistence with JPA. This is the problem I am dealing with:

Incompatible data types - factory and persistence

At a certain point in the program I would like to use the state machine which is connected to a user. There are repositories for that purpose (project spring-statemachine-data-jpa). At first there is a check if a state machine already exists for a player, using the repository. If not, creating a new state machine and persist it.

The problem is that I have different types of state machines. The factory creates a StateMachine<UserState, UserEvent>, the repository returns a JpaRepositoryStateMachine. These are not compatible to each other and for me it is not clear how to persist / create / restore the state machines.

Can you please clarify that for me?

@Autowired
private StateMachineRepository<JpaRepositoryStateMachine> repository;

public someMethod(User user) {

Optional<JpaRepositoryStateMachine> stateMachine = repository.findById(user.getId()); // JPA state machine

if(stateMachine.isEmpty()) {
            StateMachine<UserState, UserEvent> createdStateMachine = factory.getStateMachine(user.getId()); // spring state machine
            repository.save(createdStateMachine); // compile error
        }

// here: ready-to-use statemachine - how?

}

Thanks for your help!


Solution

  • Try to use SpringStateMachineService to get a state machine instance instead of explicitly retrieving it from repository or factory. You can use default implementation provided by Spring:

    @Bean
    public StateMachineService<State, Event> stateMachineService(
            final StateMachineFactory<State, Event> stateMachineFactory,
            final StateMachinePersist<WorkflowState, WorkflowEvent, String> stateMachinePersist) {
        return new DefaultStateMachineService<>(stateMachineFactory, stateMachinePersist);
    }
    

    So, your code will look like:

    @Autowired
    private StateMachineService<State, Event> stateMachineService;
    
    public someMethod(User user) {
    StateMachine<State, Event> stateMachine = stateMachineService.acquireStateMachine(user.getId(), false);
    
    // here: ready-to-use statemachine - call stateMachine.start() for example
    
    }
    

    If you go inside the acquireStateMachine method you can see that it queries state machine from repository by id and creates a new one if nothing found.

    You can use JpaPersistingStateMachineInterceptor to implicitly save and update state machine instance on every change.

    @Bean
    public JpaPersistingStateMachineInterceptor<State, Event, String>
            jpaPersistingStateMachineInterceptor() {
        return new JpaPersistingStateMachineInterceptor(stateMachineRepository);
    }
    

    See Persisting State Machine