news 2026/9/16 19:21:35

Flowable 引擎 JPA 集成实战:将 JPA 实体作为流程变量使用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Flowable 引擎 JPA 集成实战:将 JPA 实体作为流程变量使用

Flowable 引擎 JPA 集成实战:将 JPA 实体作为流程变量使用

【免费下载链接】flowable-engineA compact and highly efficient workflow and Business Process Management (BPM) platform for developers, system admins and business users.项目地址: https://gitcode.com/GitHub_Trending/fl/flowable-engine

Flowable 工作流引擎允许开发者将 JPA(Jakarta Persistence)实体直接作为流程变量使用,从而在用户任务表单与 Service 任务之间复用现有领域模型,无需为每次读写实体编写专门的查询服务。本文基于 Flowable 官方文档《JPA》一章,结合当前仓库中的源码与测试用例,系统讲解 JPA 实体的支持范围、引擎配置、变量读写原理、流程查询限制,以及结合 Spring Bean 的完整实战案例。

JPA 实体作为流程变量的能力与价值

在 Flowable 中,流程变量(Process Variable)是流程实例运行时的数据载体。除了字符串、数字、日期、序列化对象等常规类型,JPA 实体也可以直接存入变量中,并带来三类核心能力:

  • 更新既有 JPA 实体:实体可以作为流程变量被用户任务表单回填、被 Service 任务中的表达式修改,改动会自动同步回数据库;
  • 复用现有领域模型:不需要为每个实体编写显式的获取与更新服务,实体本身即可在流程中流转;
  • 基于实体属性做决策:排他网关(Exclusive Gateway)的条件表达式可以直接读取实体属性,驱动流程分支走向。

从实现角度看,Flowable 会在变量写入时解析实体的类名与主键值并持久化引用信息;下次读取变量时,再从与引擎关联的EntityManager中按“类 + 主键”重新加载实体。这意味着引擎中保存的并非实体对象的序列化副本,而是指向数据库行的引用,读取时总是拿到数据库中的最新状态。

支持范围与前置要求

并非所有 JPA 实体都能作为流程变量,只有满足以下条件的实体才被支持(对应测试见 JPAVariableTest.java):

  • 注解方式:实体必须使用 JPA 注解配置,字段访问(field access)与属性访问(property access)两种方式均受支持,同时支持映射超类(Mapped Superclass)中定义的主键;
  • 主键要求:实体必须具有标注@Id的主键。复合主键不受支持@EmbeddedId@IdClass均不支持);
  • 主键类型@Id字段/属性的类型须属于 JPA 规范支持的类型:基本类型及其包装类(boolean 除外)、StringBigIntegerBigDecimaljava.util.Datejava.sql.Date

测试代码对这些约束有非常详尽的验证:testStoreJPAEntityAsVariable用例逐一使用byteshortintlongfloatdoublecharStringDateSQLDateBigDecimalBigInteger共 12 种主键类型启动流程实例并回读断言;testIllegalEntities用例则验证了四类非法场景会抛出异常:

  • 使用复合主键(@EmbeddedId)的实体 → 抛出FlowableException,提示 “only single-valued primary keys are supported on JPA-entities”;
  • 主键值为null→ 抛出FlowableIllegalArgumentException,提示 “Value of primary key for JPA-Entity cannot be null”;
  • 主键类型非法(如Calendar)→ 抛出FlowableIllegalArgumentException,提示 “Unsupported Primary key type for JPA-Entity”;
  • 实体有主键但尚未持久化(数据库中不存在该记录)→ 读取变量时抛出FlowableException,提示 “Entity does not exist”。

引擎配置:两种接入 EntityManagerFactory 的方式

要让引擎识别并处理 JPA 实体变量,必须为引擎提供EntityManagerFactory引用,有两种途径:指定 persistence-unit 名称(引擎自行创建工厂)或注入现成的EntityManagerFactory实例。JPA 实体一旦作为变量使用会被自动检测并处理,无需额外开关。

方式一:通过 jpaPersistenceUnitName 指定持久化单元

以下配置基于StandaloneInMemProcessEngineConfiguration,直接给出 persistence-unit 名称,引擎会在启动时通过JpaHelper.createEntityManagerFactory(...)调用jakarta.persistence.Persistence.createEntityManagerFactory(persistenceUnitName)创建工厂(见 JpaHelper.java):

<bean id="processEngineConfiguration" class="org.flowable.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration"> <!-- Database configurations --> <property name="databaseSchemaUpdate" value="true" /> <property name="jdbcUrl" value="jdbc:h2:mem:JpaVariableTest;DB_CLOSE_DELAY=1000" /> <property name="jpaPersistenceUnitName" value="flowable-jpa-pu" /> <property name="jpaHandleTransaction" value="true" /> <property name="jpaCloseEntityManager" value="true" /> <!-- job executor configurations --> <property name="asyncExecutorActivate" value="false" /> <!-- mail server configurations --> <property name="mailServerPort" value="5025" /> </bean>

