Flowable实战(六)集成JPA

  上文提到,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的继承,把表单数据保存到自定义的表中,不仅把原来“无结构”的数据转换为“有结构”的数据,还减少了变量表的数据量,提高了数据的查询、使用效率。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,042评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,996评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,674评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,340评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,404评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,749评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,902评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,662评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,110评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,577评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,258评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,848评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,726评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,952评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,271评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,452评论 2 348

推荐阅读更多精彩内容