JSP JavaServer Pages



Documents pareils
Introduction à JDBC. Accès aux bases de données en Java

Programmation d application Bases de données avec Java

Java DataBaseConnectivity

Programmer en JAVA. par Tama

TP Programmation Java / JDBC / Oracle

Accès aux bases de données

Quelques patterns pour la persistance des objets avec DAO DAO. Principe de base. Utilité des DTOs. Le modèle de conception DTO (Data Transfer Object)

OpenPaaS Le réseau social d'entreprise

Compte Rendu d intégration d application

Table des matières PRESENTATION DU LANGAGE DS2 ET DE SES APPLICATIONS. Introduction

Application BdD et JDBC. Introduction 1/28. I Dans tout les cas, une seule problématique. I Quelques alternatives :

La persistance des données dans les applications : DAO, JPA, Hibernate... COMPIL 2010 francois.jannin@inp-toulouse.fr 1

Optimisations des SGBDR. Étude de cas : MySQL

Connexion à SQL Server 2005 à partir du serveur d application SJSAS 9 Utilisation d une interface JDBC

INTRODUCTION A JAVA. Fichier en langage machine Exécutable

Java et les bases de données: JDBC: Java DataBase Connectivity SQLJ: Embedded SQL in Java. Michel Bonjour

1. Langage de programmation Java

Institut Supérieur de Gestion. Cours pour 3 ème LFIG. Java Enterprise Edition Introduction Bayoudhi Chaouki

Langage et Concepts de ProgrammationOrientée-Objet 1 / 40

TP JEE Développement Web en Java. Dans ce TP nous commencerons la programmation JEE par le premier niveau d une application JEE : l application web.

Création et Gestion des tables

Premiers Pas en Programmation Objet : les Classes et les Objets

TP n 2 Concepts de la programmation Objets Master 1 mention IL, semestre 2 Le type Abstrait Pile

Introduction à Java. Matthieu Herrb CNRS-LAAS. Mars

Utilisation d objets : String et ArrayList

PHP et mysql. Code: php_mysql. Olivier Clavel - Daniel K. Schneider - Patrick Jermann - Vivian Synteta Version: 0.9 (modifié le 13/3/01 par VS)

JDBC le langage Java XVI-1 JMF

Architecture Orientée Service, JSON et API REST

Java et les bases de données

Application web de gestion de comptes en banques

Pour signifier qu'une classe fille hérite d'une classe mère, on utilise le mot clé extends class fille extends mère

as Architecture des Systèmes d Information

Mise en œuvre des serveurs d application

Remote Method Invocation (RMI)

1 Introduction et installation

COMPRENDRE LES DIFFERENTS TYPES DE CONNEXION LORS DE LA

Logiciel de gestion de caisse et d ardoises

Devenez un véritable développeur web en 3 mois!

JDBC JDBC. Java DataBase Connectivity

La base de données XML exist. A. Belaïd

PHP 4 PARTIE : BASE DE DONNEES

Alfstore workflow framework Spécification technique

Mysql avec EasyPhp. 1 er mars 2006

JavaServer Pages (JSP)

Info0101 Intro. à l'algorithmique et à la programmation. Cours 3. Le langage Java

Cours intensif Java. 1er cours: de C à Java. Enrica DUCHI LIAFA, Paris 7. Septembre Enrica.Duchi@liafa.jussieu.fr

Le langage SQL Rappels

Paginer les données côté serveur, mettre en cache côté client

INTERSYSTEMS CACHÉ COMME ALTERNATIVE AUX BASES DE DONNÉES RÉSIDENTES EN MÉMOIRE

Le langage SQL pour Oracle - partie 1 : SQL comme LDD

Exceptions. 1 Entrées/sorties. Objectif. Manipuler les exceptions ;

Dialogue avec la base de données

Gestion de stock pour un magasin

Plan du cours. Historique du langage Nouveautés de Java 7

Création d une application JEE

Olivier Mondet

Cette application développée en C# va récupérer un certain nombre d informations en ligne fournies par la ville de Paris :

Table des matières L INTEGRATION DE SAS AVEC JMP. Les échanges de données entre SAS et JMP, en mode déconnecté. Dans JMP

Web Tier : déploiement de servlets

Construire une base de donnée pour bibliothèque

1. Base de données SQLite

Structure d un programme et Compilation Notions de classe et d objet Syntaxe

WEB & DÉVELOPPEMENT LES BASES DU WEB LE LANGAGE HTML FEUILLES DE STYLES CSS HISTORIQUE D INTERNET ET DU WEB LES DIFFÉRENTS LANGAGES

Cours d introduction à l informatique. Partie 2 : Comment écrire un algorithme? Qu est-ce qu une variable? Expressions et instructions

Module BD et sites WEB

Présentation du PL/SQL

Bases de données Page 1 de 11. Bases de données. Prof. : Dzenan Ridjanovic

Classe ClInfoCGI. Fonctions membres principales. Gestion des erreurs

Pratique et administration des systèmes

Prénom : Matricule : Sigle et titre du cours Groupe Trimestre INF1101 Algorithmes et structures de données Tous H2004. Loc Jeudi 29/4/2004

BTS S.I.O PHP OBJET. Module SLAM4. Nom du fichier : PHPRévisionObjetV2.odt Auteur : Pierre Barais

Une introduction à Java

