cancel
Showing results for 
Search instead for 
Did you mean: 

Spring JpaTransactionManager not saving Activiti entities to DB

neville_sequeir
Confirmed Champ
Confirmed Champ
Nothing about Activiti is being saved to database. Application entities ARE being saved to database.  Below, in order are, the spring file, persitence.xml file and the test case.

Sprint file:

   <context:annotation-config/>
   <aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
   <tx:annotation-driven transaction-manager="transactionManager"/>

   
   <bean id="ActivitiTrialDataSource" class="org.apache.commons.dbcp.BasicDataSource" >
      <property name="driverClassName" value="net.sourceforge.jtds.jdbc.Driver" />
      <property name="url" value="jdbc:jtds:sqlserver://localhost:1433/ActivitiTrial" />
      <property name="username" value="ActivitiTrial" />
      <property name="password" value="ActivitiTrial" />
      <property name="defaultAutoCommit" value="false" />
      <property name="initialSize" value="5" />
   </bean>


   <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
      <property name="dataSource" ref="ActivitiTrialDataSource" />
       <property name="persistenceUnitName" value="ActivitiTrial"/>
   </bean>

   <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
       <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>



   <!– Activiti –>
   <bean id="activitiDataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
      <property name="targetDataSource" ref="ActivitiTrialDataSource" />
   </bean>
   
   <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
      <property name="databaseType" value="mssql" />
       <property name="dataSource" ref="activitiDataSource" />
      <property name="transactionsExternallyManaged" value="true" />
       <property name="transactionManager" ref="transactionManager" />
       <property name="databaseSchemaUpdate" value="false" />
      <property name="history" value="audit" />
       <property name="jobExecutorActivate" value="false" />
   </bean>
 
     <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
      <property name="processEngineConfiguration" ref="processEngineConfiguration" />
   </bean>

   <bean id="activitiRepositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />
   <bean id="activitiRuntimeService" factory-bean="processEngine" factory-method="getRuntimeService" />
   <bean id="activitiTaskService" factory-bean="processEngine" factory-method="getTaskService" />
   <bean id="activitiHistoryService" factory-bean="processEngine" factory-method="getHistoryService" />
   <bean id="activitiManagementService" factory-bean="processEngine" factory-method="getManagementService" />



Persistence.xml file:

<persistence-unit name="ActivitiTrial">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
       
        <properties>
            <property name="hibernate.archive.autodetection" value="hbm,class"/>
         <property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect"/>
            <property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>
            <property name="hibernate.hbm2ddl.auto" value="none"/>
         <property name="hibernate.show_sql" value="false"/>
         <property name="hibernate.ejb.metamodel.generation" value="disabled"/>
        </properties>
</persistence-unit>



TestClass :

@RunWith(SpringJUnit4ClassRunner.class)
@TransactionConfiguration(defaultRollback=false)
@ContextConfiguration({"classpath:/springApplicationContext.xml"})
public class TrialTest {
   @Autowired
   RepositoryService activitiRepositoryService;

   @Autowired
   TaskService activitiTaskService;


   /**
    * Used for persistence of entities not related to Activiti process engine. i.e, application entities
    * Activiti process engine related persistence is as per a configuration bean named "processEngineConfiguration" found in the Activiti
         * configuration file activiti.cfg.xml.
    */
   @PersistenceContext(unitName="ActivitiTrial")
   EntityManager entityManager;

   
   @Autowired
   ProcessService<String> processServiceActiviti;

   
   @Test
   @Transactional
   public void trialTest() throws Exception {
      activitiRepositoryService.createDeployment().addClasspathResource("process-definitions/neville.bpmn20.xml").deploy();

      
      // 1. Do application work. VERIFIED - THIS ENTITY IS BEING SAVED TO DB.
      ApplicationEntity applicationEntity1 = new ApplicationEntity();
      applicationEntity1.name = "App entity 1";
      entityManager.persist(applicationEntity1);

      
      // 2. Do Actviti work. VERIFIED - None of the ACTIVITI objects are saved to the db.
      ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("aProcessWithOneTaskBetweenStartAndEnd");
      String processInstanceId = processInstance.getId();
      Task userTask = activitiTaskService.createTaskQuery().processInstanceId(processInstanceId).list().get(0);
                // VERIFIED that userTask is not null.

      
      // 1. Do some more application work. VERIFIED - THIS ENTITY IS BEING SAVED TO DB.
      ApplicationEntity applicationEntity2 = new ApplicationEntity();
      applicationEntity2.name = "App entity 2";
      entityManager.persist(applicationEntity2);
   }
}
15 REPLIES 15

trademak
Star Contributor
Star Contributor
Hi,

When I look at the test you added to this post you persist an entity first, then do Activiti stuff and then persist another entity. I've changed the transaction behavior to do a rollback and everything is rollbacked. And when I set rollback to false everything is saved to the database (entities + Activiti workflow). I also test with throwing a runtime exception after the first persist and also noticed that there's an entity in the database. But it seems to be cause by the defaultRollback=false attribute of the transactional configuration.
So I still think it's working as expected.

