activiti-5.x-user-guide

10.3. Usage 用法

10.3.1. 简单例子

使用 JPA 变量的例子可以在 JPAVariableTest 中找到。我们将会一步一步的解释 JPAVariableTest.testUpdateJPAEntityValues

首先,我们需要创建一个基于 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);

在我们的流程定义的第一个节点包含一个 serviceTask 将调用方法 在 entityToUpdatesetValue,它解析为 JPA 变量,我们启动流程实例并将从相关联的当前引擎的上下文“EntityManager”进行加载。

<serviceTask id='theTask' name='updateJPAEntityTask'
  activiti:expression="${entityToUpdate.setValue('updatedValue')}" />

当 service-task 完成后,流程实例等在流程定义中定义的 userTask,这使我们能够检查流程实例。在这一点上,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());

10.3.2. 查询 JPA 处理变量

可以查询 ProcessInstancesExecution 包含 JPA 实体作为变量值。注意 只有 在 ProcessInstanceQuery 和 ExecutionQuery ,variableValueEquals(name, entity) 是支持 JPA 实体的 。 方法 variableValueNotEquals, variableValueGreaterThan, variableValueGreaterThanOrEqual, variableValueLessThanvariableValueLessThanOrEqual 不支持,并且当一个 JPA 实体传递作为值时,会抛出 ActivitiException

ProcessInstance result = runtimeService.createProcessInstanceQuery()
    .variableValueEquals("entityToQuery", entityToQuery).singleResult();

10.3.3. 高级例子:使用 Spring bean 和 JPA

JPASpringTest 例子可以在 activiti-spring-examples 中找到。它的使用场景如下:

  • 存在一个使用 JPA 实体的 Spring bean,用于存储贷款请求。
  • 使用 Activiti,我们可以利用那些由现有的 bean 获得的已有的实体,并将其作为变量在流程中使用。 流程是按以下步骤进行定义的:
    • 服务任务,利用已有的 LoanRequestBean 使用启动流程时接收的变量(比如,可以来源于开始的表单)来创建新的 LoanRequest。使用 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>

虽然上面的例子很简单,但确实展示出了结合 Spring 和参数化方法表达式来使用 JPA 的强大。此流程根本不需要定制 java 代码(当然了,除 Spring bean 外),大大加速了部署