Encapsulation. L'encapsulation consiste à rendre les membres d'un objet plus ou moins visibles pour les autres objets.

CREATION WEB DYNAMIQUE

Exploitation de bases de données relationnelles et orientées objet IFT287

Création d un service web avec NetBeans 5.5 et SJAS 9

Bien architecturer une application REST

Langage et Concepts de Programmation Objet. 1 Attributs et Méthodes d instance ou de classe. Travaux Dirigés no2

Programmation Web. Madalina Croitoru IUT Montpellier

Cours: Administration d'une Base de Données

Impact des robots d indexation sur le cache de second niveau de SPIP IMBERTI Christophe - SG/SPSSI/CP2I/DO Ouest 06/06/2012 mis à jour le 05/07/2012

Groupe Eyrolles, 2004 ISBN :

IBM DB2 Alphablox. d administration GC

Tutoriel d installation de Hibernate avec Eclipse

1/ Présentation de SQL Server :

Whitepaper. Méthodologie de création de rapports personnalisés SQL Server Reporting Services

Introduction aux SGBDR

Utilisation de JAVA coté Application serveur couplé avec Oracle Forms Hafed Benteftifa Novembre 2008

SIO Page 1 de 5. Applications Web dynamiques. Prof. : Dzenan Ridjanovic Assistant : Vincent Dussault

NFA 008. Introduction à NoSQL et MongoDB 25/05/2013

Présentation du langage et premières fonctions

Access et Org.Base : mêmes objectifs? Description du thème : Création de grilles d écran pour une école de conduite.

I4 : Bases de Données

clef primaire ; clef étrangère ; projection ; restriction ; jointure ; SQL ; SELECT ; FROM ; WHERE

Bases de données relationnelles

FileMaker Server 12. publication Web personnalisée avec XML

24/11/2011. Cours EJB/J2EE Copyright Michel Buffa. Plan du cours. EJB : les fondamentaux. Enterprise Java Bean. Enterprise Java Bean.

EP60.92 Projet d application pluridisciplinaire La chasse aux trésors

Tous les autres noms de produits ou appellations sont des marques déposées ou des noms commerciaux appartenant à leurs propriétaires respectifs.

Transcription:

JSP JavaServer Pages Duane Fields Éditions Eyrolles ISBN : 2-212-09228-8 2000

7 Intégration aux bases de données Dans ce chapitre : Lien entre l API JDBC et JSP Stockage et extraction des composants JSP avec un SGBD relationnel Affichage des résultats d une requête dans des pages JSP Établissement des connexions persistantes Autrefois réservées aux entreprises les plus nanties, les bases de données sont aujourd hui intégrées à la plupart des sites web, qu elles alimentent dynamiquement en données. On peut citer comme exemple d utilisation de bases de données sur le web la gestion des bandeaux publicitaires, des informations utilisateur, des listes de contacts, etc. Pour de tels besoins, les bases de données et les pages JSP forment une bonne combinaison : les bases de données relationnelles prennent en charge l organisation à moindre coût de grands ensembles de données dynamiques, tandis que les pages JSP offrent un moyen simple de présenter ces données. On obtient ainsi des applications web dynamiques bénéficiant à la fois de la puissance des bases de données relationnelles et de la souplesse de présentation des JSP. 1. JSP et JDBC À la différence d autres langages de script tels que ColdFusion, JavaScript côté serveur ou encore PHP, JSP ne propose pas de balises prédéfinies pour la connectivité à des bases de données. Les concepteurs des JSP ont en effet préféré tirer avantage de JDBC, une norme d interface Java puissante et populaire pour les bases de données.

2 JSP Java Server Pages La communication avec une base de données depuis une application JSP nécessite la présence d un pilote (driver) propriétaire écrit pour l API JDBC. Dès lors il s agit donc du même moyen d accès éprouvé et efficace mis au point par Sun. Nous verrons au Chapitre 8 qu en pratique, on masque l accès à la base de données dans une servlet ou un composant Java pour l isoler des détails de présentation de la page JSP. Deux approches différentes sont donc possibles, qui sont illustrées Figure 7-1. Figure 7-1 Types d accès possibles à des bases de données avec JSP Requête JSP Servlet JSP Accès direct à partir d une page JSP Pilote JDBC API JDBC Accès à la base gérée par une servlet ; les résultats sont passés à une page JSP Base de données L étude de l API JDBC n entre pas dans le cadre de ce livre, sans compter que bien des ouvrages traitent du sujet qui compte par ailleurs une documentation fournie. Vous trouverez en particulier des didacticiels en ligne sur le site de Sun à l adresse http://java.sun.com/products/ jdbc. Nous ne décrivons ici que l utilisation de JDBC dans des applications JSP. REMARQUE Les classes JDBC font partie du paquetage java.sql. Celui-ci doit être importé dans toute classe Java censée utiliser JDBC, en l occurrence vos pages JSP. Des extensions supplémentaires peuvent être ajoutées à la version 2.0 de l API JDBC à partir du paquetage javax.sql, si celui-ci est installé sur votre système. Si votre pilote JDBC ne se trouve pas dans le dossier de votre moteur de JSP, il faut soit l importer dans la page, soit y faire référence par son nom de classe complet. 1.1. JNDI et les autres sources de données Dans certains systèmes de script utilisant des modèles, tel ColdFusion, l accès à une base de données se fait à l aide d un identifiant unique représentant une connexion prédéfinie ou un pool de connexions fourni par l administrateur système. On fait ainsi l économie des détails de connectivité au niveau du code, puisque l on fait référence aux sources des données par un identifiant logique tel que DBEmployes ou DBVentes. De cette manière, si un nouveau pilote doit être utilisé, ou si le serveur de la base est déplacé, ou que les informations de session changent, il suffit de mettre à jour la description de la ressource ; les composants et les instructions qui font référence à la ressource nommée ne nécessitent aucun changement. JSP ne définit pas de système de gestion de ressources particulier. Il faut recourir à l interface Datasource de JDBC 2.0 et à l API JNDI (Java s Naming and Directory Interface) pour

