Unable to cast the result of Database query in business variable

1
0
-1

Hello,

I'm using Bonita 7.10 Community edition

I'm trying to store the result of an Oracle query in a business variable. Quite simple to say, but after half-a-day of multiple trials, It still doesn't work and I don't understand why.

My business object :
OMTestErr
ErrIntegrationCode String
ErrFileName String

My pool variable : varTestErr, created after OMTestErr, declared as multiple

The query of the database connector (it's a dummy query, just for testing) :
select e.INT_INTEGRATION_CODE, e.FILE_NAME from ERR_ERRORS e
where e.INT_INTEGRATION_CODE='8083'

For the output, I've first selected "graphical mode" and "n lines x n columns"
in the next screen, I've entered "varTestErr" in the left box.
In the right box "tableResult" cannot be overwritten, which means : "varTestErr takes the value of tableResult".

When I run the process, I get the following error in the engine log :

2020-05-06 17:19:24.603 +0200 INFOS: org.bonitasoft.connectors.database.jdbc.JdbcConnector script select e.INT_INTEGRATION_CODE, e.FILE_NAME from ERR_ERRORS_TB e
where l2.int_integration_code='8083'
2020-05-06 17:19:24.603 +0200 INFOS: org.bonitasoft.connectors.database.jdbc.JdbcConnector separator null
2020-05-06 17:19:24.603 +0200 INFOS: org.bonitasoft.connectors.database.jdbc.JdbcConnector driver oracle.jdbc.driver.OracleDriver
2020-05-06 17:19:24.603 +0200 INFOS: org.bonitasoft.connectors.database.jdbc.JdbcConnector url jdbc:oracle:thin:@XX.X.X.X:XXXX:XXXXXX
2020-05-06 17:19:24.603 +0200 INFOS: org.bonitasoft.connectors.database.jdbc.JdbcConnector outputType table
2020-05-06 17:19:28.718 +0200 GRAVE: org.bonitasoft.engine.execution.work.InSessionBonitaWork THREAD_ID=268 | HOSTNAME=MYDESKTOP | TENANT_ID=1 | The work [ExecuteConnectorOfActivity: flowNodeInstanceId = 460056, connectorDefinitionName = recup erreurs integ] failed. The failure will be handled.
2020-05-06 17:19:28.722 +0200 GRAVE: org.bonitasoft.engine.execution.work.InSessionBonitaWork THREAD_ID=268 | HOSTNAME=DESKTOP-KA8IUST | TENANT_ID=1 | java.lang.ClassCastException : "class java.util.ArrayList cannot be cast to class org.bonitasoft.engine.bdm.Entity (java.util.ArrayList is in module java.base of loader 'bootstrap'; org.bonitasoft.engine.bdm.Entity is in unnamed module of loader org.apache.catalina.loader.ParallelWebappClassLoader @a403a42)"
java.lang.ClassCastException: class java.util.ArrayList cannot be cast to class org.bonitasoft.engine.bdm.Entity (java.util.ArrayList is in module java.base of loader 'bootstrap'; org.bonitasoft.engine.bdm.Entity is in unnamed module of loader org.apache.catalina.loader.ParallelWebappClassLoader @a403a42)
at org.bonitasoft.engine.operation.MergeEntityAction.execute(MergeEntityAction.java:50)
at org.bonitasoft.engine.operation.EntitiesActionsExecutor.executeAction(EntitiesActionsExecutor.java:45)
at org.bonitasoft.engine.operation.BusinessDataAssignmentStrategy.computeNewValueForLeftOperand(BusinessDataAssignmentStrategy.java:45)
at org.bonitasoft.engine.core.operation.impl.OperationServiceImpl.calculateRightOperandValue(OperationServiceImpl.java:124)
at org.bonitasoft.engine.core.operation.impl.OperationServiceImpl.executeOperators(OperationServiceImpl.java:106)
at org.bonitasoft.engine.core.operation.impl.OperationServiceImpl.execute(OperationServiceImpl.java:95)
at org.bonitasoft.engine.core.connector.impl.ConnectorServiceImpl.executeOutputOperation(ConnectorServiceImpl.java:191)
at org.bonitasoft.engine.connector.ConnectorServiceDecorator.executeOutputOperation(ConnectorServiceDecorator.java:109)
at org.bonitasoft.engine.execution.work.ExecuteConnectorWork.evaluateOutput(ExecuteConnectorWork.java:127)
at org.bonitasoft.engine.execution.work.ExecuteConnectorOfActivity.evaluateOutput(ExecuteConnectorOfActivity.java:75)
at org.bonitasoft.engine.execution.work.ExecuteConnectorWork.lambda$executeOutputOperationsAndContinue$1(ExecuteConnectorWork.java:187)
at org.bonitasoft.engine.transaction.JTATransactionServiceImpl.executeInTransaction(JTATransactionServiceImpl.java:274)
at org.bonitasoft.engine.execution.work.ExecuteConnectorWork.executeOutputOperationsAndContinue(ExecuteConnectorWork.java:186)
at org.bonitasoft.engine.execution.work.ExecuteConnectorWork.lambda$work$0(ExecuteConnectorWork.java:157)
at java.base/java.util.concurrent.CompletableFuture$UniAccept.tryFire(Unknown Source)
at java.base/java.util.concurrent.CompletableFuture.postComplete(Unknown Source)
at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)

