cancel
Showing results for 
Search instead for 
Did you mean: 

Activiti rejects transient JPA object as process variable ?

petercahyadi
Champ in-the-making
Champ in-the-making
Hi,

I'm experiencing an issue when trying to pass a transient JPA object as an input parameter to a java service task, which then would persist the object.

my process definition looks like this :

   <process id="saveUom">
      <startEvent id="TheStartEvent" />
      <sequenceFlow id="Flow1" sourceRef="TheStartEvent" targetRef="uomService" />
      <serviceTask id="uomService" activiti:expression="#{uomService.createUom(input)}" />
      <sequenceFlow id="Flow2" sourceRef="uomService" targetRef="TheEndEvent" />
      <endEvent id="TheEndEvent" />
   </process>

Here the 'input' variable is a JPA transient object, that i'm trying to make persistent using the 'uomService'.
But when i trigger this process, i'm getting the below exception
org.activiti.engine.ActivitiException: Value of primary key for JPA-Entity cannot be null

the stack trace is as below

org.activiti.engine.impl.variable.JPAEntityMappings.getIdString(JPAEntityMappings.java:170)
org.activiti.engine.impl.variable.JPAEntityMappings.getJPAIdString(JPAEntityMappings.java:85)
org.activiti.engine.impl.variable.JPAEntityVariableType.setValue(JPAEntityVariableType.java:66)
org.activiti.engine.impl.persistence.entity.VariableInstanceEntity.setValue(VariableInstanceEntity.java:164)
org.activiti.engine.impl.persistence.entity.VariableInstanceEntity.create(VariableInstanceEntity.java:72)
org.activiti.engine.impl.persistence.entity.VariableInstanceEntity.createAndInsert(VariableInstanceEntity.java:58)
org.activiti.engine.impl.persistence.entity.VariableScopeImpl.createVariableLocal(VariableScopeImpl.java:225)
org.activiti.engine.impl.persistence.entity.VariableScopeImpl.setVariable(VariableScopeImpl.java:175)
org.activiti.engine.impl.persistence.entity.VariableScopeImpl.setVariables(VariableScopeImpl.java:264)
org.activiti.engine.impl.cmd.StartProcessInstanceCmd.execute(StartProcessInstanceCmd.java:69)
org.activiti.engine.impl.cmd.StartProcessInstanceCmd.execute(StartProcessInstanceCmd.java:31)
org.activiti.engine.impl.interceptor.CommandExecutorImpl.execute(CommandExecutorImpl.java:24)
org.activiti.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:42)
org.activiti.spring.SpringTransactionInterceptor$1.doInTransaction(SpringTransactionInterceptor.java:42)
org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:130)
org.activiti.spring.SpringTransactionInterceptor.execute(SpringTransactionInterceptor.java:40)
org.activiti.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:33)
org.activiti.engine.impl.RuntimeServiceImpl.startProcessInstanceByKey(RuntimeServiceImpl.java:54)

What i understand so far from looking the JPAEntityMappings code, is that Activiti requires the process variable of JPA type (the class is annotated with @Entity) to have an Id of non null value. which in my case it would not be there, as the object is a transient object which i'm just about to persist.

Anybody can help to explain what's the reason for this ? because i think my scenario is  not an uncommon thing right ?

Thanks,

Peter.
9 REPLIES 9

meyerd
Champ on-the-rise
Champ on-the-rise
Hi petercahyadi,

activiti will only manage the link between an execution and a persistent JPA Entity. If you want to add an instance of a Jpa Entity as a process variable you have to make sure it is in managed state. If it is a new instance, call entityManager.persist() before adding it as a process variable.

newbbie
Champ in-the-making
Champ in-the-making
Hi Peter,

I am new to Activiti. I have a requirement almost like the one you mentioned here, passing a JPA object as an input parameter to a java service task, expect the Jpa object is not transient. Would you mind share your code for reference? That will be really helpful.

Thanks in advance,
Newbbie

