Utilisation du service de requête dynamique
Parfois, dans le processus de développement, il est préférable de faire appel au service de requête dynamique plutôt qu'au service de requête EJB normale (Enterprise JavaBeans), également appelée requête de déploiement. Pendant les tests, par exemple, le service de requête dynamique peut être utilisé lors de l'exécution de l'application. Vous ne serez donc pas obligé de redéployer votre application.
Pourquoi et quand exécuter cette tâche
- Vous devez définir une requête pour l'exécution de l'application et non pour le déploiement.
- Vous devez renvoyer plusieurs zones CMP ou CMR à partir d'une requête (un seul élément peut être indiqué dans la clause SELECT pour les requêtes de déploiement). Pour plus d'informations, voir la rubrique initulée Exemple : Requêtes EJB.
- Vous souhaiter renvoyer une expression calculée dans la requête.
- Vous voulez utiliser des méthodes d'objet et de bean dans l'instruction de requête. Pour plus d'informations, voir la rubrique intitulée Expressions de chemin.
- Vous voulez tester de manière interactive une requête EJB au cours du développement sans devoir redéployer l'application à chaque fois que vous mettez à jour une requête de recherche ou de sélection.
L'API de requête dynamique est un bean session sans état. son utilisation est similaire à celle de n'importe quel autre bean d'application EJB J2EE. Il est inclus dans com.ibm.websphere.ejbquery du module de l'API.
remote interface = com.ibm.websphere.ejbquery.Query
interface home distante = com.ibm.websphere.ejbquery.QueryHome
local interface = com.ibm.websphere.ejbquery.QueryLocal
interface home locale = com.ibm.websphere.ejbquery.QueryLocalHome
Etant donné qu'elle utilise moins de mémoire du serveur d'applications, l'interface locale garantit de meilleures performances d'EJB globales que l'interface distante.
Procédure
Exemple
- Utilisation de l'interface distante pour une requête dynamique.
Lorsque vous exécutez une requête EJB (Enterprise JavaBeans) dynamique via l'interface distante, vous appelez la méthode executeQuery sur l'interface Query. La méthode executeQuery est dotée de l'attribut de transaction REQUIRED pour cette interface ; il n'est donc pas nécessaire d'établir de manière explicite un contexte de transaction pour l'exécution de la requête.
Lorsque vous exécutez une requête EJB (Enterprise JavaBeans) dynamique via l'interface distante, vous appelez la méthode executeQuery sur l'interface Query. La méthode executeQuery est dotée de l'attribut de transaction REQUIRED pour cette interface ; il n'est donc pas nécessaire d'établir de manière explicite un contexte de transaction pour l'exécution de la requête.
Commencez par les instructions d'importation suivantes :
import com.ibm.websphere.ejbquery.QueryHome; import com.ibm.websphere.ejbquery.Query; import com.ibm.websphere.ejbquery.QueryIterator; import com.ibm.websphere.ejbquery.IQueryTuple; import com.ibm.websphere.ejbquery.QueryException;
Rédigez ensuite les instructions de requête sous forme d'une chaîne, comme dans l'exemple suivant qui permet d'extraire les noms et les références d'EJB de salariés sous-payés :
String query = "select e.name as name , object(e) as emp from EmpBean e where e.salary < 50000";
Créez un objet Query à l'aide d'une référence issue de la classe QueryHome (cette classe définit la méthode executeQuery). A noter que dans un souci de simplicité, l'exemple qui suit emploie le nom JNDI de la requête dynamique pour l'objet Query :
InitialContext ic = new InitialContext(); Object obj = ic.lookup("com/ibm/websphere/ejbquery/Query"); QueryHome qh = ( QueryHome) javax.rmi.PortableRemoteObject.narrow( obj, QueryHome.class ); Query qb = qh.create();
Vous devez ensuite préciser une taille maximale de l'ensemble de résultats de la requête, définie dans l'objet QueryIterator, qui est inclus dans la classe QueryIterator. Cette classe est comprise dans le package de l'API QueryIterator. Cet exemple définit une taille maximale de l'ensemble de résultats de 99 :
QueryIterator it = qb.executeQuery(query, null, null ,0, 99 );
L'itérateur contient une collection d'objets IQueryTuple, qui sont des enregistrements des valeurs de collection renvoyées Selon les critères de l'instruction de requête de cet exemple, chaque nuplet de ce scénario contient une valeur name et une valeur object(e). Pour afficher le contenu des résultats de la requête, utilisez le code suivant :while (it.hasNext() ) { IQueryTuple tuple = (IQueryTuple) it.next(); System.out.print( it.getFieldName(1) ); String s = (String) tuple.getObject(1); System.out.println( s); System.out.println( it.getFieldName(2) ); Emp e = ( Emp) javax.rmi.PortableRemoteObject.narrow( tuple.getObject(2), Emp.class ); System.out.println( e.getPrimaryKey().toString()); }
Ce programme peut générer une sortie similaire à celle présentée ci-dessous :name Bob emp 1001 name Dave emp 298003 ...
Il convient pour terminer, d'intercepter et de traiter les exceptions éventuelles. Une exception peut être générée si la syntaxe d'une instruction de requête est erronée ou si une erreur se produit lors de l'exécution d'un traitement. L'exemple suivant intercepte et traite ces exceptions :} catch (QueryException qe) { System.out.println("Query Exception "+ qe.getMessage() ); }
Gestion des collections importantes de résultats de la requête de l'interface distante
Si vous prévoyez que la requête renvoie une collection de taille significative, vous pouvez la programmer de sorte qu'elle renvoie les résultats sous forme de plusieurs quantités plus petites, plus facilement gérables. Utilisez les paramètres skipRow et maxRow sur la méthode distante executeQuery pour extraire la réponse en la fractionnant. Exemple :
int skipRow=0; int maxRow=100; QueryIterator it = null; do { it = qb.executeQuery(query, null, null ,skipRow, maxRow ); while (it.hasNext() ) { // display result skipRow = skipRow + maxRow; } } while ( ! it.isComplete() ) ;
- Utilisation de l'interface locale pour une requête dynamique.
Lorsque vous exécutez une requête EJB (Enterprise JavaBeans) dynamique via l'interface locale, vous appelez la méthode executeQuery sur l'interface QueryLocal. Cette dernière ne lance pas de transaction pour la méthode ; il est donc nécessaire d'établir de manière explicite un contexte de transaction pour l'exécution de la requête.
Remarque : Pour ce faire, l'exemple ci-après appelle les méthodes begin() et commit(). Une alternative consiste à imbriquer le code de la requête dans une méthode EJB exécutée dans un contexte de transaction.Commencez le code de la requête par les instructions d'importation suivantes :
import com.ibm.websphere.ejbquery.QueryLocalHome; import com.ibm.websphere.ejbquery.QueryLocal; import com.ibm.websphere.ejbquery.QueryLocalIterator; import com.ibm.websphere.ejbquery.IQueryTuple; import com.ibm.websphere.ejbquery.QueryException;
Rédigez ensuite les instructions de requête sous forme d'une chaîne, comme dans l'exemple suivant qui permet d'extraire les noms et les références d'EJB de salariés sous-payés :
String query = "select e.name, object(e) from EmpBean e where e.salary < 50000 ";
Créez un objet QueryLocal à l'aide d'une référence issue de la classe QueryLocalHome (cette classe définit la méthode executeQuery). A noter que dans l'exemple qui suit, ejb/query est utilisé comme référence d'EJB locale désignant le nom JNDI de la requête dynamique (com/ibm/websphere/ejbquery/Query) :
InitialContext ic = new InitialContext(); QueryLocalHome qh = ( LocalQueryHome) ic.lookup( "java:comp/env/ejb/query" ); QueryLocal qb = qh.create();
La dernière portion du code initie une transaction, appelle la méthode executeQuery et affiche les résultats de la requête. La classe QueryLocalIterator est instanciée car elle définit l'ensemble de résultats de la requête Cette classe est incluse dans le module de l'API Class QueryIterator. L'itérateur perd sa validité à la fin de la transaction ; vous devez donc l'utiliser dans la même portée de transaction que l'appel executeQuery.
userTransaction.begin(); QueryLocalIterator it = qb.executeQuery(query, null, null); while (it.hasNext() ) { IQueryTuple tuple = (IQueryTuple) it.next(); System.out.print( it.getFieldName(1) ); String s = (String) tuple.getObject(1); System.out.println( s); System.out.println( it.getFieldName(2) ); EmpLocal e = ( EmpLocal ) tuple.getObject(2); System.out.println( e.getPrimaryKey().toString()); } userTransaction.commit();
Dans la plupart des cas, l'objet QueryLocalIterator fonctionne à la demande. En effet, il renvoie les données de manière incrémentale : pour chaque extraction d'enregistrement de la base de données, la méthode next() doit être appelée sur l'itérateur (il existe des cas où l'itérateur ne fonctionne pas à la demande. Pour plus d'informations, voir la section relatives aux interfaces de requête locales dans Remarques concernant les performances de requête dynamique).
Etant donné que l'ensemble de résultats de la requête se matérialise de manière incrémentale dans le serveur d'applications, il est aisé de contrôler sa taille. Ainsi, lors d'une exécution test vous pouvez décider que seuls quelques nuplets du résultat de la requête sont nécessaires. Dans ce cas, vous devez utiliser un appel de la méthode close() sur l'objet QueryLocalIterator pour fermer la boucle de la requête. Des ressources SQL sont alors libérées et utilisées par l'itérateur. Dans le cas contraire, ces ressources ne sont pas libérées tant que la totalité de l'ensemble de résultats ne s'est pas accumulée en mémoire ou que la transaction ne s'est par terminée.