注意:persistence-unit 必须位于 classpath 中,按照 JPA 规范其默认位置为/META-INF/persistence.xmlflowable-jpa-pu这个名字在仓库的测试中对应org/flowable/standalone/jpa/META-INF/persistence.xml,其中声明了参与持久化单元的实体类与厂商相关配置。

方式二:通过 jpaEntityManagerFactory 注入自定义工厂

若引擎运行在 Spring 容器内,更常见的做法是把自己创建的EntityManagerFactoryBean 注入给引擎。下面示例使用 Spring 的LocalContainerEntityManagerFactoryBean配合 OpenJPA 厂商适配器(示例仅展示与 JPA 相关的 Bean,其余省略;完整可运行示例见 JPASpringTest.java 所在的 flowable-spring 测试模块):

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceUnitManager" ref="pum"/> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter"> <property name="databasePlatform" value="org.apache.openjpa.jdbc.sql.H2Dictionary" /> </bean> </property> </bean> <bean id="processEngineConfiguration" class="org.flowable.spring.SpringProcessEngineConfiguration"> <property name="dataSource" ref="dataSource" /> <property name="transactionManager" ref="transactionManager" /> <property name="databaseSchemaUpdate" value="true" /> <property name="jpaEntityManagerFactory" ref="entityManagerFactory" /> <property name="jpaHandleTransaction" value="true" /> <property name="jpaCloseEntityManager" value="true" /> <property name="asyncExecutorActivate" value="false" /> </bean>

jpaEntityManagerFactory接受任何实现了jakarta.persistence.EntityManagerFactory的对象,因此无论是 OpenJPA、Hibernate 还是 EclipseLink 的工厂实例都可以接入。

方式三:编程式构建引擎

不使用 Spring/XML 时,可以通过ProcessEngineConfiguration的 setter 方法在代码中完成同样配置:

ProcessEngine processEngine = ProcessEngineConfiguration .createProcessEngineConfigurationFromResourceDefault() .setJpaPersistenceUnitName("flowable-pu") .buildProcessEngine();

四个核心配置属性说明

属性类型说明
jpaPersistenceUnitNameString要使用的 persistence-unit 名称,persistence-unit 需在 classpath 上(默认位置/META-INF/persistence.xml)。与jpaEntityManagerFactory二选一
jpaEntityManagerFactoryjakarta.persistence.EntityManagerFactory由外部创建并注入的EntityManagerFactory引用,用于加载实体与刷新(flush)更新。与jpaPersistenceUnitName二选一
jpaHandleTransactionboolean指示引擎是否在使用的EntityManager实例上自行 begin/commit/rollback 事务。使用 JTA(Java Transaction API)时应设为 false
jpaCloseEntityManagerboolean指示引擎是否关闭从EntityManagerFactory获取的EntityManager实例。EntityManager由容器管理时(例如使用不受单个事务作用域限制的 Extended Persistence Context)应设为 false

这四个属性在引擎初始化阶段被装配进EntityManagerSessionFactory(见 ProcessEngineConfigurationImpl.java):当jpaPersistenceUnitName非空时先用它创建工厂,随后将工厂连同jpaHandleTransactionjpaCloseEntityManager一起注册为引擎的EntityManagerSession会话工厂,供变量存取环节按需获取EntityManager

使用示例:一步步理解 JPA 变量读写

官方文档以JPAVariableTest.testUpdateJPAEntityValues为例(完整源码见 JPAVariableTest.java),演示实体变量的完整生命周期。这里按步骤拆解。

第一步:准备实体类与持久化单元

测试使用的实体非常简单,包含一个id和一个字符串value属性,两者都持久化。注意其@Id注解标注在字段上(字段访问方式):

