52. Data Multi Persist

Data Multi Persist is an example which is an extension of two other samples Chapter 50, JPA Config and Chapter 51, Data Persist. We still keep machine configuration in a database and persist into a database but this time we also have a machine containing two orthogonal regions showing how those are persisted independently. This sample is also using embedded H2 database with a H2 Console to ease playing with a database.

This sample uses spring-statemachine-autoconfigure which on default auto-configures repositories and entity classes needed for JPA. Thus only @SpringBootApplication is needed.

@SpringBootApplication
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

We again create a StateMachineRuntimePersister.

@Bean
public StateMachineRuntimePersister<String, String, String> stateMachineRuntimePersister(
        JpaStateMachineRepository jpaStateMachineRepository) {
    return new JpaPersistingStateMachineInterceptor<>(jpaStateMachineRepository);
}

StateMachineService makes it easier to work with a machines.

@Bean
public StateMachineService<String, String> stateMachineService(
        StateMachineFactory<String, String> stateMachineFactory,
        StateMachineRuntimePersister<String, String, String> stateMachineRuntimePersister) {
    return new DefaultStateMachineService<String, String>(stateMachineFactory, stateMachineRuntimePersister);
}

We use data from json to import configuration.

@Bean
public StateMachineJackson2RepositoryPopulatorFactoryBean jackson2RepositoryPopulatorFactoryBean() {
    StateMachineJackson2RepositoryPopulatorFactoryBean factoryBean = new StateMachineJackson2RepositoryPopulatorFactoryBean();
    factoryBean.setResources(new Resource[] { new ClassPathResource("datajpamultipersist.json") });
    return factoryBean;
}

What comes for a machine config RepositoryStateMachineModelFactory can be used as shown below.

@Configuration
@EnableStateMachineFactory
public static class Config extends StateMachineConfigurerAdapter<String, String> {

    @Autowired
    private StateRepository<? extends RepositoryState> stateRepository;

    @Autowired
    private TransitionRepository<? extends RepositoryTransition> transitionRepository;

    @Autowired
    private StateMachineRuntimePersister<String, String, String> stateMachineRuntimePersister;

    @Override
    public void configure(StateMachineConfigurationConfigurer<String, String> config)
            throws Exception {
        config
            .withPersistence()
                .runtimePersister(stateMachineRuntimePersister);
    }

    @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);
    }
}

Let’s get into actual demo. Run the boot based sample application:

# java -jar spring-statemachine-samples-datajpamultipersist-2.1.0.RC1.jar

Accessing application via http://localhost:8080 brings up a new constructed machine with every request and you can choose to send events to a machine. Possible events and machine configuration are updated from a database with every request. We also print out all state machine contexts and current root machine.

sm datajpamultipersist 1

Machine datajpamultipersist1 is simple flat machine where states S1, S2 and S3 are transitioned with events E1, E2 and E3 meaning nothing new there. However machine datajpamultipersist2 contains two regions R1 and R2 directly under root level, thus a reason why root level machine really doesn’t have a state at all but we still need that root level machine to host those regions.

Regions R1 and R2 in machine datajpamultipersist2 contains states S10, S11, S12 and S20, S21, S22 respectively and events E10, E11 and E12 are used for region R1 and events E20, E21 and E22 for region R2. Lets send events E10 and E20 to machine datajpamultipersist2 and see how things look like.

sm datajpamultipersist 2

Regions have their own contexts with their own id’s and where the actual id is postfixed with # plus region id. As shown below there are different contexts in a database for regions.

sm datajpamultipersist 3