Flowable實戰(六)整合JPA

金楊傑發表於2022-01-13

  上文提到,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審批。

  請假流程圖示:

  流程定義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就看到一條資料:

  我們再來觀察執行時變數表:

  可以看到變數“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

  檢視請假表資料:

  同樣變數表中的值也被修改。

  上面我們只是設定了變數值,沒有修改資料庫,為什麼就達到了修改實體屬性的目的呢?這是因為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的繼承,把表單資料儲存到自定義的表中,不僅把原來“無結構”的資料轉換為“有結構”的資料,還減少了變數表的資料量,提高了資料的查詢、使用效率。

相關文章