上文提到,Flowable所有的表单数据都保存在一张表(act_hi_varinst)中,随着时间的推移,表中数据越来越多,再加上数据没有结构优化,查询使用效率会越来越低。
在Flowable,可以通过集成JPA解决上述问题。JPA把表单数据保存在用户自定义的表中,有利于查询优化。
一、什么是JPA
JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
JPA在大多数系统中已经得到广泛应用,越来越多的开源框架发布了自己的JPA实现,例如Hibernate、Open JPA、Spring Data等。
二、JPA支持
在Springboot中,为Flowable添加JPA支持,增加下列依赖:
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>${flowable.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>${spring.boot.version}</version>
</dependency>
这会加入JPA用的Spring配置以及bean。默认使用Hibernate作为JPA提供者。
注意:JPA只是接口规范,没有具体实现,与Flowable使用的ORM框架MyBatis并无冲突。
在classpath的application.properties文件加入下列参数,自动创建数据库表。
spring.jpa.hibernate.ddl-auto=update
另外,推荐引用lombok包,可以让我们省去实体类写Getter和Setter方法的工作。
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
三、JPA版本的请假流程
3.1 简单的请假流程
我们以一个简单的请假流程为实例说明JPA的具体使用。该请假实例只有一个用户任务,由用户填写表单数据,发起一个请假流程实例,后交由部门经理leader审批。
请假流程图示:
请假流程.png
流程定义leave-process.bpmn20.xml
<?xml version="1.0" encoding="UTF-8"?>
<definitions
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:flowable="http://flowable.org/bpmn"
targetNamespace="Examples">
<process id="leaveProcess" name="The leave Process" >
<startEvent id="theStart" flowable:formKey="leave">
</startEvent>
<sequenceFlow sourceRef="theStart" targetRef="theLeaderApprove" />
<userTask id="theLeaderApprove" name="部门经理审批" flowable:candidateGroups="leader">
</userTask>
<sequenceFlow sourceRef="theLeaderApprove" targetRef="theEnd" />
<endEvent id="theEnd" >
</endEvent>
</process>
</definitions>
请假表单leave.form
{
"key": "leave",
"name": "请假流程",
"fields": [
{
"id": "startTime",
"name": "开始时间",
"type": "date",
"required": true,
"placeholder": "empty"
},
{
"id": "endTime",
"name": "结束时间",
"type": "date",
"required": true,
"placeholder": "empty"
},
{
"id": "reason",
"name": "请假原因",
"type": "text",
"required": true,
"placeholder": "empty"
}
]
}
3.2 启动流程时持久化JPA实体
定义一个请假申请表单类
@Data
@Entity(name="event_leave")
public class LeaveEntity implements Serializable {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
private String processInstanceId;
private LocalDate StartTime;
private LocalDate endTime;
private String reason;
private String leaderApproved;
}
注意:Flowable表单类型“Date”映射的是org.joda.time.LocalDate类,并不是JDK8自带的java.time.LocalDate类。
在流程中配置一个start类型的监听器,作用是读取用户填写的表单内容并创建实体类对象持久化到数据库中。
修改XML内容:
<startEvent id="theStart" flowable:formKey="leave">
<extensionElements>
<flowable:executionListener event="start" expression="${execution.setVariable('leave', leaveEntityManager.newLeave(execution))}}">
</flowable:executionListener>
</extensionElements>
</startEvent>
增加一个实体管理器,将表单数据映射成实体类并存入库。
@Service
public class LeaveEntityManager {
@PersistenceContext
private EntityManager entityManager;
@Transactional
public LeaveEntity newLeave(DelegateExecution execution) {
LeaveEntity leave = new LeaveEntity();
leave.setProcessInstanceId(execution.getProcessInstanceId());
leave.setStartTime((LocalDate)execution.getVariable("startTime"));
leave.setEndTime((LocalDate)execution.getVariable("endTime"));
leave.setReason(execution.getVariable("reason").toString());
entityManager.persist(leave);
return leave;
}
}
下面展示填写表单,启动流程的具体代码。
Service层代码:
@Service
public class jpaService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private RepositoryService repositoryService;
@Transactional
public void startProcess() {
List<ProcessDefinition> processDefinitionList = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("leaveProcess").orderByProcessDefinitionId().desc().list();
String proDefId = processDefinitionList.get(0).getId();
Map<String, Object> formProp = new HashMap();
formProp.put("reason", "家里有事");
formProp.put("startTime", LocalDate.now());
formProp.put("endTime", LocalDate.now());
String outcome = "outStr";
runtimeService.startProcessInstanceWithForm(proDefId, outcome, formProp, "表单任务");
}
}
Controller层代码:
@RequestMapping("/jpa")
@RestController
public class jpaController {
@Autowired
private jpaService myService;
@RequestMapping(value="/process", method= RequestMethod.POST)
public void startProcessInstance() {
myService.startProcess();
}
}
启动应用后,使用cURL测试:
curl http://localhost:8080/jpa/process
这样在流程启动后查询数据表event_leave就看到一条数据:
请假表.png
我们再来观察运行时变量表:
变量表.png
可以看到变量“leave”的类型字段(TYPE-)为“jpa-entity”,该记录的“TEXT-”、“TEXT2-“字段分别代表实体的完整类名和主键ID。
3.3 更改JPA实体属性
在流程运行时,如果用户在办理时填写了任务表单,那么还需要把改动的数据更新到实体中,比如:部门领导审核节点完成时保存审批意见。
同样的,在用户任务上添加一个complete类型的监听器。
修改XML内容:
<userTask id="theLeaderApprove" name="部门经理审批" flowable:candidateGroups="leader">
<extensionElements>
<flowable:taskListener event="complete" expression="${leave.setLeaderApproved(leaderApproved)}">
</flowable:taskListener>
</extensionElements>
</userTask>
Service层增加方法:
@Transactional
public void complete(String groupName) {
List<Task> taskList = taskService.createTaskQuery().taskCandidateGroup(groupName).orderByTaskCreateTime().desc().list();
String taskId = taskList.get(0).getId();
Map<String, Object> param = new HashMap();
param.put("leaderApproved", true);
taskService.complete(taskId, param);
}
Controller层增加方法:
@RequestMapping(value="/complete", method= RequestMethod.GET, produces= MediaType.APPLICATION_JSON_VALUE)
public void complete(@RequestParam String groupName) {
myService.complete(groupName);
}
使用cURL测试:
http://localhost:8080/jpa/complete?groupName=leader
查看请假表数据:
请假表2.png
同样变量表中的值也被修改。
上面我们只是设置了变量值,没有修改数据库,为什么就达到了修改实体属性的目的呢?这是因为Springboot已经帮我们配置了事务管理器,即由Springboot接管了Flowable的事务,当更改实体属性并提交事务时,就自动执行了数据库的update操作。
3.4 清理历史表单数据
现在我们已经成功把表单数据单独保存在用户自定义表中,但还有一个问题没有解决,那就是把历史变量表的对应数据删除,给历史变量表瘦身,提高查询效率。
同样的,我们设置一个end类型的监听器清理历史表单数据。
修改XML内容:
<endEvent id="theEnd" >
<extensionElements>
<flowable:executionListener event="end" delegateExpression="${leaveEndListener}">
</flowable:executionListener>
</extensionElements>
</endEvent>
leaveEndListener是一个service类,内容是把历史变量表act_hi_varinst中对应的变量数据删除。
@Service
@Transactional
class LeaveEndListener implements ExecutionListener {
@PersistenceContext
private EntityManager entityManager;
@Override
public void notify(DelegateExecution execution) {
String processInstanceId = execution.getProcessInstanceId();
String sql = "delete from act_hi_varinst where proc_inst_id_ = ?";
entityManager.createNativeQuery(sql).setParameter(1, processInstanceId).executeUpdate();
}
}
四、小结
本篇详细介绍了Flowable与JPA的继承,把表单数据保存到自定义的表中,不仅把原来“无结构”的数据转换为“有结构”的数据,还减少了变量表的数据量,提高了数据的查询、使用效率。