Chapter 09. JPA
Table of Contents
- Requirements(需求)
- Configuration(配置)
-
- JPA using ProcessEngineBuilder(使用ProcessEngineBuilder的JPA)
- JPA using Spring(使用Sping的JPA)
- Usage(用法)
-
- Simple Example(簡單示例)
- Query JPA process variables(查詢流程變量)
- Advanced example using Spring beans and JPA(使用Spring beans和JPA的高級示例)
You can use JPA-Entities as process variables, allowing you to:
你能使用作為流程變量的JPA-實體,允許:
-
Updating existing JPA-entities based on process variables, that can be filled in on a form in a userTask or generated in a serviceTask.
更新存在基于流程變量的JPA-實體,它被填充在userTask的表單里面,或者在serviceTask里面產生。
-
Reusing existing domain model without having to write explicit services to fetch the entities and update the values
復用已存在的領域模型,不必編寫顯式獲取這些實體和更新這些數值。
-
Make decisions (gateways) based on properties of existing entities.
基于存在實體屬性作決定(網關)
-
...
Requirements(需求)
Only entities that comply to the following are supported:
只支持遵從下面標準的實體:
-
Entities should be configured using JPA-annotations, we support both field and property-access. Mapped super classes can also be used.
應當使用JPA-標注配置實體,我們支持字段和屬性方法。也能夠使用已映射的超類。
-
Entity should have a primary key annotated with @Id
, compound primary keys are not supported (@EmbeddedId
and @IdClass
). The Id field/property can be of any type supported in the JPA-spec: Primitive types and their wrappers (excluding boolean), String
, BigInteger
, BigDecimal
, java.util.Date
and java.sql.Date
.
實體應當具有標注為@Id的主鍵,不支持組合主鍵 (@EmbeddedId
and @IdClass
)。Id 字段/屬性能夠是JPA規范里任何支持的類型:原子類型和它們的包裝類(wrapper)(排除boolean),, String
, BigInteger
, BigDecimal
, java.util.Date
and java.sql.Date
。
Configuration(配置)
To be able to use JPA-entities, the engine must have a reference to an EntityManagerFactory
. When JPA is enabled on the engine, JPA-entities used as variables will be detected automatically and will be handled accordingly.
為了能夠使用JPA-實體,引擎必須具有一個到EntityManagerFactory
的引用。當在引擎上啟用了JPA,作為變量的JPA-實體將被自動檢測并作相應的處理。
JPA using ProcessEngineBuilder(使用ProcessEngineBuilder的JPA)
When creating the ProcessEngine
using the ProcessEngineBuilder
, the method enableJPA
should be called.
當使用 ProcessEngineBuilder
建立 ProcessEngine
時,必須調用方法enableJPA
。
ProcessEngine engine = new ProcessEngineBuilder()
.configureFromPropertiesResource("activiti.properties")
.enableJPA(entityManagerFactory, true, true)
.buildProcessEngine();
This method accepts the following parameters:
這個方法接受下列參數:
-
entityManagerFactory:
An instance of javax.persistence.EntityManagerFactory
that will be used to load the Entities and flushing the updates.
entityManagerFactory:
一個javax.persistence.EntityManagerFactory
的實例,用作載入實體并刷新更新。
-
handleTransaction:
Flag indicating that the engine should begin and commit/rollback the transaction on the used EntityManager
instances. Set to false when Java Transaction API (JTA)
is used.
handleTransaction:
指示引擎應當開始和提交/回滾所使用 EntityManager
的實例的事務標志。當使用時設置Java Transaction API (JTA)
為false。
-
closeEntityManager:
Flag indicating that the engine should close the EntityManager
instance that was obtained from the EntityManagerFactory
. Set to false when the EntityManager
is container-managed (e.g. when using an Extended Persistence Context which isn't scoped to a single transaction').
closeEntityManager:
指示引擎應當關閉從 EntityManagerFactory
獲得的實例 EntityManager
的標志。當EntityManager
是容器托管的設置為false(例如,當使用不是作用到單個事物的擴展持久化上下文)
JPA using Spring(使用Sping的JPA)
The EntityManagerFactory
should be set using the ProcessEngineFactoryBean
, which is described in the section called “Configuration(配置)” As an example, we use OpenJPA as persistence-provider using a H2 datasource. The example below is only an extract from a spring configuration-file and only contains relevant beans.
應當使用 ProcessEngineFactoryBean
設置EntityManagerFactory
,在the section called “Configuration(配置)”描述。作為示例,我們使用OpenJPA作為使用H2數據源的持久化提供者。如下的示例只從一個Spring配置文件提取出來,并包含了相關的bean.
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<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="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="dataBaseType" value="h2" />
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="dbSchemaStrategy" value="create-drop" />
<property name="mailServerHost" value="localhost" />
<property name="mailServerPort" value="5025" />
<property name="jpaEntityManagerFactory" ref="entityManagerFactory" />
<property name="jpaHandleTransaction" value="true" />
<property name="jpaCloseEntityManager" value="true" />
</bean>
-
jpaEntityManagerFactory:
An reference to a bean implementing javax.persistence.EntityManagerFactory
that will be used to load the Entities and flushing the updates.
jpaEntityManagerFactory:
一個指向實現javax.persistence.EntityManagerFactory
的bean的引用,用來載入和刷新更新。
-
jpaHandleTransaction:
Flag indicating that the engine should begin and commit/rollback the transaction on the used EntityManager
instances. Set to false when Java Transaction API (JTA)
is used.
jpaHandleTransaction:
指示引擎應當開始和提交/回滾所使用 EntityManager
的實例的事務標志。當使用時設置Java Transaction API (JTA)
為false。
-
jpaCloseEntityManager:
Flag indicating that the engine should close the EntityManager
instance that was obtained from the jpaCloseEntityManager
. Set to false when theEntityManager
is container-managed (e.g. when using an Extended Persistence Context which isn't scoped to a single transaction').
jpaCloseEntityManager:
closeEntityManager:
指示引擎應當關閉從 EntityManagerFactory
獲得的實例 EntityManager
的標志。當EntityManager
是容器托管的設置為false(例如,當使用不是作用到單個事物的擴展持久化上下文)。
Usage(用法)
Simple Example(簡單示例)
Examples for using JPA variables can be found in JPAVariableTest
. We'll explain JPAVariableTest.testUpdateJPAEntityValues
step by step.
在 JPAVariableTest
里能找到使用JPA變量的示例。將一步一步解釋JPAVariableTest.testUpdateJPAEntityValues
。
First of all, we create a EntityManagerFactory
for our persistence-unit, which is based on META-INF/persistence.xml
. This contains classes which should be included in the persistence unit and some vendor-specific configuration.
首先,我們為我們的持久化單元建立 EntityManagerFactory
,這基于META-INF/persistence.xml
。這包含了了持久化單元所需的類和一些數據庫供應商相關的配置。
The EntityManagagerFactory
is set on the engine to enable JPA support. Please note that this is done using a shortcut for testing purposes, and should actually be set properly using one of the methods described in the section called “Configuration(配置)”.
引擎設置 EntityManagagerFactory來支持JPA。請注意:基于測試目的,通過快捷方式來完成,實際上,通過使用在the section called “Configuration(配置)”描述的方法之一來合理設置。
We are using a simple entity in the test, having an id and String
value property, which is also persisted. Before running the test, we create an entity and save this.
在測試里面,我們正使用一個簡單實體。這個實體具有一個id和String
值屬性。實體也要被持久化。在運行測試之前,我們建立一個實體并保存它。
@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;
}
}
We start a new process instance, adding the entity as a variable. As with other variables, they are stored in the persistent storage of the engine. When the variable is requested the next time, it will be loaded from the EntityManager
based on the class and Id stored.
我們啟動一個新的流程實例,增加實體作為一個變量。和其它變量一樣,它們保存在引擎的持久化存儲區。當下次請求這個變量時,將從基于類和保存的Id的EntityManager載入。
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("entityToUpdate", entityToUpdate);
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("UpdateJPAValuesProcess", variables);
The first node in our process definition contains a serviceTask
that will invoke the method setValue
on entityToUpdate
, which resolves to the JPA variable we set earlier when starting the process instance and will be loaded from the EntityManager
associated with the current engine's context'.
我們流程定義的第一個節點包含一個serviceTask
。它將調用在 entityToUpdate
之上的方法setValue
。 當啟動這個流程實例時,它將解析我們先前設置的JPA變量。JPA變量從當前流程上下文關聯的EntityManager
載入。
<serviceTask id='theTask' name='updateJPAEntityTask' activiti:expression="${entityToUpdate.setValue('updatedValue')}" />
When the service-task is finished, the process instance waits in a userTask defined in the process definition, which allows us to inspect the process instance. At this point, theEntityManager
has been flushed and the changes to the entity have been pushed to the database. When we get the value of the variable entityToUpdate
, it's loaded again and we get the entity with it's value
property set to updatedValue
.
當服務任務完成時,流程實例在流程定義里定義的userTask。userTask允許我們檢查這個流程實例。在這時,EntityManager
已經被刷新并且實體變更已經被推送到數據庫。當我們獲取變量entityToUpdate
的值時,它再次載入。我們取得的 updatedValue
的value
屬性。
// 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());
Query JPA process variables(查詢流程變量)
You can query for ProcessInstance
s and Execution
s that have a certain JPA-entity as variable value. Note that only variableValueEquals(name, entity)
is supported for JPA-Entities on ProcessInstanceQuery
and ExecutionQuery
. Methods variableValueNotEquals
, variableValueGreaterThan
, variableValueGreaterThanOrEqual
, variableValueLessThan
and variableValueLessThanOrEqual
are unsupported and will throw an ActivitiException
when an JPA-Entity is passed as value.
你能夠查詢 ProcessInstance
s 和 Execution
具有某個作為變量值的JPA-bean。注意:對于在 ProcessInstanceQuery
和 ExecutionQuery
上的JPA實體只支持 variableValueEquals(name, entity)
。當JPA實體作為值傳送時,不支持方法variableValueNotEquals
, variableValueGreaterThan
, variableValueGreaterThanOrEqual
, variableValueLessThan
和 and variableValueLessThanOrEqual
將拋出一個 ActivitiException
異常。
ProcessInstance result = runtimeService.createProcessInstanceQuery().variableValueEquals("entityToQuery", entityToQuery).singleResult();
Advanced example using Spring beans and JPA(使用Spring beans和JPA的高級示例)
A more advanced example, JPASpringTest
, can be found in activiti-spring-examples
. It describes the following simple use case:
一個更加高級的示例,JPASpringTest ,能夠在activiti-spring-examples
找到。它描述了下列簡單的用例:
-
An existing Spring-bean which uses JPA entities already exists which allows for Loan Requests to be stored.
使用已存在JPA實體的已存在的Spring-bean允許保存Loan Request.
-
Using Activiti, we can use the existing entities, obtained through the existing bean, and use them as variable in our process.
使用Activiti,我們能夠使用存在的實體,通過存在的bean來獲取這個實體,并在我們的流程里面以變量方式使用它們。
Process is defined in the following steps:
流程在下列步驟定義:
-
Service task that creates a new LoanRequest, using the existing LoanRequestBean
using variables received when starting the process (e.g. could come from a start form). The created entity is stored as a variable, using activiti:resultVariableName
which stores the expression result as a variable.
建立一個新的的服務任務,通過
-
UserTask that allows a manager to review the request and approve/disapprove, which is stored as a boolean variable approvedByManager
允許經理審閱請求并且批準/否決的UserTask,它作為一個布爾值approvedByManager
保存。
-
ServiceTask that updates the loan request entity so the entity is in sync with the process.
因為ServiceTask更新貸款請求實體,所以這個實體和流程同步。
-
Depending on the value of the entity property approved
, an exclusive gateway is used to make a decision about what path to take next: When the request is approved, process ends, otherwise, an extra task will become available (Send rejection letter), so the customer can be notified manually by a rejection letter.
依賴實體屬性 approved
的值,所使用的一個總開關下一步作出決策:當請求被批準,流程結束,否則,另外的任務將成為可能(發送拒絕函),這樣通過拒絕函手動通知客戶。
Please note that the process doesn't contain any forms, since it is only used in a unit test.
請注意這個流程不包括任何表單,因為它只是作為單元測試使用。

<?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:resultVariableName="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>
Although the example above is quite simple, it shows the power of using JPA combined with Spring and parametrized method-expressions. The process requires no custom java-code at all (except for the Spring-bean off course) and speeds up development drastically.
盡管上例相當簡單,但是它展示了組合Spring和參數化方法表達式JPA的威力。流程不需要任何Java代碼(當然Spring是例外),并及大地加速了開發。