Intégration aux bases de données CHAPITRE 7 3 fournir les services de nommage et de localisation. JNDI permet de séparer le code de l application des détails concernant la base de données, tels que le pilote, le nom d utilisateur, le mot de passe et l URI de la connexion. Pour créer une connexion à une base de données en utilisant l interface JNDI, on spécifie un nom de ressource qui correspond à une entrée dans une base de données ou un service de nommage, pour recevoir les informations nécessaires à l établissement de la connexion. Cela protège le code JSP et ses composants des modifications pouvant être apportées à la configuration de la base. Pour en savoir plus sur l utilisation de JNDI, reportez-vous au site de Sun à l adresse http://java.sun.com/products.jndi. Voici un exemple de création d une connexion à partir d une source de données définie dans le registre JNDI : Context ctx = new InitialContext(); DataSource ds = (DataSource)ctx.lookup("jdbc/DBVentes"); Connection con = ds.getconnection("username", "password"); Pour dissocier davantage le code JSP de l accès à la base et rendre l accès encore plus simple, il est possible d utiliser des balises personnalisées, qui utilisent JNDI pour l accès à des ressources nommées comme ce qui se fait dans ColdFusion et d autres langages à balises. 1.2. Les instructions préparées (PreparedStatement) Les instructions préparées, très similaires à des procédures stockées, sont des modèles de requêtes SQL réutilisables avec des paramètres différents à chaque exécution. La procédure consiste pour l essentiel à créer la requête, qui peut être tout type d instruction SQL, sans définir les valeurs de certaines variables. Les valeurs manquantes sont spécifiées avant l exécution de la requête, qui peut être réexécutée autant de fois que nécessaire. Les instructions préparées sont créées à partir d un objet Connection, comme des objets Statement normaux. En SQL, les valeurs des variables à spécifier lors de l exécution sont représentées par un point d interrogation (?) : String query = "SELECT * FROM GAME_RECORDS WHERE SCORE >? AND TEAM =?"; PreparedStatement statement = connection.preparestatement(query); L objet PreparedStatement prend en charge des méthodes d affectation de valeur de paramètre, correspondant chacune à un type de données particulier, int, long, String, etc. Elles prennent deux paramètres, d une part l indice du paramètre à spécifier et, d autre part, la valeur à lui affecter. Les valeurs d indice commencent à 1 et non à 0. Dans notre cas, pour obtenir tous les scores supérieurs à 10 000 dans l équipe «Gold», on initialisera les valeurs de paramètres de la requête avec les instructions suivantes, avant de l exécuter : statement.setint(1, 10000); // Score statement.setstring(2, "Gold"); // Nom de l équipe ResultSet results = statement.executequery(); Une fois créée, l instruction préparée peut être exécutée plusieurs fois avec différentes valeurs de paramètres. Il n est pas nécessaire de créer d instance nouvelle de la procédure stockée tant que la requête reste analogue. Plusieurs requêtes peuvent ainsi être exécutées sans créer d objet Statement. Une instruction préparée peut même être utilisée par différents composants d une application ou par différents utilisateurs d une servlet. En outre, le moteur de la base de données n analyse l instruction SQL qu une seule fois. En outre, les instructions préparées permettent le passage de paramètres via un composant Java plutôt que par un codage littéral. Dans l exemple suivant, le composant Java userbean,

4 JSP Java Server Pages initialisé à partir d un formulaire de saisie, passe des paramètres de requête, d où une simplification du code et une meilleure lisibilité : statement.setint(1, userbean.getscore()); // Score statement.setstring(2, userbean.getteam()); // Nom de l équipe ResultSet results = statement.execute(); Pour obtenir le même résultat sans recourir au passage de paramètres, il faudrait jongler avec des chaînes SQL, chose difficile dans le cas de requêtes complexes, comme le montre l exemple suivant, qui reprend le précédent mais sans faire appel à une instruction préparée : Statement statement = connection.getstatement(); String query = "SELECT * FROM GAME_RECORDS WHERE SCORE > " + userbean.getscore() + " AND TEAM = " + user.getteam() + userbean.getteam() + " "; ResultSet results = Statement.executeQuery(query); Ce dernier exemple met en évidence un autre avantage décisif de l utilisation des instructions préparées, à savoir l économie des détails de mise en forme. En effet, lorsqu une valeur de paramètre est spécifiée dans une instruction préparée à l aide d une méthode d affectation, nul besoin de se soucier des guillemets nécessaires à l identification des chaînes de caractères, ni des caractères d échappement, des conversions de dates et autres exigences de formats particuliers à la base de données. C est un avantage stratégique dans le cas de pages JSP destinées à récolter des paramètres de requête directement à partir de formulaires remplis par les utilisateurs, et de ce fait exposées à d imprévisibles erreurs de saisie et toutes sortes de caractères spéciaux. Chaque base de données étant généralement soumise à des contraintes de format particulières, notamment de date, l utilisation d instructions préparées contribue à affranchir encore votre code des spécificités propres à chaque base. 2. Les pages JSP orientées base de données Bien des moyens permettent de développer des applications bases de données avec les JSP. Dans ce chapitre, nous n allons étudier que l interaction entre JSP et une base de données, sans nous attarder sur l architecture du programme. La conception d applications JSP sera traitée au Chapitre 8 et illustrée au Chapitre 9 par un exemple de projet web utilisant une base de données. 2.1. Intégration des données d une table dans une page JSP L intégration d une base de données à une application JSP repose principalement sur l utilisation de composants JavaBeans, servant à faire le lien entre la base et les pages JSP. Chaque composant permet de récupérer ligne par ligne des données contenues dans une table, grâce à l analogie qui peut être établie entre une table de base de données et un composant : la structure d une table, telle la classe d un composant, spécifie le type et le nom des données qu elle contient ; les colonnes d une table, telles les propriétés d un composant, permettent de stocker les valeurs de chaque instance du type contenu. Enfin, les tables et les classes sont des modèles de stockage d informations et, à ce titre, n ont d utilité pratique que par les données qu elles permettent de stocker (bons d achat, détails d inventaire, etc.) ; une table vide n a pas d utilité pratique, tout comme une classe qui n a pas été instanciée.

Intégration aux bases de données CHAPITRE 7 5 En bref, les tables et les classes Java sont des modèles de données conçus pour la gestion d informations, concernant des objets ou des événements réels. Retenez ce principe, qui est à la base du développement de nombreuses applications de base de données JSP. Les bases de données ont généralement pour rôle d initialiser dynamiquement un composant Java à partir des informations d une table. La configuration de composants JSP à partir d une base de données est très simple si la structure de la table (ou les résultats d une opération de jointure entre plusieurs tables) correspondent une à une aux propriétés du composant. Dans ce cas, il suffit d invoquer les méthodes d accès aux enregistrements de la classe ResultSet et passer au composant les valeurs des colonnes de table correspondantes. Lorsqu il y a plusieurs lignes dans les résultats, il est nécessaire de créer autant de composants que de lignes. Initialisation d un composant à l aide de scriptlets Il est possible d utiliser des scriptlets JSP pour initialiser les propriétés d un composant lors de sa création, après l établissement de la connexion, avec les données contenues dans ResultSet. N oubliez pas d importer le paquetage java.sql dans la page avec la directive <%@page import="java.sql.*" %>. Dans l exemple suivant, nous utiliserons la classe ItemBean pour représenter un item particulier de l inventaire, en prenant le numéro d item dans l objet de requête : <%@ page import="java.sql.*" %> <jsp:usebean id="item" class="itembean"> <% Connection connection = null; Statement statement = null; ResultSet results = null; ItemBean item = new ItemBean(); try { Class.forName("oracle.jdbc.driver.OracleDriver"); String url = "jdbc:oracle:oci8@dbserver"; String id = request.getparameter(id); String query = "SELECT * FROM PRODUCTS_TABLE WHERE ITEM_ID = " + id; connection = DriverManager.getConnection(url, "scott", "tiger"); statement = connection.createstatement(); results = statement.executequery(query); if (results.next()) { item.setid(results.getint("item_id")); item.setdesc(results.getstring("description")); item.setprice(results.getdouble("prix")); item.setstock(results.getint("qte_disponible")); connection.close(); catch (ClassNotFoundException e) { System.err.println("Impossible de charger le pilote de la base de données."); catch (SQLException e) { System.err.println("Echec de la connexion à la base de données.");

6 JSP Java Server Pages finally { try { if (connection!= null) connection.close(); catch (SQLException e) { %> </jsp:usebean> <html> <body> <table> <tr><td>numéro d article</td><td> <jsp:getproperty name="item" property="id"/></td></tr> <tr><td>description</td><td> <jsp:getproperty name="item" property="desc"/></td></tr> <tr><td>prix</td><td> <jsp:getproperty name="item" property="prix"/></td></tr> <tr><td>disponible</td><td> <jsp:getproperty name="item" property="stock"/></td></tr> </table> </body> </html> À la fin de l exécution de ce programme, soit l objet ItemBean est vide, si l instruction SELECT n a fourni aucun résultat, soit il contient des données de la table PRODUCTS_TABLE. Après la création du composant et son initialisation avec des valeurs de la base de données, on affiche la valeur de ses propriétés. Dans cet exemple, une grande quantité de code Java est nécessaire pour prendre en charge une mise en forme HTML minimale. Pour écrire plusieurs pages répondant aux mêmes contraintes de présentation, il faudrait récrire à chaque fois (ou copier, coller puis adapter) ces instructions. Nous verrons au Chapitre 8 certaines architectures permettant d éviter ce type de problème. Pour l heure, nous pouvons placer ce code à l intérieur d un composant capable de récupérer seul des données, comme nous allons le montrer. Récupération dynamique des données par un composant Une technique similaire à celle qui a été décrite dans l exemple précédent peut être utilisée pour créer des composants susceptibles de récupérer des données dynamiquement. Pour cela, il suffit d établir la connexion à la base dans le constructeur du composant, exécuter la requête, affecter les valeurs obtenues aux propriétés correspondantes puis fermer la connexion. Il est encore possible de définir certaines propriétés de sorte qu elles déclenchent la récupération des données, en incluant le code d accès à la base dans la méthode. Par exemple, vous pouvez faire en sorte que la modification de la propriété ID du composant ItemBean lance la récupération de la ligne correspondante pour initialiser les autres propriétés. Influence externe Comme nous le verrons au Chapitre 8, il est souvent préférable de limiter la quantité de code Java qui se trouve dans une page JSP. Pour ce faire, il est possible d encapsuler les données récupérées dans une servlet avant de les intégrer aux composants auxquels la page JSP fait appel. L accès à la base de données se fait toujours de la même façon mais l utilisation de la servlet permet de partager et de réutiliser la connexion à la base de données. Ainsi, l on confiera à une servlet la gestion des connexions et de la récupération des données.

Intégration aux bases de données CHAPITRE 7 7 2.2. Types de données JSP et JDBC Chaque base de données prend en charge des types de données prédéfinis, qui varient sensiblement d un éditeur à un autre. Heureusement, JDBC sert d interface entre les types Java et ceux de la base de données et libère ainsi le développeur Java du souci de gérer le formatage des données et les différences subtiles qui peuvent exister entre les types équivalents. JDBC définit un ensemble de types SQL correspondant aux types natifs de la base et établit une correspondance entre ces types SQL et les types Java, et inversement. En effet, pour manipuler directement la base de données, par exemple pour établir le schéma d une table, on utilise les types de données natifs de SQL. En revanche, pour l extraction ou le stockage via JDBC, s applique le système de typage Java : la manière dont on convertit les données vers le type SQL approprié dépend des méthodes JDBC invoquées. Pour créer des composants JSP destinés à interagir avec la base, il faut bien comprendre comment fonctionne la conversion de ces données. Nous présentons ci-après les principaux types SQL et explicitons la manière dont ils sont gérés par JDBC. Nombres entiers JDBC définit quatre types SQL pour gérer les nombres entiers, mais la plupart des éditeurs de bases de données ne prennent en charge que deux d entre eux. Le type SMALLINT représente des entiers signés sur 16 bits et correspond au type Java short. Le type INTEGER est affecté au type Java int, et représente une valeur entière signée sur 32 bits. Les deux autres types, TINYINT et BIGINT, représentent respectivement des entiers sur 8 et 64 bits ; il est rare qu ils soient pris en charge. Nombres à virgule flottante JDBC spécifie deux types de données à virgule flottante, DOUBLE et FLOAT. Ce dernier a été ajouté pour améliorer la cohérence avec ODBC. Sun recommande en général de s en tenir au type DOUBLE, homologue du type Java double. Chaînes de caractères On trouve deux types primaires SQL de manipulation de texte, CHAR et VARCHAR. Ils sont traités en tant qu objets String par JDBC. Le type CHAR est pris en charge par la plupart des bases de données. Il contient une chaîne de longueur fixe à la différence de VARCHAR qui contient une chaîne de longueur variable, pour laquelle est néanmoins spécifiée une longueur maximale. Le type CHAR peut contenir des chaînes plus courtes que la longueur fixée, auquel cas JDBC comble les caractères manquants par des espaces. Les navigateurs HTML ignorent les espaces redondants dans les données JSP renvoyées. Il est en outre possible d appeler la méthode trim() de la classe String pour ôter les espaces en fin de chaînes avant de traiter ces dernières. Enfin, un troisième type est défini par JDBC, à savoir LONGVARCHAR qui peut contenir de très grands blocs de texte. Malheureusement, la prise en charge du type LONGVARCHAR diffère grandement d un éditeur à un autre, ce qui rend son utilisation difficile. Dates et heures Pour gérer les informations de date et d heure, JDBC définit trois types de données distincts : DATE, TIME et TIMESTAMP. Comme leur nom l indique, ces types de données contiennent des informations de date (jour, mois et année), des informations horaires (heures, minutes et secondes), le type TIMESTAMP combinant les informations des deux autres en y ajoutant les

8 JSP Java Server Pages nano-secondes. Hélas, aucun de ces types ne correspond exactement à la classe Date du paquetage java.util, qui ne contient pas de champ de nano-secondes. Ces types SQL sont gérés par trois sous-classes de java.util.date, à savoir java.sql.date, java.sql.time, et java.sql.timestamp. En tant que sous-classes de java.util.date, ces classes peuvent être utilisées partout où un type java.util.date est attendu. Cela permet notamment de les traiter comme des valeurs de date et d heure normales, tout en préservant la compatibilité avec la base de données. Il est important de comprendre comment chacune de ces sous-classes a été dérivée de sa classe de base. La classe java.sql.date, par exemple, occulte les champs horaires, tandis que la classe java.sql.time fait la même chose pour les champs de date. Ces détails sont à prendre en compte lorsque vous échangez des données entre la base de données et vos composants JSP. S il vous faut convertir un objet de la classe java.sql.timestamp en son équivalent java.util.date le plus proche, vous pouvez recourir au code suivant : Timestamp t = results.gettimestamp("modified"); java.util.date d; d = new java.util.date(t.gettime() + (t.getnanos()/1000000)); Le tableau 7-1 fournit un résumé des correspondances de types le plus fréquentes, ainsi que les méthodes d accès ResultSet adaptées à chaque type. Tableau 7-1. Conversions de types Java/JDBC Type Java Type JDBC Méthode d accès JDBC conseillée short SMALLINT getshort() int INTEGER getint() double DOUBLE getdouble() java.lang.string CHAR getstring() java.lang.string VARCHAR getstring() java.util.date DATE getdate() java.sql.time TIME gettime() java.sql.timestamp TIMESTAMP gettimestamp() Gestion des champs vides Lorsqu une colonne de table ne contient pas de données, la valeur null lui est affectée. Or, la représentation d absence de valeur (null) par des types de données Java, tels que int et double, pose problème. En effet, ceux-ci ne sont pas des objets et ne peuvent donc être vides. Certes, la méthode getint() pourrait renvoyer 0 ou 1 pour indiquer une valeur vide, mais il s agit de valeurs valides. Le même problème se pose pour les types String. Certains pilotes de base renvoient une chaîne vide ("") tandis que d autres ne renvoient rien (null) ou une chaîne null. L une des solutions, peu élégante mais qui a le mérite d être efficace, est la méthode wasnull() de la classe ResultSet, qui renvoie vrai ou faux selon que la dernière méthode d accès appelée aurait dû renvoyer une valeur valide ou un vrai null. Ce problème se pose lors de la création de composants JSP à partir de composants Java. L interprétation d une absence de valeur par la balise <jsp:getproperty> n est pas homogène

Intégration aux bases de données CHAPITRE 7 9 d un produit à l autre. Cela rend impossible l utilisation d une valeur littérale, et l approche à adopter est la même que pour une interface JDBC. Il est cependant possible de définir une propriété booléenne indiquant la validité de la valeur de propriété en question. Lorsqu une valeur nulle est rencontrée dans la base, cette propriété peut prendre une valeur valide à condition qu un contrôle de validité renvoie bien la valeur false. Dans le programme suivant, on affecte une valeur à la propriété indiquant la quantité disponible d un article à partir de la colonne QTE_DISPONIBLE de la classe ResultSet. Un indicateur signale aussi si la valeur était une valeur valide. init() {... myquantity = results.getint("qte_disponible"); if (results.wasnull()) { myquantity = 0; validquantity = false; else { validquantity = true;... isvalidquantity() { return validquantity; Bien entendu, cela implique qu une vérification préalable de la validité de la valeur ait été effectuée plus haut dans le code JSP, avant l utilisation de celle-ci. Pour cela, on peut appeler la méthode de vérification booléenne : Quantité disponible : <% if (item.isvalidquantity()) %> <jsp:getproperty name="item" property="quantity"/> unités <% else %> Inconnue Si la valeur utilisée dans le programme JSP est simplement destinée à être affichée, on peut aussi définir une propriété String qui renvoie la valeur appropriée, quel que soit l état de la propriété. Une telle approche aurait des conséquences sur la flexibilité du composant mais elle aurait le mérite de simplifier votre code JSP. getquantitystring() { if (validquantity) return new Integer(quantity).toString(); else return "Inconnu"; La meilleure façon d éviter ce problème est encore d interdire les valeurs non valides dans la base de données La plupart des bases de données offrent même la possibilité d interdire l entrée de valeurs non valides au niveau du schéma.

10 JSP Java Server Pages 2.3. Connexions persistantes On souhaiterait parfois utiliser une connexion pour plusieurs requêtes d un même client. Il faut cependant tenir compte de ce que le nombre de connexions qu un serveur peut prendre en charge est limité. Les connexions persistantes sont possibles tant que le nombre d utilisateurs simultanés reste modeste. Mais en cas de fort trafic réseau, il n est pas souhaitable d établir une connexion à la base à chaque requête car c est sans doute l un des processus les plus lents de l application ; il faut l éviter dans la mesure du possible. Un certain nombre de solutions s offrent à vous. La première est celle de la mise en cache de connexions (connection pooling) qui est implémentée soit par le pilote de la base de données, soit par des classes dédiées. Cette technique permet d entretenir un nombre fixe de connexions et de les «prêter» à mesure qu elles sont demandées par des pages JSP ou des composants Java. La mise en cache de connexions est un bon compromis entre un trop grand nombre de connexions simultanées ouvertes et une trop grande fréquence de connexions et de déconnexions qui ont un coût en termes de performances. Le listing 7-1 crée un composant qui encapsule une connexion à une base de données. L utilisation de cette classe ConnectionBean permet d isoler la page JSP des détails de connexion, tout en rendant la connexion persistante sur plusieurs pages en l attachant à la session. De cette manière, il n est pas utile de redemander une connexion à la base à chaque fois. Des méthodes supplémentaires ont été ajoutées pour appeler les méthodes correspondantes de l objet connexion encapsulé. Notez que les paramètres d accès à la base sont codés littéralement pour simplifier le programme. Il va sans dire qu il est souvent préférable de les rendre configurables. Listing 7-1. Code source de ConnectionBean.java package com.taglib.wdjsp.databases; import java.sql.*; import javax.servlet.http.*; public class ConnectionBean implements HttpSessionBindingListener { private Connection connection; private Statement statement; private static final String driver="postgresql.driver"; private static final String dburl="jdbc:postgresql://slide/test"; private static final String login="guest"; private static final String password="guest"; public ConnectionBean() { try { Class.forName(driver); connection=drivermanager.getconnection(dburl,login,password); statement=connection.createstatement(); catch (ClassNotFoundException e) { System.err.println("ConnectionBean : pilote non disponible"); connection = null; catch (SQLException e) { System.err.println("ConnectionBean : pilote non chargé"); connection = null;

Intégration aux bases de données CHAPITRE 7 11 public Connection getconnection() { return connection; public void commit() throws SQLException { connection.commit(); public void rollback() throws SQLException { connection.rollback(); public void setautocommit(boolean autocommit) throws SQLException { connection.setautocommit(autocommit ); public ResultSet executequery(string sql) throws SQLException { return statement.executequery(sql); public int executeupdate(string sql) throws SQLException { return statement.executeupdate(sql); public void valuebound(httpsessionbindingevent event) { System.err.println("ConnectionBean: dans la méthode valuebound"); try { if (connection == null connection.isclosed()) { connection = DriverManager.getConnection(dbURL,login,password); statement = connection.createstatement(); catch (SQLException e) { connection = null; public void valueunbound(httpsessionbindingevent event) { try { connection.close(); catch (SQLException e) { finally { connection = null; protected void finalize() {

12 JSP Java Server Pages try { connection.close(); catch (SQLException e) { La classe ConnectionBean implémente HttpSessionBindingListener, et se déconnecte automatiquement de la base si le composant est retiré de la session. La durée de vie de la connexion est ainsi limitée lorsqu elle n est plus utilisée, avant même sa suppression par le ramassemiettes. Ce composant n a pour but que d isoler l application des détails de connexion. Nous pourrions aussi créer un composant d usage plus général, contenant les valeurs de configuration nécessaires (url, username, password et driver) que la page JSP devrait initialiser pour activer la connexion. 2.4. Gestion de grands ensembles de résultats Si la requête envoyée à la base renvoie un grand nombre de lignes, il n est pas recommandé de les afficher toutes. Une table de 15 000 lignes, par exemple, est non seulement difficile à lire mais excessivement longue à télécharger sous format HTML. Si la manière dont est conçue l application le permet, il est bon d imposer une limite au nombre de lignes qu une requête peut renvoyer le moyen le plus rapide étant encore de demander à l utilisateur de préciser sa recherche. Une solution encore meilleure consiste à n afficher qu une page à la fois. Il y a bien des façons de le faire avec JSP. L interface RowSet a été ajoutée dans la version 2.0 de JDBC pour normaliser l accès à des données mises en cache à travers un composant Java ou via des systèmes distribués. Création d un objet ResultSet persistant Lorsqu une requête renvoie un objet ResultSet, tous les résultats ne sont pas en mémoire. La base de données, en effet, entretient une connexion vers la base et renvoie les lignes au fur et à mesure qu elles sont demandées. Un tel comportement a l avantage de nécessiter peu de bande passante et peu de mémoire, mais suppose un temps de connexion long, ce qui peut poser des problèmes dans des environnements réseau très denses où le recyclage des connexions doit être fréquent. Le pilote de la base de données détermine le nombre optimal de lignes à extraire à la fois, nombre qu il est désormais possible de configurer vous-même dans la version 2.0 de JDBC. Le renvoi des lignes suivantes s effectue automatiquement à mesure que vous progressez dans les résultats du ResultSet ; il est inutile de faire manuellement le suivi de la position du curseur. Ensuite, on peut parcourir les ResultSet page par page, à raison de 20 lignes par page, par exemple. Il suffit de définir une boucle à travers 20 lignes, maintenir l objet ResultSet dans la session et recommencer une autre boucle sur les 20 lignes suivantes. La position interne du curseur ne change pas d une requête à une autre, ce qui permet de lancer la requête exactement là où s était arrêtée la précédente. Il n est pas nécessaire de conserver explicitement une référence à l objet Connection car l objet ResultSet le fait de lui-même. Lorsque celui-ci sort du champ de visibilité et est supprimé par le ramasse-miettes, l objet Connection est lui aussi supprimé.

Intégration aux bases de données CHAPITRE 7 13 Vous pouvez aussi encapsuler l objet ResultSet dans un composant et implémenter HttpSessionBindingListener pour fermer automatiquement les connexions à la base dès qu elles ne sont plus utilisées, voire mettre à disposition une méthode de nettoyage et l invoquer en fin d exécution de la page JSP. Mais cela pose quand même un problème, à savoir de devoir laisser ouverte la connexion à la base aussi longtemps. Pour remédier à cela, nous allons maintenant étudier d autres solutions, qui évitent de maintenir la connexion ouverte lorsque l utilisateur navigue de page en page. Réexécution des requêtes plusieurs fois Dans cette technique, la requête de recherche est réexécutée pour chaque page de résultats affichée, la seule condition étant de stocker la position du curseur dans la session utilisateur. À chaque étape, la requête originale est réémise, puis la méthode next()de la classe ResultSet est appliquée (voire la méthode absolute()de la version 2.0 de JDBC) pour faire avancer le curseur jusqu à la position appropriée. On affiche ensuite les 20 lignes. Au deuxième chargement de la page, on fait avancer le curseur de 20 lignes, au troisième chargement, on augmente le déplacement à 40 lignes, et ainsi de suite. Pour faire savoir à l utilisateur où il se trouve dans l ensemble des résultats, il suffit de noter la taille de l objet ResultSet. En connaissant le nombre de lignes affichées à chaque fois, vous pourrez afficher une information du type «Page 1 sur 5». Cette technique présente un inconvénient, celui de réinitialiser l affichage de la base de données à chaque fois, de sorte qu une modification des données entre deux requêtes peut modifier l apparence des résultats pour l utilisateur. Préciser la requête automatiquement Une autre solution consiste à faire en sorte qu à chaque nouvelle requête, celle-ci contienne des paramètres pour l affichage des résultats qui n ont pas encore été affichés. Cette méthode ne peut être utilisée que dans certains cas. Il s agit ici d afficher une page de données, puis de stocker la clé primaire du dernier enregistrement affiché. Pour chaque page, il faut émettre une nouvelle requête, mais avec une clause WHERE limitant la recherche aux éléments non encore affichés. Ce procédé est remarquable lorsque les données de la table sont classées d une manière ordonnée, par numéro de produit par exemple. Si le dernier enregistrement affiché correspondait au numéro de produit 8375, il suffit de stocker ce nombre dans la session et d ajouter une clause WHERE dans la requête suivante pour en tenir compte, comme ceci : SELECT * FROM PRODUCTS WHERE ID > 8357 Utilisation du composant CachedRowSet Lorsque les résultats d une requête sont plus maniables, c est-à-dire que, sans tenir dans une seule page, ils n accaparent pas pour autant toute la mémoire, vous pouvez utiliser le composant CachedRowSet. L interface RowSet de JDBC 2.0 encapsule dans un composant JavaBeans, appelé CachedRowSet, une connexion à la base de données et les résultats de la requête correspondante. Ce composant fournit un conteneur navigable déconnecté pour accéder à des résultats de requêtes SQL depuis une page JSP ou depuis un conteneur JavaBeans. Il s agit là d un outil fort utile permettant de travailler sur des données de la base depuis l intérieur de la page JSP. Cette classe fait partie des extensions facultatives de JDBC 2.0. Pour en savoir davantage sur ce composant, rendez-vous sur le site web de Sun consacré à JDBC, http://java.sun.com/ products/jdbc. À la différence de la classe ResultSet, le composant CachedRowSet offre une

14 JSP Java Server Pages Figure 7-2 Navigation dans les résultats à partir d un objet CachedRowSet «connexion» hors ligne, en mettant en cache tous les résultats de la requête. Aucune connexion active n est requise car l ensemble des données demandées a déjà été collecté dans la base. Cette technique est très commode mais peut poser des problèmes de sur-utilisation de la mémoire dans le cas de très grands ensembles de résultats auquel cas la meilleure solution est probablement celle d une connexion persistante. CachedRowSet est très simple d utilisation. Il suffit de configurer les propriétés nécessaires, telles que le nom d utilisateur (username), le mot de passe (password) et l URL de la base de données, puis de définir la propriété command de votre requête SQL. Il est également possible d utiliser un objet RowSet, créé à partir d une autre requête, pour remplir le composant CachedRowSet. Exemple : parcourir les résultats avec un composant CachedRowSet Dans cet exemple, nous allons afficher page après page les résultats d une requête en utilisant le composant Java CachedRowSet et JSP. Nous commencerons par collecter les données dans la base, pour les afficher ensuite dans des pages de cinq lignes. L utilisateur aura la possibilité de revenir au premier résultat s il le souhaite. Nous aurions pu faire la même chose avec un objet ResultSet persistant, mais il aurait alors fallu recourir à des scriptlets JSP ou encapsuler l objet ResultSet dans notre propre composant (voir figure 7-2). Le code de notre programme est donné dans le listing 7.2. Listing 7-2. Code source de la page CachedResults.jsp <%@ page import="java.sql.*,javax.sql.*,sun.jdbc.rowset.*" %> <jsp:usebean id="crs" class="cachedrowset" scope="session"> <% try { Class.forName("postgresql.Driver");

Intégration aux bases de données CHAPITRE 7 15 catch (ClassNotFoundException e) { System.err.println("Erreur" + e); %> <jsp:setproperty name="crs" property="url" value="jdbc:postgresql://slide/test" /> <jsp:setproperty name="crs" property="username" value="invité" /> <jsp:setproperty name="crs" property="password" value="pomme" /> <jsp:setproperty name="crs" property="command" value="select * from navettes order by id" /> <% try { crs.execute(); catch (SQLException e) { out.println("erreur SQL : " + e); %> </jsp:usebean> <html> <body> <center> <h2>résultats mis en cache</h2> <p> <table border="2"> <tr bgcolor="tan"> <th>id</th><th>aéroport</th><th>départ</th><th>sièges</th></tr> <% try { if ("first".equals(request.getparameter("action"))) crs.beforefirst(); for (int i=0; (i < 5) && crs.next(); i++) { %> <tr> <td><%= crs.getstring("id") %></td> <td><%= crs.getstring("aéroport") %></td> <td><%= crs.getstring("heure") %></td> <td><%= crs.getstring("sièges") %></td> </tr> <% %> </table> </p> <% if (crs.isafterlast()) { crs.beforefirst(); %> <br>fin des résultats<br> <% catch (SQLException e) { out.println("erreur SQL " + e); %> <a href="<%= HttpUtils.getRequestURL(request) %>?action=first"> [5 premiers]</a> <a href="<%= HttpUtils.getRequestURL(request) %>?action=next"> [5 suivants]</a> </center> </body> </html>