Valeur de getFieldValue incorrecte après business object select

Bonjour,

Dans un adapter, je récupère une liste de business objects et effectue un traitement si l’un des champs de ces business objects a une valeur attendue.

Le problème que j’ai est qu’après sélection de l’objet via son row_id, la valeur du getFieldValue qui m’est retournée dans mon code est différente de celle qui est en base. Je soupçonne une mauvaise utilisation de ma part du mécanisme de sélection de business objet, peut-être me manque-t-il une sorte de clean ou release entre chaque sélection ?

Voici la portion concernée de mon code :

ArrayList<String> acp1ids = acp.getRowIdsFromAlternativeIdentifier(manufacturingCode, "RAFT_CODE", false);
for (int i = 0; i < acp1ids.size(); i++) {
	String acpId = acp1ids.get(i);
	acp.select(acpId);
	appendLog("acpId: " + acpId + "; sitesAcpType: " + acp.getFieldValue("sitesAcpType"));
	if (acp.getFieldValue("sitesAcpType").equals("P05")) { // P05 = FAB
		acp1id = acpId;
	}
}
if (acp1id == null || acp1id.isEmpty()) {
	throw new Exception("No P05 access point found on line: " + lineNumber);
}

La liste acp1ids retournée par l’appel à la première ligne est correcte et correspond bien à ce qui est attendu.
En revanche, le log appendLog("acpId: " + acpId + "; sitesAcpType: " + acp.getFieldValue("sitesAcpType")); donne : acpId: 4464; sitesAcpType: P11 (par exemple) ce qui résulte dans le passage dans l’exception (car j’attends P05 et c’était l’access point pour ce row_id qui avait son champ sitesAcpType qui valait P05).

Le problème est qu’en base, un SELECT acp_type FROM sites_accesspoint WHERE row_id = 4464 retourne P05 (normal) alors que mon code recupère P11 (pas normal).

<object>.select(<row ID>) renvoie un booléen donc il faudrait déjà voir ce que ça renvoie (si ça renvoie false ça veut dire que le select ne s’est pas fait correctement et les valeurs dans l’objet sont celles qui y étaient avant).

J’ai vérifié et sans surprise, le select ne s’est en effet pas fait correctement (renvoie false). Comment je peux investiguer ce qui peut causer cela ?

N’y a t-il pas un filtrage (search spec) statique ou dynamique qui limite ce que retourne un select logique vs un select direct en base ?

Peut être y a-t-il des filtres résiduels sur d’autres attributs, si oui il faudrait faire un act.resetFilters() avant la boucle pour être sûr.

Etc.

Après un resetFilters, le select s’effectue bien correctement. Je ne comprends pas pourquoi il ne se déroule pas bien sans en revanche, je n’ai pas mis de filtres sur l’objet.

Comment est instancié la variable acp ?

Voilà une version simplifiée de mon adapter avec tout (et seulement) ce qui concerne acp :

package com.simplicite.adapters.Sites;

import com.simplicite.objects.Sites.SitesAccessPoint;
import com.simplicite.util.ObjectField;
import com.simplicite.util.Tool;
import com.simplicite.util.tools.BusinessObjectTool;
import java.util.ArrayList;

public class SitesRaftEN extends com.simplicite.commons.Sites.SitesCommonAdapter {
	private static final long serialVersionUID = 1L;

	private boolean debug = true;
	private SitesAccessPoint acp;
	protected transient BusinessObjectTool acpTool;

	@Override
	public String preProcess() {
		acp = (SitesAccessPoint)getGrant().getTmpObject("SitesAccessPoint");
		acpTool = new BusinessObjectTool(acp);
		apa = (SitesAccessPointAttachment)getGrant().getTmpObject("SitesAccessPointAttachment");

		return super.preProcess();
	}

	@Override
	public String processValues(long lineNumber, String[] values) {
		try {

			String manufacturingAccount = values[0].substring(48, 56).trim();// Manufacturing Supplier account (‘00’ + account on 6 char)
			String manufacturingSiteCode = values[0].substring(56, 58).trim(); // Manufacturing Supplier site code

			if (manufacturingAccount.isEmpty()) {
				throw new Exception("manufacturingAccount is empty on line: " + lineNumber);
			}

			String manufacturingCode = manufacturingAccount.substring(2, 8) + manufacturingSiteCode;

			String acp1id = null;
			ArrayList<String> acp1ids = acp.getRowIdsFromAlternativeIdentifier(manufacturingCode, "RAFT_CODE", false);
			if (acp1ids == null) {
				throw new Exception("acp1ids is empty on line: " + lineNumber);
			}
			for (int i = 0; i < acp1ids.size(); i++) {
				String acpId = acp1ids.get(i);
				acp.resetFilters();
				if (debug) {
					boolean selectSuccess = acp.select(acpId);
					appendLog("selectSuccess: " + selectSuccess + "acpId: " + acpId + "; sitesAcpType: " + acp.getFieldValue("sitesAcpType"));
				}
				if (acp.getFieldValue("sitesAcpType").equals("P05")) { // P05 = FAB
					acp1id = acpId;
				}
			}
			if (acp1id == null || acp1id.isEmpty()) {
				throw new Exception("No P05 access point found on line: " + lineNumber);
			}
		} catch (Exception e) {
			appendLog(e.getMessage());
			appendError(values); // Mark current line as rejected
		}
		nbLines++;
		return null;
	}

	@Override
	public void postProcess() {
		super.postProcess();
	}
}

Ok acp est donc toujours la même instance (tmp_<object>).

Elle conserve donc tout ce qui a été positionné sur cette instance d’objet partout ailleurs où elle est utilisée (données, filtres, changement dynamiques de paramétrage, etc…).

Une instance d’objet est liée à une session utilisateur (le tmp_<object> d’une session 1 n’est donc pas le même tmp_<object> d’une session 2) mais le principe de Simplicité est de laisser les instances en l’état en mémoire dans la session.

Donc appeller getTmpObject() ne créé pas une nouvelle instance mais renvoie juste l’instance tmp_<object> existante en mémoire dans la session (en la créant la 1ère fois).

Quand on utilise une instance, surtout l’instance tmp_<object>, il faut donc toujours bien s’assurer de la remettre dans l’état souhaité avant de l’utiliser, typiquement vider les filtres via un resetFilters() avant un select ou un search et/ou de la “nettoyer” après.

Une autre bonne pratique c’est d’utiliser des instances dédiées pour un contexte donné (via getObject("<arbitrary prefix>_MyObject", "MyObject"). Ce qui ne dispense pas de ce qui précède. Attention à ne pas non plus multiplier inutilement les instances car chaque instance consomme de la mémoire.

Bref il faut faire preuve de bon sens et de parcimonie dans l’usage des instances d’objet tout en comprenant bien ce que c’est et en s’assurant d’être thread safe au sein de la session (typiquement quand on utilise des instances dans des tâches planifiées et/ou celles associées à la session système Grant.getSystemAdmin() pour lesquelles le synchronized s’impose)

En général on insiste bien sur ce point en formation car c’est une source d’incompréhensions classiques…