I have tried to use the script method :
in the operations output definition screen, I've entered :"varTestErr takes the value of script()"
and I've entered the following script :

import groovy.json.JsonBuilder;
import com.company.model.OMTestErr;
List errTable = new ArrayList();
def jsonb = new JsonBuilder();
while(resultset.next()){
def ligne = jsonb {
ErrIntegrationCode(resultset.getString("INT_INTEGRATION_CODE"))
ErrFileName(resultset.getString("FILE_NAME"))
}
errTable.add(ligne);
}
return errTable;

but the result is just the same.

Thank you for helping me !

Thierry.

6 answers

1
0
-1

Bonjour et merci à Emmanuel et Pierre-Yves pour vos réponses et le temps que vous y avez passé.

J'arrête là mes expérimentations, je transfère le "bébé" à un collègue développeur, et retourne à mon métier de concepteur.

Si nous décidons de poursuivre avec Bonita, je pense qu'il sera effectivement indispensable de faire appel à une prestation de conseil et d'expertise auprès de Bonitasoft.

Bien cordialement,
Thierry.

1
0
-1

Bonjour,

J'ai testé votre processus .bos.

En supprimant la tache Select de connection à Oracle (je n'avais pas de base Oracle sous la main sur laquelle monter facilement le me schema que vous), j'arrive bien à exécuter la tâche View, qui utilisent les données du formulaire pour valoriser l'objet métier OMTestErrEnreg.

La preuve en image (Console H2 des données métier, ouvert depuis le Studio):

Par ailleurs, je n'ai pas d'erreur dans les logs.

Au vu de l'erreur de classloader, pouvez-vous vérifier que vous n'avez pas le .jar du BDM à plusieurs endroits dans le classpath (opération manuelle hasardeuse?).

Pour le reste, vous avez bien compris les notions et les avez parfaitement mises en oeuvre.

Cordialement,

Emmanuel

1
0
-1

Hello,

Let me more clear about the sequence. This community tool does not allow to share images, so only by text.

1/ Assuming you have a BDM Variable "varTestErr"

2/ Create a Service task "LoadMyBdm"

3/ go to Execution / Operation, add an operation

4/ on the left part, reference your "varTestErr" variable

5/ on the right part, clicks on the pencil

6/ the Edit Expression is then visible. Choose "Query". You should have in Queries "FindBy_INT_INTEGRATION_CODE" or something like this. Select it.

7/ in the bottom of panel, give as "integration code" you should have the value 8083

That's it.

Comments

Submitted by brt6178_1422639 on Tue, 05/12/2020 - 18:06

Bonjour, je passe au français car j'ai du mal à comprendre et sans doute du mal à m'exprimer en anglais.

Encore merci pour votre réponse, mais je ne crois pas que ça corresponde à mon besoin : je ne veux pas faire de requête sur les données internes Bonita mais sur une base de données externe Oracle, qui contient en l'occurrence une table d'erreurs que je veux récupérer dans mon processus et afficher ensuite dans un formulaire.

Il me faut donc une créer tâche avec un connecteur "database-oracle11g".

Je reformule et simplifie mon cas de test :

Dans le paramétrage du connecteur, j'ai une requête sql (qui dans le projet-cible est beaucoup plus complexe et paramétrée avec des variables du processus) : "select NUM_LIGNE, CODE_ERR, DESC_ERR from TABLE_ERR".

Et je veux donc récupérer le résultat de cette requête (potentiellement plusieurs milliers de lignes) dans une variable.

J'ai donc créé un objet métier : OMErreur, composé de 3 attributs, numLigne (Integer), codeErr (String) et descErr (String).

J'ai créé une variable de pool varErreur, à partir de cet objet métier, et je l'ai déclarée "Multiple".

Dans le paramétrage du connecteur Oracle, dans les opérations de sortie, j'ai donc mis :

varErreur "prend la valeur de" newScript(), et défini le script comme suit (j'ai testé plein de variantes, mais aucune ne fonctionne):

import groovy.json.JsonBuilder;

List listeErreurs = new ArrayList();
def jsonb = new JsonBuilder();
while(resultset.next()){
def ligne = jsonb {
numLigne(resultset.getLong("NUM_LIGNE"))
codeErr(resultset.getString("CODE_ERR"))
descErr(resultset.getString("DESC_ERR"))
}
listeErreurs.add(ligne);
}

return listeErreurs;

J'ai systématiquement une erreur d'incompatibilité de type java entre le résultat retourné (arrayList) et la variable de destination (list).

J'ai également essayé de créer un objet métier OMListeErreur avec comme attribut ligneErreur, de type OMErreur, et coché Multiple, puis j'ai créé ma variable de pool varErreur à partir de OMListeErreur (non déclarée Multiple, puisque l'objet métier l'est déjà). Mais le résultat est le même !

J'ai pourtant l'impression que mon cas n'est pas compliqué et que c'est du B.A.BA de Bonita, mais je ne trouve pas la solution.

Et dans tous les exemples disponibles sur le forum, je n'en trouve aucun dont je puisse m'inspirer.

Submitted by Pierre-yves Monnet on Tue, 05/12/2020 - 22:56

Bonjour,

Nous pouvons basculer en Francais :-)

Ok, je comprends maintenant que vous n'utiliser pas le BDM.

Peut etre pourriez vous créer un processus simple et le partager ?

Si je comprends bien:

Vous avez créer un "objet métier" et vous l'avez enregistré dans les variables de pool.

comment est définie votre objet OMErreur? Si j'en crois votre type, cette variable est defini comme etant

* un ArrayList ?

* un "jsonb" / is Multiple ?

C'est le "def ligne" qui est ici primordial. D'ou vient la class "jsonb" ? Un jar externe ? Une classe que vous avez defini vous meme et importé dans le studio ?

?

Dans votre cas, j'aurais tendance à privilegié les classes de base de Java.

OMErreur : java.util.Map / IsMultiple OU java.util.List() a la definition

Dans le connecteur de Sortie, je ferais une operation disant :

OMErreur / Prends la valeur /

Le script devient

while(resultset.next()){
Map ligne = new HashMap<>();

ligne.put("numLigne", resultset.getLong("NUM_LIGNE") );
ligne.put("codeErr",resultset.getString("CODE_ERR"));
ligne.put(""descErr",resultset.getString("DESC_ERR"))
listeErreurs.add(ligne);
}

return listeErreurs;

et il faut verifier en bas de la fenetre que le type retourné est bien un java.util.List

Partager un processus ne contenant que le connecteur, la variable et nous pourrons détecter d'ou vient le probleme.

Cordialement,

Submitted by brt6178_1422639 on Wed, 05/13/2020 - 12:25

Bonjour Pierre-Yves,

Le type de retour indiqué en base de fenêtre de script est bien " java.util.List".

Mon objet métier est un objet java.util.List que j'ai créé dans Studio ("définir le Modèle de Données Métier"). Sauf si j'ai loupé quelque chose, on n'a pas la possibilité de définir un type particulier, juste d'indiquer les attributs et de préciser si ils sont multiples ou non (en l'occurrence, non).

J'ai essayé de créer directement une variable de processus, mais pour le type Objet Java, la seule classe proposée est java.util.List.

Concernant la classe jsonb, j'avais copié/collé un exemple de script trouvé sur le forum. Je n'ai pas le souvenir de l'avoir importé spécifiquement (peut-être en important des exemples de processus ?).

Cela dit, j'ai modifié le script pour coller à votre suggestion, mais le résultat est le même.

Lorsque je teste le connecteur (bouton Test sur l'IHM), il m'affiche une fenêtre de Warning avec
- le message "Une donnée de sortie non-sérialisable peut être transformée en objet sérialisable en utilisant l'éditeur d'expressions"
- le contenu du résultat sous forme d'ArrayList :
>(c) ArrayList : [{errFileName=xxxxxxxxx,errIntegrationCode=8083,errNumLigne=1},etc.]
-un message "AVERTISSEMENT : étendre une méthode l'appellera sur l'objet"

Si j'exécute quand même le processus, dans la log, j'ai l'erreur "java.lang.ClassCastException: class java.util.HashMap cannot be cast to class org.bonitasoft.engine.bdm.Entity (java.util.HashMap is in module java.base of loader 'bootstrap'; org.bonitasoft.engine.bdm.Entity is in unnamed module of loader org.apache.catalina.loader.ParallelWebappClassLoader @36082505)".

voici un lien vers un processus simplifié ne contenant que l'activité posant problème :

https://sinalys-my.sharepoint.com/:u:/g/personal/tbrochard_groupe-sinaly...

Submitted by Pierre-yves Monnet on Mon, 05/18/2020 - 19:34

Hello,

Merci pour l'exemple, tout devient plus clair.

la variable varErreur est déclaré dans la partie "Business variable" : c'est donc bien une variable de BDM, d'ou le class cast exeption.

1/ soit c'est une variable de BDM et il faut faire comme indiquer dans le précédent message, et ne pas utiliser de connecteur ORACLE.

2/ soit elle provient d'une base externe ORACLE via le connecteur ORACLE, et dans ce cas ce n'est pas une variable de BDM. Elle doit donc etre déclaré dans la partie "Pool Variable" en tant que "ArrayList"

Si apres, vous voulez sauvegarder cette donnée externe dans une variable de BDM, il faut la lire en tant que variable de processus LIST, puis faire un transfert des données vers une variable de BDM

Pour accelerer vos développements, nous disposons d'un service de consultant. Vous pouvez prendre contact avec Bonitasoft (dans la page contact) avec un de nos représentants. Nous sommes heureux d'aider ainsi plus rapidement nos clients.

1
0
-1

Hello Emmanuel and Pierre-Yves (and other readers),

I'm sorry but I don't understand what you both mean.

@Emmanuel :
- yes my query returns many records,
- inside the output script, the return variable is declared as ArrayList
- I put the script result into a varTestErr which is declared as multiple in the pool
so I don't see the point.

@Pierre-Yves :
- again, my query must return many records (in the target scope, it may return thousands of lines)
- I don't understand the "operation" suggestion
- you say " the query must return the persistenceId" : should I add something to my query ( select e.INT_INTEGRATION_CODE, e.FILE_NAME from ERR_ERRORS e) ? a line number ?
- I don't understand either how to modify my script. Sorry but I'm not an expert in Java/Groovy/Json languages. my script is as follow :

import groovy.json.JsonBuilder;
import com.company.model.OMTestErr;
List errTable = new ArrayList();
def jsonb = new JsonBuilder();
while(resultset.next()){
def ligne = jsonb {
ErrIntegrationCode(resultset.getString("INT_INTEGRATION_CODE"))
ErrFileName(resultset.getString("FILE_NAME"))
}
errTable.add(ligne);
}
return errTable;

- if I add "errTable.add(OMTestErrDAO.findByPersistence(resultSet.getLong("persistenceId")))" then the script is incorrect

Is there somewhere a complete example of a process accessing a database to retrieve a list with multiple occurrences ?

Regards,

Thierry

1
0
-1

Hello Thierry,

According to the error message (ClassCastException: class java.util.ArrayList cannot be cast to class org.bonitasoft.engine.bdm.Entity) and the script you wrote, you are returning an ArrayList from the script, and you declared that the result varTestErr is of type OMTestErr, which is your Business Variable (all business variables inherits from org.bonitasoft.engine.bdm.Entity).

So, to fix your problem, you need:

  • either to return one simple OMTestErr from the script (make sure the return type you declare in the script is of type OMTestErr)
  • or return the ArrayList (if you have a query that returns various lines, it makes sense), declare the return type of type List / ArrayList, and store it (using "takes value of") in a variable of type List, that is a variable declared as OMTestErr and that is multiple ("multiple "tick box" checked)

I hope this clear things up,

HTH

Emmanuel

1
0
-1

Hello,

The variable must be a List of HashMap, which is what the connector produced.

Your variable is a BDM, so Bonita has already all the tooling to do that.

1/ Create an operation

2/ setup in the left part your variable

3/ in the right part, click on the pencil. In the Edit Expression, choose "Query", then select the Business Object and then the query (findByCode in your example). You can create in the BDM your own query.

If you want to use your own query (a complex one), you can use the JDBC Connector, but then

1/ the query must return the persistenceId

2/ then you the manual, not the graphical, because you have some code to do

3/ code will be something like

List listOMTest = new ArrayList();

while (resultset.next() )

{

listOMTest .add( OMTestErrDAO.findByPersistence( resultSet.getLong( "persistenceId" ));

}

return listOMTest;

Hope this help !

Notifications