This is the 8th day of my participation in Gwen Challenge

Introduction to JPA in Activiti

  • You can useJPA entitiesAs aProcess variables,And perform operations:
    • Updating existing JPA entities based on process variables can be filled out in a form for a user task or generated by a service task
    • Reuse existing domain models without writing displayed services to retrieve entities or update their values
    • Make decisions based on attributes of existing entities (gateways are branch aggregation)

JPA entity requirements

  • JPA in Activiti only supports entities that meet the following requirements:
    • Entities should be configured using JPA annotations, supporting both field and attribute access.@MappedSuperclass should also be available
    • The entity should have a primary key with the @ID annotation, and the compound primary keys @embeddeDID and @idClass are not supported:
      • Id field or attributeCan use any type supported by the JPA specification:
        • Native data types and their wrapper types (except Boolean)
        • String
        • BigInteger
        • BigDecimal
        • java.util.Date
        • java.sql.Date

JPA configuration

  • The engine must have a reference to the EntityManagerFactory to use the JPA entity, either by configuring the reference or by providing a persistence unit name
  • JPA entities as variables will be automatically detected and processed accordingly
  • Configure with jpaPersistenceUnitName:
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration">

    <! -- Database configuration -->
    <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>
Copy the code
  • Configure a customEntityManagerFactory,
    • The OpenJPA Entity Manager is used here
    • The snippet contains only the beans associated with the example, leaving out the others.
    • OpenJPA entity management example of complete and can be used in activiti – spring – examples (/ activiti – spring/SRC/test/Java/org/activiti/spring/test/jpa/JPASpringT Est. Found in 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>
Copy the code
  • Configuration can also be done while programmatically creating an engine:
ProcessEngine processEngine = ProcessEngineConfiguration
  .createProcessEngineConfigurationFromResourceDefault()
  .setJpaPersistenceUnitName("activiti-pu")
  .buildProcessEngine();
Copy the code

The configured properties are:

  • jpaPersistenceUnitName:Use the name of the persistence unit:
    • To ensure that the persistence unit is available under the classpath, the default path is/meta-INF /persistence.xml
    • Either use jpaEntityManagerFactory or jpaPersistenceUnitName
  • jpaEntityManagerFactory:One implementedjavax.persistence.EntityManagerFactorythebeanReferences:
    • Will be used to load entities and refresh updates
    • Either use jpaEntityManagerFactory or jpaPersistenceUnitName
  • jpaHandleTransaction:In useEntityManagerOn an instance, this flag indicates whether the process engine needs to start and commit or roll back a transaction:
    • Set to false when using the Java Transaction API(JTA)
  • jpaCloseEntityManager:This flag indicates whether the process engine should close the slaveEntityManagerFactoryTo obtain theEntityManagerExamples:
    • Set to false when EntityManager is container-managed: when using extended persistence contexts that are not single-transaction scoped

JPA usage

A simple example

  • First, you need to create an EntityManagerFactory based on meta-INF /persistence.xml as a persistence unit: it contains all the classes in the persistence unit and some vendor-specific configurations
  • As a test, use a simple entity that contains an ID and a String value attribute, which will also be persisted
  • Before testing, create an entity and save:
@Entity(name = "JPA_ENTITY_FIELD")
public class FieldAccessJPAEntity {

  @Id
  @Column(name = "ID_")
  private Long id;

  private String value;

  public FieldAccessJPAEntity(a) {
    // Empty constructor needed for JPA
  }

  public Long getId(a) {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public String getValue(a) {
    return value;
  }

  public void setValue(String value) {
    this.value = value; }}Copy the code
  • Start a new process instance and add an entity as a variable. Other variables will be stored in the process engine’s persistent database. The next time the variable is fetched, it will be loaded from EntityManager based on the class and storage Id:
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("entityToUpdate", entityToUpdate);

ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("UpdateJPAValuesProcess", variables);
Copy the code
  • The first node in the process definition is a service task that will call the setValue method on entityToUpdate, which is the JPA variable set earlier when the process instance is started, and will be loaded from the EntityManager context-associated with the current process engine:
<serviceTask id='theTask' name='updateJPAEntityTask' activiti:expression="${entityToUpdate.setValue('updatedValue')}" />
Copy the code
  • When a service task is completed, the process instance will rest on the user task segment defined in the process definition:
    • You can view an example of the process
    • EntityManager has been refreshed and the changed entity has been saved to the database
    • When you get the entityToUpdate variable value, the entity will be reloaded and the value of the attribute that gets the entity will be 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())
Copy the code

Query JPA process variables

  • ProcessInstances and Executions with a JPA entity as variable
  • inProcessInstanceQueryandExecutionQueryOnly in the queryvariableValueEquals(name, entity)Support for JPA entity variables:
    • [variableValueNotEquals],[variableValueGreaterThan],[variableValueGreaterThanOrEqual],[variableValueLessThan],[variableV AlueLessThanOrEqual] is not supported and throws a ActivitiException when passing a JPA entity value
ProcessInstance result = runtimeService.createProcessInstanceQuery().variableValueEquals("entityToQuery", entityToQuery).singleResult();   
Copy the code

Use Spring Beans in conjunction with JPA

  • JPASpringTest,In activiti – spring – examples:
    • A Spring-Bean already exists that uses JPA entities to store loan applications
    • With Activiti, you can retrieve an already used entity from an existing bean and use it as a variable in the process
  • Process definition steps:
    • Service tasks:
      • Create a new loan application using the existing LoanRequestBean to accept variables from the process startup form
      • Use Activiti :resultVariable(which stores the result returned by an expression as a variable) to store the created entity as a variable
    • User tasks:
      • Allow the manager to view the loan application and fill in the approval opinion (agree/disagree)
      • The approval opinion is stored as a Boolean variable approvedByManager
    • Service tasks:
      • Update the loan application entity so that it is in sync with the process
    • According to the loan application entity variableapprovedWill use the unique gateway to automatically determine which path to select next:
      • When the application is approved, the process ends
      • Otherwise, an additional task will be used (send rejection letter) so that a rejection letter can be sent to notify the customer


      
<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">The ${! 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>
Copy the code

The above example demonstrates the powerful advantage of JPA combining Spring with parameterized method expressions: All processes do not require custom Java code (with the exception of Spring beans), dramatically speeding up process deployment