@Entity(name = "JPA_ENTITY_FIELD") public class FieldAccessJPAEntity { @Id @Column(name = "ID_") private Long id; private String value; public FieldAccessJPAEntity() { // Empty constructor needed for JPA } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } }

测试启动前会先创建EntityManagerFactory(基于META-INF/persistence.xml中的 persistence-unit,其中声明了要纳入持久化单元的实体类与厂商配置),并提前在数据库中持久化一个entityToUpdate实例(id=3)。

第二步:将实体作为变量启动流程实例

启动流程时把实体放入变量 Map。与其他变量一样,实体引用信息会存入引擎自身的持久化存储中;当变量再次被请求时,引擎会根据存储的类名和主键从EntityManager中重新加载实体

Map<String, Object> variables = new HashMap<String, Object>(); variables.put("entityToUpdate", entityToUpdate); ProcessInstance processInstance = runtimeService.startProcessInstanceByKey( "UpdateJPAValuesProcess", variables);

第三步:在 ServiceTask 中通过表达式修改实体

流程定义的第一个节点是一个 ServiceTask,通过flowable:expression调用变量上的方法setValue。这里的entityToUpdate会解析为启动流程时设置的 JPA 变量,并从引擎上下文关联的EntityManager中加载:

<serviceTask id='theTask' name='updateJPAEntityTask' flowable:expression="${entityToUpdate.setValue('updatedValue')}" />

第四步:flush 后读取最新实体

ServiceTask 完成后,流程实例会在一个 userTask 处等待。此时EntityManager已被 flush,实体的修改已推送到数据库。随后通过runtimeService.getVariable再次读取变量时,实体被重新加载,value属性已经是updatedValue

// Servicetask in process 'UpdateJPAValuesProcess' should have set value on entityToUpdate. Object updatedEntity = runtimeService.getVariable(processInstance.getId(), "entityToUpdate"); assertTrue(updatedEntity instanceof FieldAccessJPAEntity); assertEquals("updatedValue", ((FieldAccessJPAEntity)updatedEntity).getValue());

值得补充的是,仓库测试还覆盖了更多变量操作边界:testStoreJPAEntityAsVariable验证了实体变量可被置为null后再恢复、以及 12 种主键类型全部可用;testStoreJPAEntityListAsVariable验证了实体列表也可以作为变量存取;testReplaceExistingJPAEntityWithAnotherOfSameType验证了同一变量可被替换为同类型的另一实体。列表变量的内部实现会将每个元素按实体引用解析,因此列表必须是“纯 JPA 实体列表”,混入不可序列化的非 JPA 对象会抛出FlowableException

查询带 JPA 实体变量的流程实例

可以通过ProcessInstanceQueryExecutionQuery查询“某个变量的值等于指定 JPA 实体”的流程实例或执行实例:

ProcessInstance result = runtimeService.createProcessInstanceQuery() .variableValueEquals("entityToQuery", entityToQuery).singleResult();

需要注意限制:对 JPA 实体变量,查询仅支持variableValueEquals(name, entity)一种操作符。variableValueNotEqualsvariableValueGreaterThanvariableValueGreaterThanOrEqualvariableValueLessThanvariableValueLessThanOrEqual均不支持——当传入 JPA 实体作为值时,这些方法会抛出FlowableException

这一点在 JPAVariableTest.java 的testQueryJPAVariable用例中得到完整验证:同类型不同主键的实体查询不到结果;五种不支持的比较操作符全部抛出FlowableIllegalArgumentException,消息为 “JPA entity variables can only be used in 'variableValueEquals'”。

高级示例:Spring Bean + JPA 的贷款审批流程

JPASpringTest展示了 JPA 与 Spring Bean 组合的更复杂场景,其对应源码位于 flowable-spring 测试模块(见 LoanRequestBean.java、LoanRequest.java、JpaTest.java)。场景如下:

