Flowable actual combat integration JPA

  as mentioned above, all the form data of Flowable are saved in one table (act_hi_variables). With the passage of time, there are more and more data in the table. In addition, the data structure is not optimized, and the query efficiency will be lower and lower.

  in Flowable, the above problems can be solved by integrating JPA. JPA saves form data in user-defined tables, which is conducive to query optimization.

1, What is JPA

   JPA is the abbreviation of Java Persistence API and its Chinese name is java persistence layer API. It is the mapping relationship between JDK 5.0 annotation or XML description object and relational table, and persistence the entity object at run time to the database.

  JPA has been widely used in most systems. More and more open source frameworks have released their own JPA implementations, such as Hibernate, Open JPA, Spring Data, etc.

2, JPA support

  in Springboot, add JPA support for Flowable and add the following dependencies:

    <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>

  this will add Spring configuration and bean s for JPA. Hibernate is used as the JPA provider by default.

Note: JPA is only an interface specification without specific implementation, and does not conflict with the ORM framework MyBatis used by Flowable.

  application in classpath The following parameters are added to the properties file to automatically create database tables.

    spring.jpa.hibernate.ddl-auto=update

  in addition, it is recommended to refer to lombok package, which can save us the work of entity class writing Getter and Setter methods.

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.8</version>
    </dependency>

3, Leave process in JPA version

3.1 simple leave process

  we take a simple leave process as an example to illustrate the specific use of JPA. The leave instance has only one user task. The user fills in the form data, initiates a leave process instance, and then submits it to the Department Manager leader for approval.

  leave process diagram:

  process definition 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">
            <extensionElements>
                <flowable:executionListener event="start" expression="${execution.setVariable('leave', leaveEntityManager.newLeave(execution))}}">
                </flowable:executionListener>
            </extensionElements>
        </startEvent>
        <sequenceFlow sourceRef="theStart" targetRef="theLeaderApprove" />

        <userTask id="theLeaderApprove" name="Department manager approval" flowable:candidateGroups="leader">
            <extensionElements>
                <flowable:taskListener event="complete" expression="${leave.setLeaderApproved(leaderApproved)}">
                </flowable:taskListener>
            </extensionElements>
        </userTask>
        <sequenceFlow sourceRef="theLeaderApprove" targetRef="theEnd" />

        <endEvent id="theEnd" >
            <extensionElements>
                <flowable:executionListener event="end" delegateExpression="${leaveEndListener}">
                </flowable:executionListener>
            </extensionElements>
        </endEvent>
    </process>

</definitions>

  leave form form

{
    "key": "leave",
    "name": "Leave process",
    "fields": [
    {
        "id": "startTime",
        "name": "start time",
        "type": "date",
        "required": true,
        "placeholder": "empty"

    },
    {
        "id": "endTime",
        "name": "End time",
        "type": "date",
        "required": true,
        "placeholder": "empty"
    },
    {
        "id": "reason",
        "name": "Reason for leave",
        "type": "text",
        "required": true,
        "placeholder": "empty"
    }
]
}

3.2 persistent JPA entities when starting a process

  define a leave application form class

@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;
}

Note: the Flowable form type "date" maps to org joda. time. The localdate class is not the Java. Date class that comes with JDK8 time. Localdate class.

  configure a start listener in the process to read the form content filled in by the user, create an entity class object and persist it to the database.

  modify XML content:

    <startEvent id="theStart" flowable:formKey="leave">
        <extensionElements>
            <flowable:executionListener event="start" expression="${execution.setVariable('leave', leaveEntityManager.newLeave(execution))}}">
            </flowable:executionListener>
        </extensionElements>
    </startEvent>

  add an entity manager to map form data into entity classes and store them together.

@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;
    }
}

  the following shows the specific code for filling in the form and starting the process.

  Service layer code:

@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", "There's something at home");
        formProp.put("startTime", LocalDate.now());
        formProp.put("endTime", LocalDate.now());
        String outcome = "outStr";
        runtimeService.startProcessInstanceWithForm(proDefId, outcome, formProp, "Form task");
    }
}

  Controller layer code:

@RequestMapping("/jpa")
@RestController
public class jpaController {

    @Autowired
    private jpaService myService;

    @RequestMapping(value="/process", method= RequestMethod.POST)
    public void startProcessInstance() {
        myService.startProcess();
    }
}

   after starting the application, use cURL to test:

    curl http://localhost:8080/jpa/process

  in this way, the data table event is queried after the process is started_ Leave will see a piece of data:

  let's look at the runtime variable table:
Variable table png

   you can see that the TYPE field (TYPE -) of the variable "leave" is "JPA entity", and the "TEXT -" and "TEXT2 -" fields of the record represent the complete class name and primary key ID of the entity respectively.

3.3 changing JPA entity attributes

   when the process is running, if the user fills in the task form during processing, the changed data needs to be updated to the entity, such as saving the approval comments when the department leader approves the node.

  similarly, add a complete listener on the user task.

  modify XML content:

    <userTask id="theLeaderApprove" name="Department manager approval" flowable:candidateGroups="leader">
         <extensionElements>
             <flowable:taskListener event="complete" expression="${leave.setLeaderApproved(leaderApproved)}">
             </flowable:taskListener>
         </extensionElements>
    </userTask>

  adding method of Service layer:

    @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);
    }

  adding method of Controller layer:

    @RequestMapping(value="/complete", method= RequestMethod.GET, produces= MediaType.APPLICATION_JSON_VALUE)
    public void complete(@RequestParam String groupName) {
        myService.complete(groupName);
    }

  test with cURL:

    http://localhost:8080/jpa/complete?groupName=leader

  view leave form data:
Leave form 2 png

  similarly, the values in the variable table are modified.

    above, we only set the variable value without modifying the database. Why did we achieve the purpose of modifying the entity attribute? This is because Springboot has configured the transaction manager for us, that is, Springboot takes over the Flowable transaction. When the entity attribute is changed and the transaction is committed, the database update operation is automatically executed.

3.4 clearing historical form data

    now we have successfully saved the form data in the user-defined table, but there is another problem that has not been solved, that is, delete the corresponding data of the historical variable table, reduce the size of the historical variable table and improve the query efficiency.

  similarly, we set up an end listener to clean up the historical form data.

  modify XML content:

    <endEvent id="theEnd" >
        <extensionElements>
            <flowable:executionListener event="end" delegateExpression="${leaveEndListener}">
            </flowable:executionListener>
        </extensionElements>
    </endEvent>

   leaveEndListener is a service class, which puts the history variable table act_ hi_ Delete the corresponding variable data in 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();
    }
}

4, Summary

   this article introduces the inheritance of Flowable and JPA in detail. It saves the form data into a user-defined table, which not only converts the original "unstructured" data into "structured" data, but also reduces the amount of data in the variable table and improves the efficiency of data query and use.

Keywords: npm

Added by kjtocool on Thu, 13 Jan 2022 18:25:08 +0200