Business methods Transaction Asynchronous
Bonjour, je me pose beaucoup de questions autour du terme business methods.
En effet lors de mes dernières expériences, je constate plusieurs choses qui me laissent perplexe :
Soit le code suivant simplifié...
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| @Stateless
@LocalBean
public AClass {
@EJB
BClass bclass;
@TransactionAttribute(NOT_SUPPORTED)
public void aMethod() {
bMethod();
cMethod();
bClass.dMethod();
bClass.eMethod();
}
@TransactionAttribute(REQUIRED_NEW)
public void bMethod() {
}
@Asynchronous
public void cMethod() {
}
}
public BClass {
@TransactionAttribute(REQUIRED_NEW)
public void dMethod() {
}
@Asynchronous
public void eMethod() {
}
} |
Je constate la chose suivante :
aMethod comme prévu n'est pas dans une transaction : NOT_SUPPORTED.
bMethod N'est PAS dans une transaction malgré REQUIRED_NEW
cMethod N'est PAS Asynchronous malgré Asynchronous
dMethod est dans une nouvelle transaction quoi qu'il arrive
eMethod est asynchronous
Donc si je comprends bien mes lectures et mes constatations : bMethod et cMethod ne sont pas des business method dans ce cas, mais pourrait l’être dans le cas d'un autre appel, les autres oui car elle sont appelées par extérieure de l'EJB.
Je constate aussi la même chose quand à l'application des Interceptors.
soit un interceptor :
Code:
1 2 3 4 5 6
| public AInterceptor {
@AroundInvoke
public Object process(InvocationContext ic) throws Exception {
return ic.proccess();
}
} |
puis sur la class :
Code:
1 2
| @Interceptors(AInterceptor.class)
public class AClass { ...} |
Seule la methode aMethod (appelé de l'exterieur) sera "décoré" par l'interceptor.
Déjà à ce stade, je trouve cela bien dommage et surtout pas très clair.
Maintenant un cas pratique :
Soit un ejb gérant la persistance d'une Entity
celui ci permet de persister une entité ou plusieurs entités à la fois comme suit, mais je veux que quoi qui l'arrive chaque persistance soit individuelle. Mais les transactions gérer par le container toujours.
Naïvement :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| ...
@TransactionAttribute(REQUIRED_NEW)
public void persistOk(MonEntity o) throws NokException(){
// code de persistence metant en oeuvre plusieurs persitence d'objets
... throw new NokException(); // ou pas
}
@TransactionAttribute(REQUIRED_NEW)
public void persistNok(MonEntity o) {}
@TransactionAttribute(REQUIRED_NEW)
public void persistList(Collection<MonEntity> list {
for(MonEntity o : list) {
try {
persistOk(o);
} catch(NokException ne) {
persistNok(o);
}
}
... |
on pourrait imaginer que si j'appelle persistList, chaque persistOk et persistNok soit dans leur propre transaction. Il n'en ai rien. seule persistList crée une nouvelle transaction.
Le code précédent ne marche donc pas.
En revanche si vous déplacer dans un autre EJB les methodes persistOk et persistNok. tout rentre dans l'ordre.
Quelque chose m’échappe ? Je réalise une grosse appli, et mes EJBs se compte par dizaines. et certains ont parfois plusieurs dizaines de méthode. Si mes constatation sont justes, il va falloir revoir pas mal de truc.
Mes EJBs on plutôt tendances à gérer un coté fonctionnel. un EJB pour s'occuper des Users, un, EJB pour les Groups etc... N'est ce pas comme cela qu'il faut faire ???
J'avoue être très perplexe. J'ai plutôt tendance à essayer de laisser le container gérer le plus de chose. Je sais bien que l'on peut gérer les Transaction en mode BEAN, mais je pense que normalement cela devrait être à utiliser qu'en cas d'impasse avec le container.
Je lis tout ce qui me tombe sur la main, mais les exemples sont souvent très simplistes.
Quelqu'un à t'il quelques tuyaux, ouvrages, tutos qui explique les transactions et tout ce qui va autour vraiment en détails ?
Cordialement
Personne ne constate cela ?
Je remonte le post, je suis étonné que personne n'est son avis la dessus. c'est pourtant primordiale.
toujours sur la même histoire
Encore quelques doutes.
J'utilise cette fois le pattern et l'EJB Singleton
Prenons l'exemple suivant un singleton qui sert de cache à une donné issue d'un calcul savant. (C'est pour illustrer mon problème)
Si la donné à déjà été calculé, parfait, je la retourne, si elle n'a pas encore était calculé, je la calcul et la stocke dans une Map avant de la retourner.
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Singleton
public class ConstantsManager {
private Map<String, Long> map = null;
@PostConstruct
public void init() {
map = new HashMap();
}
@Lock(LockType.READ)
public Long getConstant(String key) {
if(!map.contains(key)) return null;
return map.get(key);
}
@Lock(LockType.WRITE)
public void buildConstant(String key) {
if(!map.contains(key)) {
Long result = buildComplex(key); // methode complexe et longue
map.push(key, result);
}
}
} |
Le problème ici est évident, il serait dommage que deux processus demande de faire le même calcul couteux.
Idéalement ici l'accès à la donné en lecture est totalement concurrente.
annotée Lock(LockType.READ), la méthode getConstant n'est pas exclusive.
La map quand à elle est threadsafe.
Pour l’écriture en revanche. C'est plus complexe, même si ici l'approche est simpliste, car plusieurs demandes de calcul pour des clés différentes devraient s'attendre, l'accès à la méthode buildConstant est quand à elle exclusive. Un processus accédant à cette méthode attendrait son tour. C'est ce que l'on veut (à peut prés). Elle illustre bien mon problème.
Si un autre EJB exécute le code suivant tout va bien.
C'est l'EJB suivant qui devra être visible par mes autres services qui voudraient accéder aux constantes.
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Stateless
@LocalBean
public class ConstantServices {
@EJB
ConstantsManager contantsManager;
public Long getConstantByKey(String key) {
Long result = contantsManager.getConstant(key); // accès concurrentiel
if(result==null) {
contantsManager.buildConstant(key); // accès bloquant
result = contantsManager.getConstant(key); // accès concurrentiel
}
return result;
}
} |
Maintenant, toujours dans l'idée de ne pas coder 150 classes qui ne font pas grand chose, j'aurais été tenté d'écrire le ConstantsManager comme suit.
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Singleton
public class ConstantsManager {
private Map<String, Long> map = null;
@PostConstruct
public void init() {
map = new HashMap();
}
@Lock(LockType.READ)
public Long getConstant(String key) {
if(!map.contains(key)) buildConstant(key); // appel interne bloquant ???
return map.get(key);
}
@Lock(LockType.WRITE)
private void buildConstant(String key) {
if(!map.contains(key)) {
Long result = buildComplex(key); // methode complexe et longue
map.push(key, result);
}
}
} |
Il suffirait donc aux services nécessitant une constante de demander directement au singleton.
hors après mes constations qui m'ont fait ouvrir ce post, je pense que la deuxième solution n'est pas bonne. Car "buildConstant" n'étant pas une méthode business, le container ne peut gérer les annotations appliquées. Est ce vrai aussi dans ce cas ? Quelqu'un peut il me confirmer mes doutes ?
Au final, je trouve cela très limitatif. surtout que l'appel via la méthode getBusinessMethod ne fonctionne pas tout le temps.
Je suis très perplexe, si quelqu'un à des idées ou des lectures à me conseiller.
Merci d'avance.