Best regards,

neville_sequeir
Confirmed Champ
Confirmed Champ
Hi Tijs,

So you think it is not an issue that there are multiple transactions? Or are you saying that the multiple transactions are due to the way the test case is configured? I will try running the same code using a main() method without any test infrastructure and report back. If there are multiple transactions, I think that would be an issue.

neville_sequeir
Confirmed Champ
Confirmed Champ
I am seeing the same behavior by running this code outside any testing infrastructure - i.e., multiple transactions. So, effectively, the question is: how do we make sure Activiti related db interaction and other db interaction is happening on same transaction? Below is my code using a main() method. The spring configuration file, persistence.xml and the process definition XML file are all the same as before, except for the addition of TransactionManagerTesterImpl bean definition to the spring configuration file.

<code>
<bean id="transactionManagerTester" class="TransactionManagerTesterImpl"/>
</code>


<code>
import java.util.Date;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.transaction.annotation.Transactional;


public class TransactionManagerTesterImpl implements TransactionManagerTester {
    @Autowired
RepositoryService activitiRepositoryService;

@Autowired
RuntimeService activitiRuntimeService;

@Autowired
TaskService activitiTaskService;


@PersistenceContext(unitName="ActivitiTrial")
EntityManager entityManager;



@Override
@Transactional
public void execute() throws Exception {
  long entryMilliseconds = new Date().getTime();

 
  ApplicationEntity applicationEntity1 = new ApplicationEntity();
  applicationEntity1.name = "App entity 1";
  applicationEntity1.createDate = new Date();
  entityManager.persist(applicationEntity1);
 
 
  activitiRepositoryService.createDeployment().addClasspathResource("process-definitions/neville.bpmn20.xml").deploy();
  ProcessInstance processInstance = activitiRuntimeService.startProcessInstanceByKey("neville");
  String processInstanceId = processInstance.getId();
  Task userTask = activitiTaskService.createTaskQuery().processInstanceId(processInstanceId).list().get(0);


  ApplicationEntity applicationEntity2 = new ApplicationEntity();
  applicationEntity2.name = "App entity 2";
  applicationEntity2.createDate = new Date();
  entityManager.persist(applicationEntity2);

 
  System.out.println("Leaving TransactionManagerTesterImpl.execute() in : " + (new Date().getTime() - entryMilliseconds) + " milliseconds.");
}



public static void main(String[] args) throws Exception {
  ApplicationContext applicationContext = new ClassPathXmlApplicationContext("springApplicationContext.xml");
  TransactionManagerTester transactionManagerTester = applicationContext.getBean(TransactionManagerTester.class);
 
  transactionManagerTester.execute();
}
}
</code>


<code>
public interface TransactionManagerTester {
public void execute() throws Exception;
}
</code>

neville_sequeir
Confirmed Champ
Confirmed Champ
I noticed that the following line was causing the current transaction to be suspended and a new transaction to be created - on debugging, I found that this is because Activiti is telling the transaction manager that the propagationBehavior used should be TransactionDefinition.PROPAGATION_REQUIRES_NEW
<code>
  activitiRepositoryService.createDeployment().addClasspathResource("process-definitions/neville.bpmn20.xml").deploy();
</code>


So, I removed that line and deployed the process definition separately before running the test. Now, I am noticing that the following line is causing a new transaction to be created - due to similar reasoning.

<code>
  ProcessInstance processInstance = activitiRuntimeService.startProcessInstanceByKey("neville");
</code>

Why is Activiti telling the spring JPA transaction manager (via TransactionDefinition.PROPAGATION_REQUIRES_NEW) to suspend current transaction and creating new one inspite of setting the relevant properties on processEngineConfiguration correctly?
Is there anything additional to be done to prevent Activiti from causing new transactions? Anything to be configured in iBatis?

Its very critical for us that we have one and only one transaction for one user initiated action.

trademak
Star Contributor
Star Contributor
When you look in this much detail at transaction handling in Activiti it's important to have a full overview of what happens. When a new deployment is created for example, the DbIdGenerator class will generate new ids for the deployment, the resources and the process definition entities. This DbIdGenerator has a REQUIRES_NEW for transaction handling. So on a database level you'll see more than one transaction. One for the JPA transaction manager and one or more for the DbIdGenerator.

The main thing is that you verify that if you rollback the transaction, everything is rollbacked as expected. So you should have no new application entities, no new deployment, no new process definition and instance. And that's what I see is working as expected. So as I said I think it works just fine and the multiple transactions can be explained by the DbIdGenerator.

Best regards,

neville_sequeir
Confirmed Champ
Confirmed Champ
Thanks a lot for you responses Tijs.
This should help me move forward with my evaluation of Activiti.