工作流中的数据持久化详解!Activiti框架中JPA的使用分析

Activiti中JPA简介
- 可以使用JPA实体作为流程变量, 并进行操作:
- 基于流程变量更新已有的JPA实体,可以在用户任务的表单中填写或者由服务任务生成
- 重用已有的领域模型,不需要编写显示的服务获取实体或者更新实体的值
- 根据已有实体的属性做出判断(网关即分支聚合)
JPA实体要求
- Activiti中JPA只支持符合以下要求的实体:
- 实体应该使用JPA注解进行配置, 支持字段和属性访问两种方式.@MappedSuperclass也要能够被使用
- 实体中应该有一个使用@Id注解的主键,不支持复合主键@EmbeddedId 和 @IdClass:
- Id字段或者属性能够使用JPA规范支持的任意类型:
- 原生态数据类型和他们的包装类型(Boolean除外)
- String
- BigInteger
- BigDecimal
- java.util.Date
- java.sql.Date
- Id字段或者属性能够使用JPA规范支持的任意类型:
JPA配置
- 引擎必须有一个对EntityManagerFactory的引用才能够使用JPA的实体,这样可以通过配置引用或者提供一个持久化单元名称
- 作为变量的JPA实体将会被自动检测并进行相应的处理
- 使用jpaPersistenceUnitName配置:
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration">
<!-- 数据库的配置 -->
<property name="databaseSchemaUpdate" value="true" />
<property name="jdbcUrl" value="jdbc:h2:mem:JpaVariableTest;DB_CLOSE_DELAY=1000" />
<property name="jpaPersistenceUnitName" value="activiti-jpa-pu" />
<property name="jpaHandleTransaction" value="true" />
<property name="jpaCloseEntityManager" value="true" />
<!-- job executor configurations -->
<property name="jobExecutorActivate" value="false" />
<!-- mail server configurations -->
<property name="mailServerPort" value="5025" />
</bean>
- 配置一个自定义的EntityManagerFactory,
- 这里使用了OpenJPA实体管理器
- 该代码片段仅仅包含与例子相关的beans,去掉了其他beans.
- OpenJPA实体管理的完整并可以使用的例子可以在activiti-spring-examples(/activiti-spring/src/test/java/org/activiti/spring/test/jpa/JPASpringTest.java) 中找到
<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.activiti.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="jobExecutorActivate" value="false" />
</bean>
- 也可以在编程式创建一个引擎时完成配置:
ProcessEngine processEngine = ProcessEngineConfiguration
.createProcessEngineConfigurationFromResourceDefault()
.setJpaPersistenceUnitName("activiti-pu")
.buildProcessEngine();
配置的属性有:
- jpaPersistenceUnitName: 使用持久化单元的名称:
- 要确保该持久化单元在类路径下是可用的,默认的路径是 /META-INF/persistence.xml
- 要么使用jpaEntityManagerFactory要么或者是jpaPersistenceUnitName
- jpaEntityManagerFactory: 一个实现了javax.persistence.EntityManagerFactory的bean的引用:
- 将被用来加载实体并且刷新更新
- 要么使用jpaEntityManagerFactory要么或者是jpaPersistenceUnitName
- jpaHandleTransaction: 在被使用的EntityManager实例上,该标记表示流程引擎是否需要开始和提交或者回滚事务:
- 当使用Java事务API(JTA) 时,设置为false
- jpaCloseEntityManager: 该标记表示流程引擎是否应该关闭从 EntityManagerFactory获取的EntityManager的实例:
- 当EntityManager是由容器管理的时候需要设置为false: 当使用并不是单一事务作用域的扩展持久化上下文的时候
JPA用法
简单示例
- 首先,需要创建一个基于META-INF/persistence.xml的EntityManagerFactory作为持久化单元:包含持久化单元中所有的类和一些供应商特定的配置
- 使用一个简单的实体作为测试,其中包含有一个id和String类型的value属性,也将会被持久化
- 在测试之前,创建一个实体并且保存:
@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;
}
}
- 启动一个新的流程实例,添加一个实体作为变量. 其他的变量,将会被存储在流程引擎的持久化数据库中.下一次获取该变量的时候,将会根据该类和存储Id从EntityManager中加载:
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("entityToUpdate", entityToUpdate);
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("UpdateJPAValuesProcess", variables);
- 流程定义中的第一个节点是一个服务任务,将会调用entityToUpdate上的setValue方法,其实就是之前在启动流程实例时候设置的JPA变量并且将会从当前流程引擎的上下文关联的EntityManager中加载:
<serviceTask id='theTask' name='updateJPAEntityTask' activiti:expression="${entityToUpdate.setValue('updatedValue')}" />
- 当完成服务任务时,流程实例将会停留在流程定义中定义的用户任务环节上:
- 可以查看该流程实例
- EntityManager已经被刷新了并且改变的实体已经被保存进数据库中
- 获取entityToUpdate的变量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())
查询JPA流程变量
- 以查询某一JPA实体作为变量的ProcessInstances和Executions
- 在ProcessInstanceQuery和ExecutionQuery查询中仅仅variableValueEquals(name, entity) 支持JPA实体变量:
- [variableValueNotEquals],[variableValueGreaterThan],[variableValueGreaterThanOrEqual],[variableValueLessThan],[variableValueLessThanOrEqual]不被支持并且传递JPA实体值的时候会抛出一个ActivitiException
ProcessInstance result = runtimeService.createProcessInstanceQuery().variableValueEquals("entityToQuery", entityToQuery).singleResult();
使用Spring beans和JPA结合
- JPASpringTest, 在activiti-spring-examples中:
- 已经存在了一个使用JPA实体的Spring-bean, 用来存储贷款申请
- 使用Activiti,可以通过已经存在的bean获取已经使用的实体,并使用它作为变量用于流程中
- 流程定义步骤:
- 服务任务:
- 创建一个新的贷款申请,使用已经存在的LoanRequestBean接受启动流程时候的变量(来自流程启动时候的表单)
- 使用activiti:resultVariable(作为一个变量对表达式返回的结果进行存储)将创建出来的实体作为变量进行存储
- 用户任务:
- 允许经理查看贷款申请,并填入审批意见(同意/不同意)
- 审批意见将作为一个boolean变量approvedByManager进行存储
- 服务任务:
- 更新贷款申请实体,因此该实体与流程保持同步
- 根据贷款申请实体变量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:activiti="http://activiti.org/bpmn"
targetNamespace="org.activiti.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'
activiti:expression="${loanRequestBean.newLoanRequest(customerName, amount)}"
activiti: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'
activiti: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>
上面的例子展示了JPA结合Spring和参数化方法表达式的强大优势 :所有的流程就不需要自定义java代码(Spring bean除外),大幅度的加快了流程部署
工作流中的数据持久化详解!Activiti框架中JPA的使用分析的更多相关文章
- iOS学习之数据持久化详解
前言 持久存储是一种非易失性存储,在重启设备时也不会丢失数据.Cocoa框架提供了几种数据持久化机制: 1)属性列表: 2)对象归档: 3)iOS的嵌入式关系数据库SQLite3: 4)Core Da ...
- C++中static数据成员详解
本文和大家分享的主要是c++中static数据成员的相关用法及源码示例,希望能帮助大家更好的学习C++. static(静态存储)数据成员 StaticTest.cpp : 定义控制台应用程序 ...
- 详解SSH 框架中对象调用流程
摘要:SSH=Struts+Spring+Hibernate SSH不是一个框架,而是多个框架(struts+spring+hibernate)的集成,是目前较流行的一种Web应用程序开源集成框架,用 ...
- Activiti工作流学习之流程图应用详解
Activiti工作流学习之流程图应用详解 1.目的 了解Activiti工作流是怎样应用流程图的. 2.环境准备2.1.相关软件及版本 jdk版本:Jdk1.7及以上 IDE:eclipse ...
- iOS中MVC等设计模式详解
iOS中MVC等设计模式详解 在iOS编程,利用设计模式可以大大提高你的开发效率,虽然在编写代码之初你需要花费较大时间把各种业务逻辑封装起来.(事实证明这是值得的!) 模型-视图-控制器(MVC)设计 ...
- Redis AOF 持久化详解
Redis 是一种内存数据库,将数据保存在内存中,读写效率要比传统的将数据保存在磁盘上的数据库要快很多.但是一旦进程退出,Redis 的数据就会丢失. 为了解决这个问题,Redis 提供了 RDB 和 ...
- c++中vector的用法详解
c++中vector的用法详解 vector(向量): C++中的一种数据结构,确切的说是一个类.它相当于一个动态的数组,当程序员无法知道自己需要的数组的规模多大时,用其来解决问题可以达到最大节约空间 ...
- 011-Scala中的apply实战详解
011-Scala中的apply实战详解 object中的apply方法 class中的apply方法 使用方法 apply方法可以应用在类或者Object对象中 class类 必须要创建实例化的类对 ...
- java中的io系统详解 - ilibaba的专栏 - 博客频道 - CSDN.NET
java中的io系统详解 - ilibaba的专栏 - 博客频道 - CSDN.NET 亲,“社区之星”已经一周岁了! 社区福利快来领取免费参加MDCC大会机会哦 Tag功能介绍—我们 ...
随机推荐
- 09- monkey命令详解
安装模拟器 如果你没有手机可以安装个模拟器,如果有手机了,忽略此步骤. 1.下载模拟器:http://www.xyaz.cn/ 2.安装:直接默认安装. 3.打开安卓模拟器,设置-关于手机-点击版本号 ...
- 100多个很有用的JavaScript函数以及基础写法大集合
100多个很有用的JavaScript函数以及基础写法大集合 1.document.write("");为 输出语句2.JS中的注释为//3.传统的HTML文档顺序是:docume ...
- C++处理char*,char[],string三种类型间的转换
前言 在C和C++中,有一个相当重要的部分,就是字符串的编程描述.在学C的时候,很多人习惯了char[],char*表示法,直到遇见了C++后,出现了第三者:string.这时候,很多初学者就会在这三 ...
- adbi学习:java hook实现机制
adbi的java hook实现代码ddi不在之前下载的文件中,下载地址:https://github.com/crmulliner/ddi,具体的编译看readme里面很详细的介绍了.注意ddi代码 ...
- CVE-2012-0774:Adobe Reader TrueType 字体整数溢出漏洞调试分析
0x01 TrueType 字体 TTF 字体是 Apple 和 Microsoft 两家公司共同推出的字体格式,现在已经广泛的运用于 Windows 操作系统,其中 PDF 文档也可以嵌入 TTF ...
- [CTF]盲文对照表
[CTF]盲文对照表 摘自:https://wenku.baidu.com/view/28b04fd380eb6294dd886ca7.html 学点盲文 盲文又称点字,国际通用的点字由6个凸起的圆点 ...
- 小技巧!CSS 提取图片主题色功能探索
本文将介绍一种利用 CSS 获取图片主题色的小技巧.一起看看~ 背景 起因是微信技术群里有个同学发问,有什么方法能够获取图片的主色呢?有一张图片,获取他的主色调: 利用获取到的这个颜色值,来实现类似这 ...
- ES6新增常用方法
字符串新增方法 padStart.padEnd 如果原字符串不够指定长度,则会在左侧(右侧)填充字符串,用以补全 padStart( length: number, fillStr?: string ...
- Spring MVC工作原理及源码解析(一) MVC原理介绍、与IOC容器整合原理
MVC原理介绍 Spring MVC原理图 上图是Spring MVC工作原理图(图片来自网上搜索),根据上图,我们可以得知Spring MVC的工作流程如下: 1.用户(客户端,即浏览器)发送请求至 ...
- 解决Latex输出PDF纸张自适应大小及中文无法显示问题
遗留的问题 之前我们进行了基于texlive定制chemfig化学式转换Python服务镜像,虽然完成pdf的输出服务改造,但是输出效果并不是太好,如下图: 这个图有两个比较严重问题 不支持中文 空白 ...