|
EJB 3 - Les Entreprise Java Bean version 3 (JavaBeans)
2.3.Spécifications des différents beans
Les différents composants d’un bean (classe / interface / descripteur de déploiement) se simplifient avec la version 3. Nous allons voir en détails chacun de ces changements.
2.3.1.Classe bean et interfaces
-
Classe bean
Le développeur définit la classe de l’entreprise bean et l’annote en utilisant le concept de metadata en Java. Ces annotations peuvent être appliquées à la classe du bean pour spécifier les informations liées au conteneur, pour demander un service ou encore pour fournir différentes informations de configuration pour le deployer. L’élément requis par la classe du bean est la définition du type de bean à utiliser (Stateless Statefull Entity ). Il est bien entendu possible de ne pas utiliser les annotations metadata et utiliser alors un descripteur de déploiement.
-
Interfaces
Dans EJB 3, les interfaces sont des « pures » interfaces java. C'est-à-dire que vous n’avez plus besoin d’hériter de « EJBObject » ou « EJBLocalObject ».
-
Business (métier)
Seuls les session bean et les message driven bean ont besoin d’une business interface (nous verrons plus tard comment cela se passe avec les entity bean). Cependant les message driven bean utilise typiquement l’interface javax.jms.MessageListener (à cause de l’API JMS : Java Message Service). La classe bean peut implémenter directement l’interface métier. Une classe bean peut cependant implémenter plusieurs interfaces à la fois à condition de respecter ces quelques règles :
- Si le bean implémente qu’une seule interface, alors ça doit être l’interface métier (business). Celle-ci doit donc être la local ou la remote (ou bien entendu les deux) et l’interface doit porter l’annotation indiquant le type d’interface (Local Remote )
- Un bean peut implémenter plusieurs interfaces. Dans ce cas, chacune des interfaces doit explicitement être marquée par les annotations indiquant le type de l’interface métier (Local ou Remote).
- Une interface métier ne doit pas étendre javax.ejb.EJBObject ou javax.ejb.EjbLocalObject
Les annotations liées aux interfaces sont :
- @Local : pour spécifier que l’interface est de type local
- @Remote : pour spécifier que l’interface est de type remote
-
Exceptions
Les méthodes des interfaces métiers peuvent déclarer des exceptions liées à l’application. Cependant vous n’avez plus à spécifier, pour les méthodes de la remote interface, l’exception : java.rmi.RemoteException. Si un problème survient au niveau du protocole, une EJBException est jetée par le conteneur.
-
Home
Les home interfaces ont été éliminées. Les sessions beans n’ont plus besoin d’avoir de home interface. Un client peut acquérire une référence du session bean directement. En ce qui concerne les Entity, la création d’entité se fait simplement par l’opérateur new (d’instantation) et par l’utilisation de l’EntityManager API (vu plus loin dans l’essentiel).
2.3.2.Callbacks et Classe Listener Callback
Une méthode peut être définie comme étant une méthode « callback » afin de recevoir une notification au niveau du cycle de vie d’un session ou message-driven bean. Les méthodes « callback » doivent être annotées avec les annotations de callback. Voici un example : @Stateful public class ShoppingCartBean implements ShoppingCart { private float total; private Vector productCodes; public int someShoppingMethod(){...}; ... @PreDestroy endShoppingCart() {...}; } Vous pouvez définir des méthodes callback directement dans la classe de votre bean. Dans ce cas, la signature de votre méthode doit respecter : public void <METHOD> () Cependant vous pouvez également utiliser une classe « listener de callback » qui peut être utilisée à la place des méthodes intégrées au bean. Cela dans le but de séparer le traitement du cycle de vie du traitement logique lié au bean. Dans ce cas, il vous faudra utiliser l’annotation CallbackListener dans la classe du bean avec laquelle vous voulez associer votre classe listener. Une classe listener callback doit définir obligatoirement un constructeur public ayant aucun argument. Les annotations utilisées dans la classe listener sont les mêmes que celles utilisées dans la classe du bean. Cependant la signature des méthodes de la classe listener est : public void METHOD (Object) où Object est le type du bean actuel.
2.3.3.Session Bean
-
Stateless
-
Bean
La classe du bean doit être annotée avec « @Stateless ». Cette classe n’a pas à implémenter l’interface javax.ejb.SessionBean.
-
Callbacks
Les session bean supportent les callback d’évènement suivant :
La callback PostConstruct intervient après toutes les dépendances d’injection effectuée par le conteneur et avant le premier appel de la méthode métier. La callback PreDestroy est appelée au moment ou l’instance de du bean est détruite.
-
Dépendance d’injection
Si un session bean utilise le mecanisme de dépendance d’injection afin d’acquérir des références à d’autres ressources ou d’autres objets de l’application, alors le conteneur injecte ces références avant que toute méthode liée au cycle de vie de l’EJB ou méthode métier soit appelée.
-
Interceptor
Il est possible de créer des méthodes d’interception d’appel des méthodes métiers d’un bean. Ces méthodes peuvent être définies directement dans le bean ou dans une classe d’interceptor et appliquées aux méthodes métiers du bean.
-
Exemple
Voici un exemple très simple de SessionBean (type stateless). Il faut bien entendu définir la ou les interface(s) que notre bean va implémenter. Ici nous définissons simplement l’interface Remote qui contient une seule méthode : getHello(). De plus nous l’annotons avec l’annotation @Remote afin de la déclarer comme interface distante. Cependant il est possible de ne pas l’annoter directement mais de la déclarer dans la classe du Bean, cela permet une entière compatibilité avec des clients utilisant la JVM 1.4. Voici le code de l’interface distante (Remote) : import javax.ejb.Remote; @Remote public interface RemoteHello { public String getHello(); } La classe du bean implémente l’interface précédente et est annotée par @Stateless afin de la déclarée comme Session Bean Stateless. Voici le code de cette classe : import javax.ejb.Stateless; import com.society.testejb3.interfaces.RemoteHello; @Stateless public class HelloBean implements RemoteHello { public String getHello() { return "Hello world"; } }
-
Stateful
Statefull session bean a les mêmes caractéristiques que le Stateless. Seuls les points en plus sont expliqués dans les sections ci-dessous.
-
Bean
La classe du bean doit être annotée avec « @Statefull ». Cette classe n’a pas à implémenter l’interface javax.ejb.SessionBean.
-
Callbacks
Les session bean supportent les callback d’évènement suivant :
- PostConstruct
- PreDestroy
- PostActivate
- PrePassivate
Les callback PrePassivate et PostActivate représentent les anciennes méthodes : « ejbActivate » et « ejbPassivate ».
-
Removal method
L’annotation @Remove permet de définir une méthode indiquant la suppression du Statefull Bean lorsque cette méthode aura fini son exécution (exécution normal ou anormale).
2.3.4.Entity Bean
Les entity beans subissent de grandes modifications avec la version EJB3. En effet, les spécifications ce ceux-là sont séparées de celles des Session Bean et Message Driven. Nous allons donc voir l’ensemble des besoins et spécifications que les entity bean doivent respecter.
-
Bean Class
La classe du bean doit tout d’abord être annotée avec @Entity. Le constructeur doit avoir aucun argument (comme le constructeur par défaut) mais il se peut que la classe ait d’autres constructeurs en plus de celui-là. Ce constructeur doit être public ou protected. Si l’entity a la possiblité d’être utilisé comme objet détaché (comme dans une relation client de type Remote), la classe doit implémenter l’interface java.io.Serializable. La classe de l’entité ne doit pas être finale et aucune de ses méthodes aussi. Les entités supportent l’héritage, le polymorphisme. Une classe abstraite peut être une entité. L’état d’une entité est représenté par des variables d’instances qui doivent correspondrent à des propriétés JavaBean.
-
Champ persistants & propriétés
L’état persistant d’une entité est accessible par l’implémentation du fournisseur de persistance soit comme dans le cas des JavaBeans soit par les variables d’instances.
- Si l’entité est annotée avec access=FIELD, le fournisseur accède directement aux variables d’instances que ne sont pas Transient, afin de les rendre persistantes.
- Si l’entité est annotée avec access=PROPERTY, le fournisseur accède aux champs persistants par le biais des méthodes setter/getter. Celles-ci doivent être public ou protected.
Vous pouvez utiliser les collections dans vos JavaBeans, les types supportés sont : java.util.Collection, java.util.Set, java.util.List, java.util.Map. L’utilisation des génèriques est également possible, par exemple : Set<Order> est autorisé. Vous pouvez utiliser l’ensemble des types suivants pour vos champs persistants : java.lang.String, java.math.BigInteger, java.util.Date, java.util.Calendar, java.sql.Date, java.sql.Time, java.sql.Timestamp et les tous types sérializables.
-
Création d’instance
La création d’instance d’une entité, se fait simplement par la simple instanciation de la classe par l’opréateur new. Cependant la persistance de l’objet créé est réalisée avec l’EntityManager API. Nous verrons cela plus en détail ci-dessous.
-
Primary key & Identité
Une simple clé primaire (non composite) doit correspondre à un simple champ persistant ou une propritété de la classe. Une clé composite doit également correspondre soit à un champ persistant soit à un ensemble de champs ou propriétés. Une classe pour la clé primaire doit impérativement être définie pour représenter une clé composite. Vous utiliserez une clé composite lorsque vous ferez un mapping avec une base de données dans laquelle la clé est composée de plusieurs champs. Le type de la clé primaire peut être : n’importe quel type primitif de java, n’importe quel wrapper (Integer, Double …), java.lang.String, java.util.Date, java.sql.Date. On n’utilise généralement pas des types float ou double en tant que clé primaire. Voici les différentes règles à respecter pour une clé primaire composite :
- La classe de la clé primaire doit être public et doit avoir un construteur public sans argument
- Si l’accès est de type PROPERTY, la classe peut être soit public soit protected
- La classe doit être Serializable
- La classe doit définir les méthodes equals et hashCode
- Si la clé primaire est mappée à de multiples champs ou propriétés, le noms des champs de la clé primaire doivent être les mêmes que ceux utilisés dans le bean.
-
Mapping par défaut
Lorsque vous allez créer votre bean, il vous faudra mapper l’ensemble de vos champs. Cependant il est possible d’utiliser le mapping par défaut. Ce mapping sera utilisé lorsque vous n’aurez pas utilisé d’annotation pour un champ (qu’il soit persistant ou relationnel). Le mapping standard est utilisé lorsque c’est un champ persistant (non relationnel) : @Basic.
-
Exemple simple
Voici un exemple de POJO (Plain Oriented Java Object) simple. Il permet de définir la structure de la table « COUNTRY ». Il implémente Serializable afin de pouvoir être envoyé directement à un client distant (et donc de passer à travers un réseau). Les annotations obligatoires sont :
- @Entity qui déclare la classe comme étant une entité
- @Id qui déclare la clé primaire (generate permet de définir le type de génération à utiliser pour cette clé. GeneratorType.AUTO définit une clé auto incrémentée).
Les autres annotations ne sont pas obligatoires mais permettent de spécifier plus précisément différents paramètres :
- @Table définit le nom de la table à utiliser
- @Basic définit le type d’un champ persistant (par défaut cet type est utilisé)
import java.io.Serializable; import javax.persistence.Entity; import javax.persistence.GeneratorType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "COUNTRY") public class Country implements Serializable { private int id; private String name; public Country() { } @Id(generate = GeneratorType.AUTO) @Basic public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
-
Relations
Les relations entre entités peuvent être: one-to-one, one-to-many, many-to-one, many-to-one, many-to-many. Si vous souhaitez relier deux entités, vous pourrez utiliser les annotations : OneToOne, OneToMany, ManyToOne, ManyToMany. Pour les associations qui ne spécifient pas le type de bean « target » (utilisation de Collection sans les génériques), il est nécessaire de spécifier l’entité qui est la cible. Les relations peuvent être unidirectionnelles ou bidirectionnelles.Un relation bidirectionnelle est accessible des deux côtés (des deux beans) alors qu’une relation unidirectionnelle n’est valable que depuis un bean. Voici les règles à respecter pour les relations :
- L’inverse parti d’une relation bidirectionnelle doit être référencée par l’utilisation de l’attribut mappedBy des éléments : OneToOne, OneToMany, ManyToOne, ManyToMany. Cet attribut désigne la propriété ou le champ de l’entité qui est le propriétaire de la relation.
- Vous n’avez pas besoin de définir l’attribut mappedBy dans le cas des relations bidirectionnelles OneToMany et ManyToOne.
- Pour les relations OneToOne, la partie propriétaire correspond à la partie contenant la clé étrangère.
- Dans le cas de la relation ManyToMany, les deux côtés sont propriétaires !
-
Exemple relationnel
Ce bean intègre une relation avec un autre bean de type : ManyToOne. Nous pouvons remarquer que cette relation met en évidence un champ de type Country dans ce bean. L’utilisation des annotations relationnelles décrit plus en détail la relation :
- @ManyToOne définit le type de la relation (ManyToOne)
- @JoinColumn définit la colonne de jointure pour cette relation
package com.society.stockmanager3.entities.beans; import java.io.Serializable; import javax.persistence.Entity; import javax.persistence.GeneratorType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; @Entity @Table(name = "CITY") public class City implements Serializable { protected int id; protected String name; protected Country country; public City() { } public City(String name) { this.name = name; } @ManyToOne(optional = false) @JoinColumn(name = "countryId") public Country getCountry() { return country; } public void setCountry(Country country) { this.country = country; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Id(generate = GeneratorType.AUTO) public int getId() { return id; } public void setId(int id) { this.id = id; } }
-
Héritage
Une entité peut hériter d’un autre. Les entités supportent l’héritage, l’association polymorphique, les requêtes polymorphiques. Les classes abstraites ou concrètes peuvent être des entités. Des entités peuvent hériter de classes (non entité) et inversement. Lorsqu’une entité hérite d’une autre entité, les clés primaires doivent être du même type.
-
Abstract class
Une classe abstraite peut être une entité. La différence avec une classe concrète est qu’il n’est pas possible d’instancier cette entité directement. Cependant vous pouvez utiliser la classe abstraite en tant que target dans une requête (ce qui vous permettra par exemple de récupérer l’ensemble des sous types de votre abstract classe). Vous devez bien entendu définir votre classe abstraite comme une entité (via Entity).
-
Héritage
Une entité peut avoir une classe parente (abstract ou concrète) qui ne soit pas une entité. La superclasse sert seulement pour l’héritage d’un comportement. En effet, aucune des proprités de la super classe (non entité) ne sera rendu persistante. Vous ne pourrez pas passer en argument une classe « non entité » à l’EntityManager. Il existe 3 stratégies de mapping :
- Une table pour une hiérarchie de classe
- Une table par classe concrète
- Stratégie consistant à séparer les champs spécifiques d’une classe fille dans une table séparée par rapport à la table contenant les données de la table parente. Une jonction est alors faite pour instantier la classe fille.
-
Une table pour une hiérarchie de classe
Dans cette stratégie, toutes les classes de la hiérarchie sont mappées dans une même et unique table. La table a une colonne qui définit le type de données (pour l’enregistrement). Cette stratégie fournit de nombreux avantages concernant les relations polyphormiques entre entités et pour les requêtes. Cependant, les données utilisent plus de place car l’ensemble des colonnes n’est pas toujours utilisé (suivant le type de données enregistrées).
-
Exemple
Voici un exemple d’héritage de type : ONLY_ONE_TABLE (cela est fait par l’utilisation de l’annotation @Inheritance, c’est le type par défaut d’héritage). Il faut définit une colonne permettant de différencier les différents types d’entité (cela est effectuée par @DiscrimanatorColumn). De plus il faut indiquer la valeur de « descriminitation » pour chaque entité (via discriminatorValue, attribut de @Inheritance). import java.io.Serializable; import javax.persistence.DiscriminatorColumn; import javax.persistence.Entity; import javax.persistence.GeneratorType; import javax.persistence.Id; import javax.persistence.Inheritance; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; @Entity @Table(name = "record") @Inheritance(discriminatorValue="B") @DiscriminatorColumn(name="record_type") public class Record implements Serializable { protected int id; protected double saving; protected double result; public Record() { } public Record(int id, double result, double saving) { this.id = id; this.result = result; this.saving = saving; } @Id(generate = GeneratorType.AUTO) public int getId() { return id; } public void setId(int id) { this.id = id; } public double getResult() { return result; } public void setResult(double result) { this.result = result; } public double getSaving() { return saving; } public void setSaving(double saving) { this.saving = saving; } public void setFund(Fund fund) { this.fund = fund; } public void setInvestor(Investor investor) { this.investor = investor; } } Voici la classe fille TimeRecord : import java.sql.Timestamp; import javax.persistence.Entity; import javax.persistence.Inheritance; @Entity @Inheritance(discriminatorValue = "T") public class TimeRecord extends Record { private Timestamp ts; public TimeRecord() { } public TimeRecord(double result, double saving, Timestamp ts) { super(id, result, saving); this.ts = ts; } public Timestamp getTs() { return ts; } public void setTs(Timestamp ts) { this.ts = ts; } } Voici le résultat au niveau de la base de données (une seule table pour deux entités) : 
-
Une table par classe concrète
Chaque concrète classe est liée à sa propre table. Cela signifie que toutes les propriétés de la classe (incluant les propriétés héritées) sont mappées dans la table liée à la classe. Voici les inconvénients de cette statégie :
- Mauvaise gestion des relations polymorphiques
- Le SGDB doit effectuer une requête de type UNION pour récupérer l’ensemble des données de l’ensemble de la hiérarchie des classes.
-
Exemple
Voici un exemple d’utilisation de l’héritage de type : TABLE_PER_CLASS. Une petite modification a également été faite au niveau des accès aux données. L’accès se fait directement via les variables d’instances (définit via access=AccessType.FIELD) ; ce type d’accès vous évite de créer des getters et setters pour chacune de vos propriétés. Le type d’héritage est déclarée dans l’annotation @Inheritance grâce à l’attribut (strategy=InheritanceType.TABLE_PER_CLASS). import javax.persistence.AccessType; import javax.persistence.Basic; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; @Entity(access=AccessType.FIELD) @Inheritance(strategy=InheritanceType.TABLE_PER_CLASS) public abstract class Animal { @Id protected Integer id; @Basic protected String name; @Basic protected Integer age; public Animal() { } } Voici une classe fille : Bird de la classe Animal : import javax.persistence.AccessType; import javax.persistence.Basic; import javax.persistence.Entity; @Entity(access=AccessType.FIELD) public class Bird extends Animal { @Basic protected Integer nbPlume; } Voici une classe fille : Dog de la classe Animal : import javax.persistence.AccessType; import javax.persistence.Basic; import javax.persistence.Entity; @Entity(access=AccessType.FIELD) public class Dog extends Animal { @Basic protected String race; public Dog() { } } Voici le résultat en base de données (toutes les données sont intégrées dans chacune des tables) : 
-
Une table pour une partie de données spécifiques
Dans cette stratégie, la classe mère des entités est représentée par une simple table. Chaque sous classe est représentée par une table séparée contenant l’ensemble des propriétés spécifiques à la classe fille (et bien entendu la clé primaire). La clé primaire de la classe parente est utilisée comme clé étrangère avec la clé primaire de la classe fille. Cette stratégie fournit un bon support pour les relations polyphormiques entre entités. L’inconvénient est qu’elle requière l’utilisation de plusieurs jointures entre les tables lors de la récupération de données. Dans le cas de hiérarchie importante (grande profondeur de hiérarchie) cela peut entrainer de mauvaise performance.
-
Exemple
Seul le type d’héritage change dans cet exemple. Il est déclaré à : « InheritanceType.JOINED ». import javax.persistence.AccessType; import javax.persistence.Basic; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; import javax.persistence.Version; @Entity(access=AccessType.FIELD) @Inheritance(strategy=InheritanceType.JOINED) public abstract class Employee { @Id protected Integer id; @Version protected Integer version; @Basic protected String name; public Employee() { } } Voici une classe fille PartTimeEmployee de Employee : import javax.persistence.AccessType; import javax.persistence.Basic; import javax.persistence.Entity; import javax.persistence.Inheritance; import javax.persistence.PrimaryKeyJoinColumn; @Entity(access=AccessType.FIELD) @Inheritance(discriminatorValue="PT") @PrimaryKeyJoinColumn(name="PT_EMPID") public class PartTimeEmployee extends Employee { @Basic protected Float hourlyWage; public PartTimeEmployee() { } } Voici une classe fille FullTimeEmployee de Employee : import javax.persistence.AccessType; import javax.persistence.Basic; import javax.persistence.Entity; import javax.persistence.Inheritance; import javax.persistence.PrimaryKeyJoinColumn; @Entity(access=AccessType.FIELD) @Inheritance(discriminatorValue="FT") @PrimaryKeyJoinColumn(name="FT_EMPID") public class FullTimeEmployee extends Employee { @Basic protected Integer salary; public FullTimeEmployee() { } } Voici le résultat au niveau de la base de données (optimisation de la place prise par les données) :
-
Opérations sur les entités
Nous allons décrire, dans cette partie, l’utilisation de l’EntityManager API afin de gérer le cycle de vie d’une instance d’entité et l’utilisation de l’API Query qui permet d’exécuter des requêtes sur des objets persistants.
-
EntityManager
Une instance d’un EntityManager est associée avec un contexte de persistance. Ce contexte est un ensemble d’instances d’entités pour lequel pour n’importe quelle entité, il existe une unique instance de l’entité. Grâce à ce contexte, les instances d’entités et leur cycle de vie peuvent être gérés. The EntityManager interface définit l’ensemble des méthodes que l’on peut utiliser pour intérargir avec le contexte de persistance. Cette API est utilisée pour créer et supprimer des instances d’entités ou encore pour en trouver via leur clé primaire ou par le biais de requêtes sur les objets eux-mêmes. L’ensemble des entités qui peuvent être gérer par une instance d’EntityManager est définit par une unité persistante. Cette unité définit l’ensemble des classes qui sont liées à l’application et qui peuvent être liées au sein d’un même mapping dans une même base de données. Voici les méthodes les plus utilisées concernant cette interface :
- persist : permet de persister une instance d’une entité
- merge : permet de sauvegarder les modifications faites concernant une entité dans le contexte
- remove : permet de supprimer une instance d’une entité
- find : permet de récupérer une entité en fonction de sa clé primaire
- refresh : permet de réinitialiser l’état d’un objet par rapport à la base de données (annule toute modification)
- createQuery : méthode retournant un objet Query permettant d’effectuer des requêtes plus spécialisées
-
Gestion du cycle de vie d’une entité
Nous allons décrire, dans cette section, les opérations liées à la gestion du cycle de vie des entités. Une instance peut avoir l’état de : nouveau, gérée, détachée ou supprimée.
- Une nouvelle instance n’a pas d’identité persistante, elle n’est pas encore associée au contexte de persistance.
- Une instance gérée est une instance avec une identité persistante, elle est associée avec un contexte persistant.
- Une instance détachée est une instance ayant une identité persistante mais qui n’est plus liée au contexte de persistance.
- Une instance supprimée est une instance ayant une identité persistante, associée avec le contexte de persistance mais qui est programmée pour être supprimée de la base de données.
-
Persister une instance d’une entité
Une nouvelle instance d’entité devient persistante et gérée lorsque l’on invoke la méthode persist avec l’instance en paramètre ou lorsque cette opération est effectuée en cascade. Voici quelques détails concernant cette opération (E est une entité) :
- Si E est une nouvelle entité, elle passe à l’état gérée. L’entité E sera ajoutée en base de données avant que la transaction soit commitée ou lorsque que la méthode flush est appelée.
- Si E est une entité préexistante, elle est ignorée par l’opération de persistance. Cependant l’opération est de type « cascade » sur l’ensemble des entités référencées par E, si les relations de E vers d’autres entités sont marquées avec cascade=PERSIST ou cascade=ALL (nous verrons plus tard l’ensemble des différentes annotations)
- Si E est une entité supprimée, elle passe en état : « gérée »
- Si E est détachée, alors une exception de type IllegaleArgumentException est lancée
-
Supprimer une instance
Vous pouvez supprimer une entité en appelant la méthode remove de l’EntityManager et en lui passant l’entité à supprimer, ou alors l’entité peut être supprimée par « cascade ». Voici quelques détails concernant cette opération (E est une entité) :
- Si E est une nouvelle entité, l’opération est ignorée. Cependant l’opération de suppression est de type « cascade » sur l’ensemble des entités référencées par E, si les relations sur ces autres entités sont marquées comme cascade=REMOVE ou cascade=ALL.
- Si E est détachée, alors une exception de type IllegaleArgumentException est lancée
- Si E est supprimée, elle est ignorée par cette opération
-
Synchronisation avec la base de données
Cette opération met à jour l’état d’un bean par rapport aux valeurs de la base de données. Cette synchronisation écrit l’ensemble des modifications des entités persistantes et de leurs relations. Le fournisseur de persistance est permit d’effectuer une synchronisation vers la base de données à d’autres moment, comme par exemple avant l’exécution d’une requête. La méthode flush permet de forcer une synchronisation. Cette synchronisation est appliquée aux entités associées au contexte de persistence. Il existe l’annotation FlushMode qui vous permet de spécifier de façon plus détaillée l’opération de synchronisation. Voici quelques détails concernant cette opération (E est une entité) :
- Si E est une instance ayant l’état gérée, elle est synchronisée avec la base de donnés
- Pour toutes les entités Y référencées par E, si la relation entre ces deux entités est annotée : cascade=PERSIST ou cascade=ALL, l’opération de persistance est appliquée à Y.
- Pour toutes les entités Y référencées par E, si la relation entre ces deux entités n’est pas annotée : cascade=PERSIST ou cascade=ALL :
- Si Y est nouveau, une exception IllegaleStateException est jetée et la transaction est « rolled back ».
- Si Y est détaché, cela dépend du type de relation qu’il y a entre Y et E. Si E connaît la relation, aucune modification est effectuée par rapport à la relation.Si Y connaît la relation, le comportement est indéfinit.
- Si E est supprimée, l’entité est supprimée de la base de données. Aucune opération de « cascade » n’est lancée.
-
Entités détachées
Lorsque le contexte de persistance est stoppé, toutes les entités rattachées à ce contexte sont détachées. L’application doit alors accéder avec prudence aux propriétés de ces objets. Voici les états sur lesquels il faut faire attention :
- Les champs persistants marqués par : fetch=LAZY
Les instances détachées continuent de vivre à l’extérieur du contexte de persistance et leur état n’est plus guaranti d’être mis à jour avec la base de données. Une entité détachée peut simplement être le résultat d’une sérialisation d’une entité ou lorsque vous le passer à un autre tiers de votre application (une remote interface par exemple). Dans ce cas, les mêmes règles s’appliquent.
-
« Merge » opération
L’opération « merging » vous permet de propager l’état « détaché » d’une entité vers l’état persistant grâce à l’EntityManager. Voici quelques détails concernant cette opération (E est une entité) :
- Si E est une instance détachée, elle est copiée dans une instance « gérée » E’ préexistante de la même entité ou alors une nouvelle instance de E est créée.
- Si E est une nouvelle instance, une nouvelle instance « gérée » E’ est créée et l’état de E est copié dans E’.
- Si E est une instance supprimée, une IllegaleArgumentException est jetée.
-
Exemple d’utilisation
//TODO import java.util.Collection; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import com.society.stockmanager3.entities.beans.City; import com.society.stockmanager3.entities.beans.Country; import com.society.stockmanager3.exceptions.NotFindException; import com.society.stockmanager3.sessions.interfaces.LocalStorage; import com.society.stockmanager3.sessions.interfaces.RemoteStorage; @Stateless public class StorageBean implements RemoteStorage, LocalStorage { // Injection @PersistenceContext(unitName = "stockManager") protected EntityManager em; public StorageBean() { } public Collection<Country> getAllCountry() { return em.createQuery("from Country c").getResultList(); } public void addCountry(Integer countryId, String name) { Country country = new Country(); country.setId(countryId); country.setName(name); em.persist(country); } public void removeCountry(Integer countryId) { em.remove(em.find(Country.class, countryId)); } public void addCity(Integer cityId, String name, Integer countryId) { City city = new City(); city.setCountry(em.find(Country.class, countryId)); city.setId(cityId); city.setName(name); em.persist(city); } public void removeCity(Integer cityId) { em.remove(em.find(City.class, cityId)); } }
2.3.5.Message Driven Bean (MDB)
-
Business Interface
L’interface à utiliser, est bien entendu, l’interface définit par JMS (Java Message Service), c'est-à-dire javax.jms.MessageListener. Cette interface peut dépendre du type de messagerie sur laquelle vous voulez connecter votre bean (c’est généralement JMS). Il existe cependant un nouveau type de Message Driven Bean, ce sont les Message Driven Bean POJOs. Ce type permet d’interconnecter un client avec un MDB et de permettre à ce client de faire de l’appel de méthode à distance via JMS. Ce type d’utilisation se rapproche des Session bean. Cependant la connexion se fait alors par message JMS et non via RMI. De plus les appels et la réception de résultat sont asynchrones.
-
Bean Class
Vous devez simplement annoter votre classe avec @MessageDriven. Votre classe doit également implémenter la méthode onMessage(Message msg) dans le cas d’une utilisation directe avec JMS. Il vous faut également définir les différents paramètres à utiliser pour la connexion à la destination … Pour cela on utilisera les propriétés : destinationType et destination. Voici un exemple de paramétrage d’un bean : @MessageDriven(activateConfig = { @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"), @ActivationConfigProperty(propertyName="destination", propertyValue="queue/mdb") }) Cette annotation est bien entendu à utiliser sur la classe d’implémentation du bean.
-
Exemple
//TODO import javax.annotation.Resource; import javax.ejb.ActivationConfigProperty; import javax.ejb.MessageDriven; import javax.ejb.MessageDrivenContext; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.TextMessage; @MessageDriven(activateConfig = { @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"), @ActivationConfigProperty(propertyName="destination", propertyValue="queue/simpleQueue") }) public class SimpleMessageBean implements MessageListener { @Resource MessageDrivenContext mdc; public void onMessage(Message inMessage) { TextMessage msg = null; try { if (inMessage instanceof TextMessage) { msg = (TextMessage) inMessage; System.out.println ("MESSAGE BEAN: Message received: " + msg.getText()); } else { System.out.println ("Message of wrong type: " + inMessage.getClass().getName()); } } catch (JMSException e) { e.printStackTrace(); mdc.setRollbackOnly(); } catch (Throwable te) { te.printStackTrace(); } } }
|
|
 |