使用Common TransactionManager管理Hibernate和Activiti

时间:2017-06-21 06:32:15

标签: spring hibernate jpa activiti transactionmanager

我想为hibernate和activiti使用一个通用的事务管理器(JpaTransactionManager),但我不能!我已经阅读了所有互联网资源!听到是一个简单的场景(甚至不使用hibernate !!):

  1. 在任务中保存变量
  2. 在任务执行中保存变量
  3. 完成任务
  4. 场景实现(在带有@Transactional注释的spring bean方法中):

    @Component
    public class TaskManager {
    
        @Autowired TaskService taskService;
        @Autowired RuntimeService runtimeService;
    
        @Transactional
        public void completeTask(CompleteTaskRequest request) {
            Task task = taskService.createTaskQuery().taskId(request.getTaskId()).singleResult();
            if (task == null) {
                throw new ActivitiObjectNotFoundException("No task found");
            }
            taskService.setVariableLocal(task.getId(), "actionDisplayUrl", request.getActionDisplayUrl());
            taskService.setVariableLocal(task.getId(), "actionSummaryUrl", request.getActionSummaryUrl());
            runtimeService.setVariableLocal(task.getExecutionId(), "prevTaskId", task.getId());
            taskService.complete(task.getId());
        }
    }
    

    很明显:如果taskService.complete抛出错误,整个事务应该回滚,所以应该回滚所有保存的变量,并且应该传递以下测试用例:

    @Test
    @Deployment(resources = "org.activiti.test/CompleteTaskTest.bpmn20.xml")
    public void testCompleteTaskWithError() {
        Map<String, Object> processVars = new HashMap<>();
        processVars.put("error", true); // Causes throwing error in ScriptTaskListener
        runtimeService.startProcessInstanceByKey("CompleteTaskTest", processVars);
        Task task = taskService.createTaskQuery().taskName("Task 1").singleResult();
    
        CompleteTaskRequest req = new CompleteTaskRequest();
        req.setTaskId(task.getId());
        req.setActionDisplayUrl("/actions/1234");
        req.setActionSummaryUrl("/actions/1234/summary");
        try {
            taskManager.completeTask(req);
            fail("An error expected!");
        } catch(Exception e) {
        }
    
        // Check variables rollback
        assertNull(taskService.getVariableLocal(task.getId(),"actionSummaryUrl"));
        assertNull(taskService.getVariableLocal(task.getId(),"actionDisplayUrl"));
        assertNull(runtimeService.getVariableLocal(task.getExecutionId(), "prevTaskId"));
    }
    

    但它失败了,变量被提交给DB(没有回滚)。

    Spring上下文(使用org.springframework.orm.jpa.JpaTransactionManager作为事务管理器):

    <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
       <property name="dataSource" ref="dataSource" />
       <property name="transactionManager" ref="transactionManager" />
       <property name="idGenerator" ref="idGenerator"/>
       <property name="databaseSchemaUpdate" value="true" />
       <property name="jpaEntityManagerFactory" ref="entityManagerFactory" />
       <property name="jpaHandleTransaction" value="true" />
       <property name="jpaCloseEntityManager" value="true" />
       <property name="beans" ref="processEngineBeans" />
       <property name="jobExecutorActivate" value="false" />
       <property name="asyncExecutorEnabled" value="true" />
       <property name="asyncExecutorActivate" value="true" />
    </bean>
    
    <aop:config proxy-target-class="true" />
    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
    
    <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
       <property name="processEngineConfiguration" ref="processEngineConfiguration" />
    </bean>
    
    <bean id="processEngineBeans" class="java.util.HashMap">
       <constructor-arg index="0" type="java.util.Map">
          <map>
          </map>
       </constructor-arg>
    </bean>
    
    <bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />
    <bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" />
    <bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" />
    <bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" />
    <bean id="managementService" factory-bean="processEngine" factory-method="getManagementService" />
    <bean id="identityService" factory-bean="processEngine" factory-method="getIdentityService" />
    <bean id="formService" factory-bean="processEngine" factory-method="getFormService" />
    <bean id="idGenerator" class="org.activiti.engine.impl.persistence.StrongUuidGenerator" />
    
    <bean id="activitiRule" class="org.activiti.engine.test.ActivitiRule">
       <property name="processEngine" ref="processEngine" />
    </bean>
    
    
    <bean id="persistenceUnitManager"
         class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
       <property name="packagesToScan" value="org.activiti.test" />
       <property name="defaultDataSource" ref="dataSource" />
    </bean>
    
    <bean id="entityManagerFactory"
         class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
       <property name="persistenceUnitManager" ref="persistenceUnitManager" />
       <property name="persistenceProvider">
          <bean class="org.hibernate.jpa.HibernatePersistenceProvider" />
       </property>
       <property name="jpaProperties">
          <props>
             <prop key="hibernate.dialect_resolvers">org.hibernate.engine.jdbc.dialect.internal.DialectResolverSet</prop>
             <prop key="hibernate.hbm2ddl.auto">create</prop>
             <prop key="hibernate.cache.use_second_level_cache">false</prop>
             <prop key="hibernate.cache.use_query_cache">false</prop>
             <prop key="hibernate.show_sql">true</prop>
          </props>
       </property>
    </bean>
    
    <bean id="dataSource"
         class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
       <property name="driverClass" value="org.h2.Driver" />
       <property name="url" value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" />
       <property name="username" value="sa" />
       <property name="password" value="" />
    </bean>
    
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
       <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
    
    <!-- bean post-processor for JPA annotations -->
    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
    
    <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" >
       <property name="proxyTargetClass" value="true" />
    </bean>
    

    版本:

    • Activiti版本:5.22.0
    • DB:h2(还在生产中使用Oracle测试)

    我的配置有什么问题?

    P.S。

    UPDATE ::

    通过使用org.springframework.jdbc.datasource.DataSourceTransactionManager代替org.springframework.orm.jpa.JpaTransactionManager,测试用例已通过。但现在我无法坚持JPA实体。

1 个答案:

答案 0 :(得分:1)

通过解决MyBatis(JDBC)和Hibernate(JPA)之间的冲突解决了这个问题:

应将

jpaVendorAdapter属性添加到entityManagerFactory bean:

<property name="jpaVendorAdapter">
    <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>

所以entityManagerFactory bean应该是这样的:

<bean id="entityManagerFactory"
      class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceUnitManager" ref="persistenceUnitManager" />
    <property name="persistenceProvider">
        <bean class="org.hibernate.jpa.HibernatePersistenceProvider" />
    </property>
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
    </property>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.dialect_resolvers">org.hibernate.engine.jdbc.dialect.internal.DialectResolverSet</prop>
            <prop key="hibernate.hbm2ddl.auto">create</prop>
            <prop key="hibernate.cache.use_second_level_cache">false</prop>
            <prop key="hibernate.cache.use_query_cache">false</prop>
            <prop key="hibernate.show_sql">true</prop>
        </props>
    </property>
</bean>

有关详细信息,请参阅this question的回答。