Chapter 09. JPA
Table of Contents
- Requirements(需求)
- Configuration(配置)
-
- JPA using ProcessEngineBuilder(使用ProcessEngineBuilder的JPA)
- JPA using Spring(使用Sping的JPA)
- Usage(用法)
-
- Simple Example(簡(jiǎn)單示例)
- Query JPA process variables(查詢流程變量)
- Advanced example using Spring beans and JPA(使用Spring beans和JPA的高級(jí)示例)
You can use JPA-Entities as process variables, allowing you to:
你能使用作為流程變量的JPA-實(shí)體,允許:
-
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-實(shí)體,它被填充在userTask的表單里面,或者在serviceTask里面產(chǎn)生。
-
Reusing existing domain model without having to write explicit services to fetch the entities and update the values
復(fù)用已存在的領(lǐng)域模型,不必編寫顯式獲取這些實(shí)體和更新這些數(shù)值。
-
Make decisions (gateways) based on properties of existing entities.
基于存在實(shí)體屬性作決定(網(wǎng)關(guān))
-
...
Requirements(需求)
Only entities that comply to the following are supported:
只支持遵從下面標(biāo)準(zhǔn)的實(shí)體:
-
Entities should be configured using JPA-annotations, we support both field and property-access. Mapped super classes can also be used.
應(yīng)當(dāng)使用JPA-標(biāo)注配置實(shí)體,我們支持字段和屬性方法。也能夠使用已映射的超類。
-
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
.
實(shí)體應(yīng)當(dāng)具有標(biāo)注為@Id的主鍵,不支持組合主鍵 (@EmbeddedId
and @IdClass
)。Id 字段/屬性能夠是JPA規(guī)范里任何支持的類型:原子類型和它們的包裝類(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-實(shí)體,引擎必須具有一個(gè)到EntityManagerFactory
的引用。當(dāng)在引擎上啟用了JPA,作為變量的JPA-實(shí)體將被自動(dòng)檢測(cè)并作相應(yīng)的處理。
JPA using ProcessEngineBuilder(使用ProcessEngineBuilder的JPA)
When creating the ProcessEngine
using the ProcessEngineBuilder
, the method enableJPA
should be called.
當(dāng)使用 ProcessEngineBuilder
建立 ProcessEngine
時(shí),必須調(diào)用方法enableJPA
。
ProcessEngine engine = new ProcessEngineBuilder()
.configureFromPropertiesResource("activiti.properties")
.enableJPA(entityManagerFactory, true, true)
.buildProcessEngine();
This method accepts the following parameters:
這個(gè)方法接受下列參數(shù):
-
entityManagerFactory:
An instance of javax.persistence.EntityManagerFactory
that will be used to load the Entities and flushing the updates.
entityManagerFactory:
一個(gè)javax.persistence.EntityManagerFactory
的實(shí)例,用作載入實(shí)體并刷新更新。
-
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:
指示引擎應(yīng)當(dāng)開(kāi)始和提交/回滾所使用 EntityManager
的實(shí)例的事務(wù)標(biāo)志。當(dāng)使用時(shí)設(shè)置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:
指示引擎應(yīng)當(dāng)關(guān)閉從 EntityManagerFactory
獲得的實(shí)例 EntityManager
的標(biāo)志。當(dāng)EntityManager
是容器托管的設(shè)置為false(例如,當(dāng)使用不是作用到單個(gè)事物的擴(kuò)展持久化上下文)
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.
應(yīng)當(dāng)使用 ProcessEngineFactoryBean
設(shè)置EntityManagerFactory
,在the section called “Configuration(配置)”描述。作為示例,我們使用OpenJPA作為使用H2數(shù)據(jù)源的持久化提供者。如下的示例只從一個(gè)Spring配置文件提取出來(lái),并包含了相關(guān)的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:
一個(gè)指向?qū)崿F(xiàn)javax.persistence.EntityManagerFactory
的bean的引用,用來(lái)載入和刷新更新。
-
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:
指示引擎應(yīng)當(dāng)開(kāi)始和提交/回滾所使用 EntityManager
的實(shí)例的事務(wù)標(biāo)志。當(dāng)使用時(shí)設(shè)置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:
指示引擎應(yīng)當(dāng)關(guān)閉從 EntityManagerFactory
獲得的實(shí)例 EntityManager
的標(biāo)志。當(dāng)EntityManager
是容器托管的設(shè)置為false(例如,當(dāng)使用不是作用到單個(gè)事物的擴(kuò)展持久化上下文)。
Usage(用法)
Simple Example(簡(jiǎn)單示例)
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.
首先,我們?yōu)槲覀兊某志没瘑卧?EntityManagerFactory
,這基于META-INF/persistence.xml
。這包含了了持久化單元所需的類和一些數(shù)據(jù)庫(kù)供應(yīng)商相關(guān)的配置。
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(配置)”.
引擎設(shè)置 EntityManagagerFactory來(lái)支持JPA。請(qǐng)注意:基于測(cè)試目的,通過(guò)快捷方式來(lái)完成,實(shí)際上,通過(guò)使用在the section called “Configuration(配置)”描述的方法之一來(lái)合理設(shè)置。
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.
在測(cè)試?yán)锩妫覀冋褂靡粋€(gè)簡(jiǎn)單實(shí)體。這個(gè)實(shí)體具有一個(gè)id和String
值屬性。實(shí)體也要被持久化。在運(yùn)行測(cè)試之前,我們建立一個(gè)實(shí)體并保存它。
@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.
我們啟動(dòng)一個(gè)新的流程實(shí)例,增加實(shí)體作為一個(gè)變量。和其它變量一樣,它們保存在引擎的持久化存儲(chǔ)區(qū)。當(dāng)下次請(qǐng)求這個(gè)變量時(shí),將從基于類和保存的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'.
我們流程定義的第一個(gè)節(jié)點(diǎn)包含一個(gè)serviceTask
。它將調(diào)用在 entityToUpdate
之上的方法setValue
。 當(dāng)啟動(dòng)這個(gè)流程實(shí)例時(shí),它將解析我們先前設(shè)置的JPA變量。JPA變量從當(dāng)前流程上下文關(guān)聯(lián)的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
.
當(dāng)服務(wù)任務(wù)完成時(shí),流程實(shí)例在流程定義里定義的userTask。userTask允許我們檢查這個(gè)流程實(shí)例。在這時(shí),EntityManager
已經(jīng)被刷新并且實(shí)體變更已經(jīng)被推送到數(shù)據(jù)庫(kù)。當(dāng)我們獲取變量entityToUpdate
的值時(shí),它再次載入。我們?nèi)〉玫?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
具有某個(gè)作為變量值的JPA-bean。注意:對(duì)于在 ProcessInstanceQuery
和 ExecutionQuery
上的JPA實(shí)體只支持 variableValueEquals(name, entity)
。當(dāng)JPA實(shí)體作為值傳送時(shí),不支持方法variableValueNotEquals
, variableValueGreaterThan
, variableValueGreaterThanOrEqual
, variableValueLessThan
和 and variableValueLessThanOrEqual
將拋出一個(gè) ActivitiException
異常。
ProcessInstance result = runtimeService.createProcessInstanceQuery().variableValueEquals("entityToQuery", entityToQuery).singleResult();
Advanced example using Spring beans and JPA(使用Spring beans和JPA的高級(jí)示例)
A more advanced example, JPASpringTest
, can be found in activiti-spring-examples
. It describes the following simple use case:
一個(gè)更加高級(jí)的示例,JPASpringTest ,能夠在activiti-spring-examples
找到。它描述了下列簡(jiǎn)單的用例:
-
An existing Spring-bean which uses JPA entities already exists which allows for Loan Requests to be stored.
使用已存在JPA實(shí)體的已存在的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,我們能夠使用存在的實(shí)體,通過(guò)存在的bean來(lái)獲取這個(gè)實(shí)體,并在我們的流程里面以變量方式使用它們。
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.
建立一個(gè)新的的服務(wù)任務(wù),通過(guò)
-
UserTask that allows a manager to review the request and approve/disapprove, which is stored as a boolean variable approvedByManager
允許經(jīng)理審閱請(qǐng)求并且批準(zhǔn)/否決的UserTask,它作為一個(gè)布爾值approvedByManager
保存。
-
ServiceTask that updates the loan request entity so the entity is in sync with the process.
因?yàn)镾erviceTask更新貸款請(qǐng)求實(shí)體,所以這個(gè)實(shí)體和流程同步。
-
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.
依賴實(shí)體屬性 approved
的值,所使用的一個(gè)總開(kāi)關(guān)下一步作出決策:當(dāng)請(qǐng)求被批準(zhǔn),流程結(jié)束,否則,另外的任務(wù)將成為可能(發(fā)送拒絕函),這樣通過(guò)拒絕函手動(dòng)通知客戶。
Please note that the process doesn't contain any forms, since it is only used in a unit test.
請(qǐng)注意這個(gè)流程不包括任何表單,因?yàn)樗皇亲鳛閱卧獪y(cè)試使用。

<?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.
盡管上例相當(dāng)簡(jiǎn)單,但是它展示了組合Spring和參數(shù)化方法表達(dá)式JPA的威力。流程不需要任何Java代碼(當(dāng)然Spring是例外),并及大地加速了開(kāi)發(fā)。