divya1
Champ in-the-making
Champ in-the-making
Hi Daniel Meyer,

   Could you please share your intuition on this development. I feel its a common use case where people fill in some values for an entity through UI as its just a POJO, use it in activiti process to refine and save the value ultimately. Or If I have to customize activiti code to work in this way, could you please let me know where to customize the code.

Thanks,
-Divya

trademak
Star Contributor
Star Contributor
Daniel is not part of the Activiti community anymore. The use case you are describing is supported, only for new entities you have to make sure the entity manager is aware of the entity.

Best regards,

ollib
Champ in-the-making
Champ in-the-making
i guess the best solution is to create and save the empty entity early on in the process and then store it in activiti as a jpa process variable. actitivi definitely needs the id for an jpa entity.

wdavilaneto
Champ in-the-making
Champ in-the-making
This is not a, let us say, "nice" answer. He probably needs an transaction inside the workflow. It means it cant be persisted before this transaction. Activity must be capable of create an entity inside his own transaction.

rafaelduqueestr
Champ in-the-making
Champ in-the-making
Hi everyone, I'm having the same problem, i need to create a entity when the process started.

<code>
org.activiti.engine.ActivitiIllegalArgumentException: Value of primary key for JPA-Entity cannot be null
at org.activiti.engine.impl.variable.JPAEntityMappings.getIdString(JPAEntityMappings.java:174) ~[activiti-engine-5.16.4.jar:5.16.4]
at org.activiti.engine.impl.variable.JPAEntityMappings.getJPAIdString(JPAEntityMappings.java:87) ~[activiti-engine-5.16.4.jar:5.16.4]
at org.activiti.engine.impl.variable.JPAEntityVariableType.setValue(JPAEntityVariableType.java:68) ~[activiti-engine-5.16.4.jar:5.16.4]
at org.activiti.engine.impl.persistence.entity.VariableInstanceEntity.setValue(VariableInstanceEntity.java:184) ~[activiti-engine-5.16.4.jar:5.16.4]
at org.activiti.engine.impl.persistence.entity.VariableInstanceEntity.create(VariableInstanceEntity.java:80) ~[activiti-engine-5.16.4.jar:5.16.4]
at org.activiti.engine.impl.persistence.entity.VariableInstanceEntity.createAndInsert(VariableInstanceEntity.java:67) ~[activiti-engine-5.16.4.jar:5.16.4]
at org.activiti.engine.impl.persistence.entity.VariableScopeImpl.createVariableInstance(VariableScopeImpl.java:386) ~[activiti-engine-5.16.4.jar:5.16.4]
at org.activiti.engine.impl.persistence.entity.ExecutionEntity.createVariableInstance(ExecutionEntity.java:1163) ~[activiti-engine-5.16.4.jar:5.16.4]
at org.activiti.engine.impl.persistence.entity.VariableScopeImpl.createVariableLocal(VariableScopeImpl.java:297) ~[activiti-engine-5.16.4.jar:5.16.4]
at org.activiti.engine.impl.persistence.entity.VariableScopeImpl.createVariableLocal(VariableScopeImpl.java:284) ~[activiti-engine-5.16.4.jar:5.16.4]
at org.activiti.engine.impl.persistence.entity.VariableScopeImpl.setVariable(VariableScopeImpl.java:264) ~[activiti-engine-5.16.4.jar:5.16.4]
at org.activiti.engine.impl.persistence.entity.VariableScopeImpl.setVariable(VariableScopeImpl.java:247) ~[activiti-engine-5.16.4.jar:5.16.4]
at org.activiti.engine.impl.persistence.entity.VariableScopeImpl.setVariables(VariableScopeImpl.java:190) ~[activiti-engine-5.16.4.jar:5.16.4]
at org.activiti.engine.impl.cmd.StartProcessInstanceCmd.execute(StartProcessInstanceCmd.java:103) ~[activiti-engine-5.16.4.jar:5.16.4]
at org.activiti.engine.impl.cmd.StartProcessInstanceCmd.execute(StartProcessInstanceCmd.java:37) ~[activiti-engine-5.16.4.jar:5.16.4]
at org.activiti.engine.impl.interceptor.CommandInvoker.execute(CommandInvoker.java:24) ~[activiti-engine-5.16.4.jar:5.16.4]
at org.activiti.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:57) ~[activiti-engine-5.16.4.jar:5.16.4]
at org.activiti.spring.SpringTransactionInterceptor$1.doInTransaction(SpringTransactionInterceptor.java:47) [activiti-spring-5.16.4.jar:5.16.4]
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133) [spring-tx-4.0.7.RELEASE.jar:4.0.7.RELEASE]
at org.activiti.spring.SpringTransactionInterceptor.execute(SpringTransactionInterceptor.java:45) [activiti-spring-5.16.4.jar:5.16.4]
at org.activiti.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:37) [activiti-engine-5.16.4.jar:5.16.4]
at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:40) [activiti-engine-5.16.4.jar:5.16.4]
at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:35) [activiti-engine-5.16.4.jar:5.16.4]
at org.activiti.engine.impl.RuntimeServiceImpl.startProcessInstanceByKey(RuntimeServiceImpl.java:77) [activiti-engine-5.16.4.jar:5.16.4]
at br.gov.mprj.mgp2.api.business.adapter.ActivitiAdapter.iniciarProcessoParametro(ActivitiAdapter.java:22) [classes/:na]
at specs.incluirParametro.IncluirParametroCamposObrigatoriosAcceptance.incluirParametroComNome(IncluirParametroCamposObrigatoriosAcceptance.java:28) [test-classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.7.0_67]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[na:1.7.0_67]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_67]
at java.lang.reflect.Method.invoke(Method.java:606) ~[na:1.7.0_67]
</code>
This is my process definition:
<code>
    <process id="processParametro">
        <startEvent id="iniciarProcessoParametro"/>
        <sequenceFlow sourceRef="iniciarProcessoParametro" targetRef="criarParametro"/>
        <serviceTask id="criarParametro"
                     name="Incluir Parametro"
                     activiti:expression="${parametroService.create(parametro)}"
                     activiti:resultVariable="parametroCriado"/>
        <sequenceFlow sourceRef="criarParametro" targetRef="avaliarInclusaoParametro"/>
        <userTask id="avaliarInclusaoParametro"
                  name="Avaliar Inclusão de Parametro"/>
        <sequenceFlow sourceRef="avaliarInclusaoParametro" targetRef="finalizarProcessoParametro"/>
        <endEvent id="finalizarProcessoParametro"/>
    </process>
