工作流中的數據持久化詳解!Activiti框架中JPA的使用分析


Activiti中JPA簡介

  • 可以使用JPA實體作為流程變量, 並進行操作:
    • 基於流程變量更新已有的JPA實體,可以在用戶任務的表單中填寫或者由服務任務生成
    • 重用已有的領域模型,不需要編寫顯示的服務獲取實體或者更新實體的值
    • 根據已有實體的屬性做出判斷(網關即分支聚合)

JPA實體要求

  • Activiti中JPA只支持符合以下要求的實體:
    • 實體應該使用JPA注解進行配置, 支持字段和屬性訪問兩種方式.@MappedSuperclass也要能夠被使用
    • 實體中應該有一個使用@Id注解的主鍵,不支持復合主鍵@EmbeddedId 和 @IdClass:
      • Id字段或者屬性能夠使用JPA規范支持的任意類型:
        • 原生態數據類型和他們的包裝類型(Boolean除外)
        • String
        • BigInteger
        • BigDecimal
        • java.util.Date
        • java.sql.Date

JPA配置

  • 引擎必須有一個對EntityManagerFactory的引用才能夠使用JPA的實體,這樣可以通過配置引用或者提供一個持久化單元名稱
  • 作為變量的JPA實體將會被自動檢測並進行相應的處理
  • 使用jpaPersistenceUnitName配置:
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration">

    <!-- 數據庫的配置 -->
    <property name="databaseSchemaUpdate" value="true" />
    <property name="jdbcUrl" value="jdbc:h2:mem:JpaVariableTest;DB_CLOSE_DELAY=1000" />

    <property name="jpaPersistenceUnitName" value="activiti-jpa-pu" />
    <property name="jpaHandleTransaction" value="true" />
    <property name="jpaCloseEntityManager" value="true" />

    <!-- job executor configurations -->
    <property name="jobExecutorActivate" value="false" />

    <!-- mail server configurations -->
    <property name="mailServerPort" value="5025" />
</bean>
  • 配置一個自定義的EntityManagerFactory,
    • 這里使用了OpenJPA實體管理器
    • 該代碼片段僅僅包含與例子相關的beans,去掉了其他beans.
    • OpenJPA實體管理的完整並可以使用的例子可以在activiti-spring-examples(/activiti-spring/src/test/java/org/activiti/spring/test/jpa/JPASpringTest.java) 中找到
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="persistenceUnitManager" ref="pum"/>
  <property name="jpaVendorAdapter">
    <bean class="org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter">
      <property name="databasePlatform" value="org.apache.openjpa.jdbc.sql.H2Dictionary" />
    </bean>
  </property>
</bean>

<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
  <property name="dataSource" ref="dataSource" />
  <property name="transactionManager" ref="transactionManager" />
  <property name="databaseSchemaUpdate" value="true" />
  <property name="jpaEntityManagerFactory" ref="entityManagerFactory" />
  <property name="jpaHandleTransaction" value="true" />
  <property name="jpaCloseEntityManager" value="true" />
  <property name="jobExecutorActivate" value="false" />
</bean>
  • 也可以在編程式創建一個引擎時完成配置:
ProcessEngine processEngine = ProcessEngineConfiguration
  .createProcessEngineConfigurationFromResourceDefault()
  .setJpaPersistenceUnitName("activiti-pu")
  .buildProcessEngine();

配置的屬性有:

  • jpaPersistenceUnitName: 使用持久化單元的名稱:
    • 要確保該持久化單元在類路徑下是可用的,默認的路徑是 /META-INF/persistence.xml
    • 要么使用jpaEntityManagerFactory要么或者是jpaPersistenceUnitName
  • jpaEntityManagerFactory: 一個實現了javax.persistence.EntityManagerFactorybean的引用:
    • 將被用來加載實體並且刷新更新
    • 要么使用jpaEntityManagerFactory要么或者是jpaPersistenceUnitName
  • jpaHandleTransaction: 在被使用的EntityManager實例上,該標記表示流程引擎是否需要開始和提交或者回滾事務:
    • 當使用Java事務API(JTA) 時,設置為false
  • jpaCloseEntityManager: 該標記表示流程引擎是否應該關閉從 EntityManagerFactory獲取的EntityManager的實例:
    • EntityManager是由容器管理的時候需要設置為false: 當使用並不是單一事務作用域的擴展持久化上下文的時候

JPA用法

簡單示例

  • 首先,需要創建一個基於META-INF/persistence.xmlEntityManagerFactory作為持久化單元:包含持久化單元中所有的類和一些供應商特定的配置
  • 使用一個簡單的實體作為測試,其中包含有一個idString類型的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;
  }
}
  • 啟動一個新的流程實例,添加一個實體作為變量. 其他的變量,將會被存儲在流程引擎的持久化數據庫中.下一次獲取該變量的時候,將會根據該類和存儲IdEntityManager中加載:
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("entityToUpdate", entityToUpdate);

ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("UpdateJPAValuesProcess", variables);
  • 流程定義中的第一個節點是一個服務任務,將會調用entityToUpdate上的setValue方法,其實就是之前在啟動流程實例時候設置的JPA變量並且將會從當前流程引擎的上下文關聯的EntityManager中加載:
<serviceTask id='theTask' name='updateJPAEntityTask' activiti:expression="${entityToUpdate.setValue('updatedValue')}" />
  • 當完成服務任務時,流程實例將會停留在流程定義中定義的用戶任務環節上:
    • 可以查看該流程實例
    • 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())

查詢JPA流程變量

  • 以查詢某一JPA實體作為變量的ProcessInstancesExecutions
  • ProcessInstanceQueryExecutionQuery查詢中僅僅variableValueEquals(name, entity) 支持JPA實體變量:
    • [variableValueNotEquals],[variableValueGreaterThan],[variableValueGreaterThanOrEqual],[variableValueLessThan],[variableValueLessThanOrEqual]不被支持並且傳遞JPA實體值的時候會拋出一個ActivitiException
ProcessInstance result = runtimeService.createProcessInstanceQuery().variableValueEquals("entityToQuery", entityToQuery).singleResult();   

使用Spring beans和JPA結合

  • JPASpringTest, 在activiti-spring-examples中:
    • 已經存在了一個使用JPA實體的Spring-bean, 用來存儲貸款申請
    • 使用Activiti,可以通過已經存在的bean獲取已經使用的實體,並使用它作為變量用於流程中
  • 流程定義步驟:
    • 服務任務:
      • 創建一個新的貸款申請,使用已經存在的LoanRequestBean接受啟動流程時候的變量(來自流程啟動時候的表單)
      • 使用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>

上面的例子展示了JPA結合Spring和參數化方法表達式的強大優勢 :所有的流程就不需要自定義java代碼(Spring bean除外),大幅度的加快了流程部署


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM