网路冷眼@BlogJava

熙熙攘攘一闲人 以冷静的眼光观察技术
posts - 88, comments - 193, trackbacks - 0, articles - 28
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

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 EntityManageris 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的值时,它再次载入。我们取得的 updatedValuevalue 属性。

// 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 ProcessInstances and Executions 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.

你能够查询 ProcessInstances 和 Execution具有某个作为变量值的JPA-bean。注意:对于在 ProcessInstanceQueryExecutionQuery上的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.

请注意这个流程不包括任何表单,因为它只是作为单元测试使用。

jpa.spring.example.process

<?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是例外),并及大地加速了开发。


只有注册用户登录后才能发表评论。


网站导航: