使用 spring-statemachine 状态机持久化时,可以通过内存、spring-statemachine-redis 或 spring-statemachine-data-jpa 现有方式持久化处理。
因项目审核操作记录频繁,数据量大,使用 内存 或 spring-statemachine-redis 模式不可取,而项目使用的是 MyBatis,使用 spring-statemachine-data-jpa 也不合适,需要自定义实现,简述步骤如下:
-
- <dependency>
- <groupId>org.springframework.statemachinegroupId>
- <artifactId>spring-statemachine-starterartifactId>
- <version>2.2.3.RELEASEversion>
- dependency>
-
- <dependency>
- <groupId>org.springframework.statemachinegroupId>
- <artifactId>spring-statemachine-kryoartifactId>
- <version>1.2.14.RELEASEversion>
- dependency>
- CREATE TABLE `state_machine_context` (
- `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主键',
- `machine_type` VARCHAR (32) DEFAULT '' COMMENT '状态机类型',
- `machine_id` VARCHAR (36) DEFAULT '' COMMENT '状态机ID',
- `machine_data` TINYBLOB COMMENT '状态机数据',
- `machine_state` VARCHAR (32) DEFAULT '' COMMENT '状态机状态',
- `machine_event` VARCHAR (36) DEFAULT '' COMMENT '状态机事件',
- `create_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
- `update_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
- PRIMARY KEY (`id`),
- KEY `idx_machine_id` (`machine_id`)
- ) ENGINE = INNODB COMMENT = '状态机上下文'
关键字段说明
其它字段可根据自己业务需求自定义
- import com.esotericsoftware.kryo.Kryo;
- import com.esotericsoftware.kryo.io.Input;
- import com.esotericsoftware.kryo.io.Output;
- import org.apache.commons.lang3.tuple.Pair;
- import org.springframework.messaging.MessageHeaders;
- import org.springframework.statemachine.StateMachineContext;
- import org.springframework.statemachine.StateMachinePersist;
- import org.springframework.statemachine.kryo.MessageHeadersSerializer;
- import org.springframework.statemachine.kryo.StateMachineContextSerializer;
- import org.springframework.statemachine.kryo.UUIDSerializer;
-
- import java.io.ByteArrayInputStream;
- import java.io.ByteArrayOutputStream;
- import java.util.Date;
- import java.util.Objects;
- import java.util.Optional;
- import java.util.UUID;
-
- /**
- * @author songjianyong
- */
- public class CustomStateMachinePersist
implements StateMachinePersist> { - private static final ThreadLocal
KRYO_THREAD_LOCAL = ThreadLocal.withInitial(() -> { - Kryo kryo = new Kryo();
- kryo.addDefaultSerializer(StateMachineContext.class, new StateMachineContextSerializer());
- kryo.addDefaultSerializer(MessageHeaders.class, new MessageHeadersSerializer());
- kryo.addDefaultSerializer(UUID.class, new UUIDSerializer());
- return kryo;
- });
-
- private byte[] serialize(StateMachineContext
context) { - Kryo kryo = KRYO_THREAD_LOCAL.get();
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- Output output = new Output(out);
- kryo.writeObject(output, context);
- output.close();
- return out.toByteArray();
- }
-
- @SuppressWarnings("unchecked")
- private StateMachineContext
deserialize(byte[] data) { - if (data == null || data.length == 0) {
- return null;
- }
- Kryo kryo = KRYO_THREAD_LOCAL.get();
- ByteArrayInputStream in = new ByteArrayInputStream(data);
- Input input = new Input(in);
- return kryo.readObject(input, StateMachineContext.class);
- }
-
- private final StateMachineContextDao stateMachineContextDao;
-
- public SongStateMachinePersist(StateMachineContextDao stateMachineContextDao) {
- this.stateMachineContextDao = stateMachineContextDao;
- }
-
- @Override
- public void write(StateMachineContext
context, Pair pair) throws Exception { - byte[] machineData = serialize(context);
- String machineId = pair.getKey();
- String machineType = pair.getValue();
- StateMachineContextEntity stateMachineContexEntity = stateMachinePersistDao.findByMachineIdAndMachineType(machineId, machineType);
- if (Objects.nonNull(stateMachineContexEntity)) {
- stateMachineContexEntity.setMachineData(machineData);
- stateMachineContexEntity.setMachineState(Optional.ofNullable(context.getState()).map(Objects::toString).orElse(stateMachineContexEntity.getMachineState()));
- stateMachineContexEntity.setMachineEvent(Optional.ofNullable(context.getEvent()).map(Objects::toString).orElse(stateMachineContexEntity.getMachineEvent()));
- stateMachineContexEntity.setUpdateDate(new Date());
- stateMachineContextDao.updateById(stateMachineContexEntity);
- return;
- }
- StateMachineContextEntity entity = new StateMachineContextEntity();
- entity.setMachineId(machineId);
- entity.setMachineData(machineData);
- entity.setMachineType(machineType);
- entity.setMachineState(Optional.ofNullable(context.getState()).map(Objects::toString).orElse(null));
- entity.setMachineEvent(Optional.ofNullable(context.getEvent()).map(Objects::toString).orElse(null));
- stateMachineContextDao.save(entity);
- }
-
- @Override
- public StateMachineContext
read(Pair pair) throws Exception { - String machineId = pair.getKey();
- String machineType = pair.getValue();
- StateMachineContextEntity stateMachineContexEntity = stateMachineContextDao.findByMachineIdAndMachineType(machineId, machineType);
- if (Objects.isNull(stateMachineContexEntity)) {
- return null;
- }
- byte[] machineData = stateMachineContexEntity.getMachineData();
- return deserialize(machineData);
- }
- }
- import org.apache.commons.lang3.tuple.Pair;
- import org.springframework.statemachine.StateMachinePersist;
- import org.springframework.statemachine.persist.AbstractStateMachinePersister;
-
- /**
- * @author songjianyong
- */
- public class CustomStateMachinePersister
extends AbstractStateMachinePersister> { -
- public CustomStateMachinePersister(StateMachinePersist
> stateMachinePersist) { - super(stateMachinePersist);
- }
- }
- /**
- * 持久化到库中
- *
- * @return 数据库持久化状态机
- */
- @Bean(name = "customStateMachinePersister")
- public CustomStateMachinePersister
customStateMachinePersister(StateMachineContextDao stateMachineContextDao) { - CustomStateMachinePersist
customStateMachinePersist = new CustomStateMachinePersist<>(stateMachineContextDao); - return new CustomStateMachinePersister<>(customStateMachinePersist);
- }
- @Resource
- private StateMachinePersister
pair> customStateMachinePersister;