  • 已存在一个使用 JPA 实体存储贷款申请(LoanRequest)的 Spring Bean;
  • 借助 Flowable,可以直接把通过既有 Bean 获取的实体作为流程变量使用,整个流程由以下步骤构成:
    1. ServiceTask 创建贷款申请:调用既有的LoanRequestBean,使用启动流程时传入的变量(例如来自启动表单的customerNameamount),并通过flowable:resultVariable把表达式结果存为流程变量loanRequest
    2. UserTask 人工审批:经理审查申请并批准/拒绝,结果存为布尔变量approvedByManager
    3. ServiceTask 同步实体状态:把审批结果写回贷款申请实体,使实体与流程状态保持一致;
    4. 排他网关决策:根据实体属性approved决定后续路径——批准则流程结束,否则多出一个“发送拒信”的人工任务,以便人工通知客户。

需要说明的是,该流程仅用于单元测试,因此没有包含任何表单。完整流程定义如下:

<?xml version="1.0" encoding="UTF-8"?> <definitions id="taskAssigneeExample" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:flowable="http://flowable.org/bpmn" targetNamespace="org.flowable.examples"> <process id="LoanRequestProcess" name="Process creating and handling loan request"> <startEvent id='theStart' /> <sequenceFlow id='flow1' sourceRef='theStart' targetRef='createLoanRequest' /> <serviceTask id='createLoanRequest' name='Create loan request' flowable:expression="${loanRequestBean.newLoanRequest(customerName, amount)}" flowable:resultVariable="loanRequest"/> <sequenceFlow id='flow2' sourceRef='createLoanRequest' targetRef='approveTask' /> <userTask id="approveTask" name="Approve request" /> <sequenceFlow id='flow3' sourceRef='approveTask' targetRef='approveOrDissaprove' /> <serviceTask id='approveOrDissaprove' name='Store decision' flowable:expression="${loanRequest.setApproved(approvedByManager)}" /> <sequenceFlow id='flow4' sourceRef='approveOrDissaprove' targetRef='exclusiveGw' /> <exclusiveGateway id="exclusiveGw" name="Exclusive Gateway approval" /> <sequenceFlow id="endFlow1" sourceRef="exclusiveGw" targetRef="theEnd"> <conditionExpression xsi:type="tFormalExpression">${loanRequest.approved}</conditionExpression> </sequenceFlow> <sequenceFlow id="endFlow2" sourceRef="exclusiveGw" targetRef="sendRejectionLetter"> <conditionExpression xsi:type="tFormalExpression">${!loanRequest.approved}</conditionExpression> </sequenceFlow> <userTask id="sendRejectionLetter" name="Send rejection letter" /> <sequenceFlow id='flow5' sourceRef='sendRejectionLetter' targetRef='theOtherEnd' /> <endEvent id='theEnd' /> <endEvent id='theOtherEnd' /> </process> </definitions>

对应的 Spring Bean 实现(节选)如下,它通过@PersistenceContext注入EntityManager,并在@Transactional方法中创建并持久化贷款申请:

public class LoanRequestBean { @PersistenceContext private EntityManager entityManager; @Transactional public LoanRequest newLoanRequest(String customerName, Long amount) { LoanRequest lr = new LoanRequest(); lr.setCustomerName(customerName); lr.setAmount(amount); lr.setApproved(false); entityManager.persist(lr); return lr; } public LoanRequest getLoanRequest(Long id) { return entityManager.find(LoanRequest.class, id); } }

这个例子虽然简单,却充分展示了 JPA + Spring + 带参数方法表达式组合的威力:整个流程除既有 Spring Bean 外不需要编写任何自定义 Java 代码,实体创建、状态同步、网关决策全部通过 BPMN 表达式完成,可以极大加速业务流程的开发。

小结与最佳实践

  • JPA 实体变量让“流程编排”与“领域模型”直接打通,实体引用 + 主键的存储方式保证了读取时总能拿到数据库最新状态;
  • 配置时务必在jpaPersistenceUnitNamejpaEntityManagerFactory之间二选一;jpaHandleTransactionjpaCloseEntityManager的取值要依据事务边界与容器管理方式(JTA / Extended Persistence Context)谨慎决定;
  • 实体主键必须是@Id标注的单值主键,且类型须在规范允许范围内;布尔型主键与复合主键会直接报错;
  • 对 JPA 实体变量的查询仅支持variableValueEquals,其余比较操作符会抛出异常;
  • 在生产中建议优先使用 Spring 注入的EntityManagerFactory,将事务交由 Spring 事务管理器统一管理,jpaHandleTransaction设为false,以获得一致的事务语义。

相关参考资源:官方文档原文见 ch09-JPA.md;实体变量全部用例见 JPAVariableTest.java;Spring 集成用例见 JpaTest.java 及同目录下的LoanRequest.javaLoanRequestBean.java

【免费下载链接】flowable-engineA compact and highly efficient workflow and Business Process Management (BPM) platform for developers, system admins and business users.项目地址: https://gitcode.com/GitHub_Trending/fl/flowable-engine

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 19:21:12

智能农业的融合之道:从数据孤岛到种植决策闭环

去年参观一个300亩的设施农业园区&#xff0c;负责人给我看他手机里装的四个管理App——水肥一体化、气象站、虫情测报、牛舍监控各一个&#xff0c;互不相通。他说设备没少花钱&#xff0c;但每天还是要靠人把数据抄来抄去&#xff0c;病虫害预警推送到手机上也不知道该不该信…

作者头像 李华
网站建设 2026/9/16 19:19:15

基于DSP的车牌识别系统:ERFilter、SVM与ANN的嵌入式实现

简介&#xff1a;基于DSP的车牌识别系统完整源码包&#xff0c;内含可直接使用的工程文件与配套说明&#xff0c;并对新能源绿牌识别做了扩展。项目覆盖车牌定位、字符分割、特征提取与识别等关键流程&#xff0c;SVM分类器与ANN网络相关模型、字符映射表、XML配置等一应俱全&a…

作者头像 李华
网站建设 2026/9/16 19:18:39

包络谱分析:轴承故障诊断原理与MATLAB实现方法

简介&#xff1a;面向机械故障诊断与信号处理领域的工程师、研究生及高年级本科生&#xff0c;这份Matlab源码包聚焦包络谱轴承故障诊断&#xff0c;提供一套可直接运行的完整分析流程。资源共8个文件&#xff0c;压缩包仅1.14MB&#xff0c;包含7个.m脚本和1个.mat实测振动数据…

作者头像 李华