</code>

And this is my java code:
<java>
    public Parametro iniciarProcessoParametro(Parametro parametro) {
        Map<String, Object> variaveis = new HashMap<>();
        variaveis.put("parametro", parametro);
        ProcessInstance processParametro = runtimeService.startProcessInstanceByKey("processParametro", variaveis);
        Object parametroCriado = runtimeService.getVariable(processParametro.getId(), "parametroCriado");

        return (Parametro) parametroCriado;
    }
</java>
Parametro is a @Entity with the id = null because the entity has not been persited yet.

My dought is: is it possible to use any @Entity with activiti param before the entity to be persisted? Or i need to pass each field to be persisted when i have a transient entity? I wouldn't to like to create a POJO only to pass my fields to activiti process…

rafaelduqueestr
Champ in-the-making
Champ in-the-making
Sorry ollib, but this "workaround" is not so good.

I think the activiti doesn't have a good solution for this, I can't pass a jpa entity before I had been persisted yet, so I need to pass each entity fields with param for the process … this isn't a good idea =/

ollib
Champ in-the-making
Champ in-the-making
you can construct your JPA entitiy in a java service task and save it there. the more interesting question is: is it saved in the same transaction as the things that are going on in activiti. perhaps somebody has a code and configuration example for this. i would appreciate it very much.