Posted on février 24th, 2010 by admin

17 Comments

Création d’un formulaire de login Struts 2 avec stockage des données dans une base SQL

Nous allons mettre en place un formulaire de login en struts 2 sous eclipse Galileo, Tomcat 6 et Mysql 5. Les informations de l’utilisateur (nom, mot de passe) seront stockées en base. L’application finale respectera strictement le modèle MVC 2 cher à struts 2. Pour aborder ce tutoriel dans les meilleures conditions vous devez avoir réalisé le tutoriel formulaires avec struts2.

1 – L’architecture de notre application

Reprenons notre projet formulaire (vous pouvez télécharger le war ici )

A – Structure de notre projet et mise en place de l’environnement SQL.

1 – Intégrons le projet à éclipse galileo :

nous devons sélectionner le type de source à importer :

il nous faut maintenant configurer le Build Path et intégrer nos jar (bibliothèques java)

Faites un clic droit sur le nom du projet puis sélectionner propriétés.

puis ;

1 – Sélectionnez le Java Buil Path

2 – Sélectionnez l’onglet Librairies

3 – Cliquez sur Add Jars

4 – naviguez dans votre projet jusqu’au répertoire LIB, situé dans /webcontent/WEB-INF/ et sélectionnez tous les *.jar

5 – validez par ok.

l’ensemble des jars sont maintenant associés à votre projet qui ressemble à ceci :

Comme vu au tutoriel formulaires avec struts2 nous avons stocké nos mot de passe et nom d’utilisateur dans la classe Bdd.TableUtilisateur. Nous allons effacer cette classe qui ne va plus nous servir et monter notre serveur sql.

2 – mise en place du serveur SQl et création de la base.

Installons easyPhp (équivalents : wamp, mamp, xamp) que vous pouvez télécharger ici.

Pour lancer le serveur cliquez sur l’exécutable d’easyPHP (pour plus d’information sur la mise en place veuillez consulter ce tutoriel.

Nous allons créer notre première base nommée login. Ouvrez la console d’administration phpMyadmin en tapant cette adresse : http://127.0.0.1/home/mysql/

Nous allons maintenant créer notre table

puis nos champs :

voici le script Sql :

--
 
-- Structure de la table `identification`
 
--
 
CREATE TABLE IF NOT EXISTS `identification` (
 
`id` int(11) NOT NULL AUTO_INCREMENT,
 
`identifiant` text NOT NULL,
 
`mdp` text NOT NULL,
 
PRIMARY KEY (`id`)
 
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
 
--
 
-- Contenu de la table `identification`
 
--
 
INSERT INTO `identification` (`id`, `identifiant`, `mdp`) VALUES
 
(1, 'admin', 'admin'),
 
(2, 'user', 'user');

nous avons ajouter dans notre bases deux enregistrements.


3 – Configuration de la couche DAO (data object access).

Dans notre package model nous allons créer une interface de type DAO qui implémentera notre modèle DAO. Nous affecterons également une classe de type model à notre Utilisateur.java.

a- L’interface DAO.java

package model;
 
import java.sql.Connection;
 
import javax.naming.NamingException;
 
/**
 * @author Olivier Guillou
 * fevrier 2009
 */
 
public interface Dao {
 
	/**
	 * Ouvre une connexion vers le SGBD.
	 * @return Connection
	 */
	public Connection getConnection() throws NamingException;
 
}

Cette interface (contrat) obligera les classes dépendantes à implémenter la méthode getConnection();

b - Le modele Data Object Access ModeleDao.java

package model;
 
import java.sql.Connection;
import java.sql.SQLException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContext;
import javax.sql.DataSource;
 
import org.apache.struts2.ServletActionContext;
 
	/**
	 * @author Olivier Guillou
	 * 2009 février
	 *
	 * Classe de connexion ModelDao.
	 */
	public class ModelDao  implements Dao
	{	
 
		//-------------------------------------------------------------
		// Properties
		//-------------------------------------------------------------
 
		/**
		 * initialisation du context
		 */
		Context ctx = null;
 
		/**
		 * Le DataSource
		 */
		DataSource dataSource=null;
 
		/**
		 * La Connection
		 */
 
		//-------------------------------------------------------------
		// Others methods
		//-------------------------------------------------------------		
 
		/* (non-Javadoc)
		 * @see model.Dao#getConnection()
		 */
		public Connection getConnection() throws NamingException
		{
 
			ServletContext servletContext=ServletActionContext.getServletContext();
			if(this.dataSource==null)
			{
				dataSource=(DataSource)servletContext.getAttribute("dataSource");
			}
			Connection connection=null;
			if(dataSource!=null)
			{
				try
				{
					connection=dataSource.getConnection();
				}
				catch(SQLException e)
				{
					System.out.println(e);
				}
			}
			return connection;
		}
 
		/**
		 * @param dataSource
		 */
		public void setConnection(DataSource dataSource)
		{
			this.dataSource=dataSource;
		}
	}

Cette classe implémente notre interface DAO et défini la méthode de connexion. Cette méthode de connexion utilisera un pool de connexion (une DataSource) qui est une simple fabrique de connexions vers la source de données SQL, dont le nom est stocké dans le web.xml et les paramètres de connexion dans le fichier context.xml

c - Le model utilisateur (dans notre cas) UtilisateurModel.java

package model;
 
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import services.GestionConnexion;
import bean.Utilisateur;
 
/**
 * @author Olivier Guillou
 * 2009 février
 *
 *  Model Utilisateur (requetes et mapping).
 */
 
public class UtilisateurModel extends ModelDao {
 
	//-------------------------------------------------------------
	// Attributs
	//-------------------------------------------------------------
 
	/**
	 * initilisation de la connection
	 */
	Connection connexion = null;
 
	/**
	 * initialisation du resultSet
	 */
	ResultSet resultat = null;
 
	/**
	 * initialisation du PreparedStatement
	 */
	PreparedStatement requete=null;
 
	/**
	 * initialisation du bean utilisateur
	 */
	Utilisateur utilisateur=null;
 
	//-------------------------------------------------------------
	// Methodes du model
	//-------------------------------------------------------------	
 
	/**
	 * Méthode de mapping Objet/relationnel pour l'Utilisateur
	 * @param resultat
	 * @return Utilisateur
	 */
	public Utilisateur mappingUtilisateur(ResultSet resultat)
	{
 
		// on instancie un nouvel utilisateur
		Utilisateur utilisateur = new Utilisateur();
 
		try 
 
		{	
 
			// ---------------
 
			if ((resultat.getString("id") == null))
			{
				utilisateur.setId(0);
			}
			else
			{
				utilisateur.setId(resultat.getInt("id"));
			}
 
			// ---------------
 
			if (resultat.getString("identifiant") == null)
			{
				utilisateur.setIdentifiant("");
			}
			else
			{
				utilisateur.setIdentifiant(resultat.getString("identifiant"));
			}
 
			// ---------------
 
			if (resultat.getString("mdp") == null)
			{
				utilisateur.setMdp("");
			}
			else
			{
				utilisateur.setMdp(resultat.getString("mdp"));
			}	
 
		}
		catch (Exception e)
		{
			utilisateur=null;
			System.out.println("erreur de mapping :" + e);
		}
 
		return utilisateur;
	}	
 
	/**
	 * Methode d'accès aux données de l'utilisateur
	 * @param loginForm
	 * @param passwordForm
	 * @return
	 */
	public Utilisateur Identifier(String loginForm, String passwordForm) {
 
		// initialisation de la requete
		String requeteString=null;
		Utilisateur utilisateur=null;
 
		try
		{
			// ouverture connexion
			connexion=super.getConnection();
 
			// creation requète
			requeteString = "SELECT * FROM identification WHERE identifiant=? AND mdp=?";
 
			// preparation requête
			requete=connexion.prepareStatement(requeteString);
			requete.setString(1,loginForm);
			requete.setString(2,passwordForm);
 
			// éxécution requête
			resultat= requete.executeQuery();
 
			// On stocke le resultat dans l'objet utilisateur
			if(resultat!=null)
			{
				if(resultat.next())
				{
					// mapping des attributs <=> champs Sql
					utilisateur=mappingUtilisateur(resultat);
				}
			}
		}
		catch(Exception e)
		{
			utilisateur=null;
		}
		finally
		{
			try
			{
				// Fermeture de la connexion
				if(resultat!=null)
				{
					GestionConnexion.closeResulset(resultat);
				}
				if(requete!=null)
				{
					GestionConnexion.closeRequest(requete);
				}
				if(connexion!=null)
				{
					GestionConnexion.closeConnection(connexion);
				}
			}
			catch(Exception errorConnection)
			{
				System.out.println("La fermeture de la connexion a provoqué une erreur " + errorConnection);
			}
		}
 
		return utilisateur;
	}
 
}

C’est le model associé au bean Utilisateur.java et que nous appellerons via la classe d’action UtilisateurAction.java. Le model va nous fournir un ensemble de méthode pour créer, supprimer, modifier nos informations en base de donnée. Nous remarquerons également une méthode de mapping qui réalise la transformation objet-relationnel c’est-a-dire : qui associe les attributs de ma classe Utilisateur.java aux champs de la table correspondante dans la base de donnée.

d - Le service de connexion au SGBD.

Nous allons maintenant configurer notre écouteur (DatasourceListener.java) , chargé de récupéré la dataSource.

package services;
 
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.sql.DataSource;
 
public class DatasourceListener implements ServletContextListener{
 
	Context context=null;
 
	//fonction appelée lors de la création du lanceur
	public void contextInitialized(ServletContextEvent servletContextEvent)
	{
		ServletContext servletContext=servletContextEvent.getServletContext();
 
		System.out.println("récupération du ServletContext" + servletContext);
 
		String dataSourceJNDI=servletContext.getInitParameter("dataSource");
 
		System.out.println("récupération de la dataSourceJNDI" + dataSourceJNDI);
 
		try
		{
			context=new InitialContext();
			DataSource dataSource=(DataSource)context.lookup(dataSourceJNDI);
			if(dataSource==null)
			{
				System.out.println("Echec lors de la récupèration de la datasource");
			}
			else
			{
				System.out.println("DataSource chargée");
			}
			servletContext.setAttribute("dataSource", dataSource);
		}
		catch(NamingException e)
		{
			throw new RuntimeException();
		}
		finally
		{
			try
			{
				//fermer le context
				if(context!=null)
				{
					context.close();
				}
			}
			catch(Exception e)
			{
				System.out.println("Erreur lors de initCtx !");
			}
		}
	}
 
	//fonction appelée lors de la destruction du lanceur
	public void contextDestroyed(ServletContextEvent servletContextEvent)
	{
		try
		{
			//fermer le context
			if(context!=null)
			{
				context.close();
			}
		}
		catch(Exception e)
		{
			System.out.println("Erreur lors de initCtx !");
		}
	}
}

Il doit ête déclaré dans le fichier web.xml

e - Factorisation des fermetures de connexion : GestionConnexion.java

package services;
 
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
 
public class GestionConnexion
{
 
	// ferme le ResultSet
	public static void closeResulset(ResultSet resultat)
	{
		if(resultat!=null)
		{
			try
			{
				System.out.println("Resultset fermé avec succès");
				resultat.close();
			}
 
			catch(Exception e)
			{
				System.out.println("Erreur de fermeture du Resultset");
			}
		}
	}
 
	// Ferme une requête
	public static void closeRequest(Statement requete)
	{
		if(requete!=null)
		{
			try
			{
				System.out.println("requete fermée avec succès");
				requete.close();
			}
			catch(Exception e)
			{
				System.out.println("Erreur de fermeture d'une requète");
			}
		}
	}
 
	// Ferme une connexion
	public static void closeConnection(Connection connexion)
	{
		if(connexion!=null)
		{
			try
			{
				System.out.println("connexion fermée avec succès");
				connexion.close();
			}
 
			catch(Exception e)
			{
				System.out.println("Erreur de fermerture d'une connexion");
			}
		}
	}
 
}

Cette classe nous permet de fermer notre connexion depuis nos méthodes d’accès en base.

f - Configuration du fichier web.xml

Nous allons associer la ressource jdbc (jdbcMysql) à la dataSource : javax.sql.DataSource

ainsi que le paramètre dataSource préfixé par java:/comp/env/

g - Configuration du fichier context.xml

Nous allons maintenant configurer le fichier context.xml que nous placerons dans le répertoire META-INF du /webContent de notre application. Ce fichier déclare les différents paramètres nécessaires à la connexion à la base de donnée. Attention renommez context en Context et ressource en Ressource

< ?xml version="1.0" encoding="UTF-8"?>
 
<context docBase="ProjectName" path="/ProjectName" reloadable="true" source="org.eclipse.jst.jee.server:apiBlog" >
 
 
		<resource name="jdbcMysql" 
		                  auth="Container" 
		                  type="javax.sql.DataSource"
		                  username="root" 
		                  password="" driverClassName="com.mysql.jdbc.Driver"
		                  url="jdbc:mysql://localhost:3306/login"
		                  maxActive="20" 
		                  maxIdle="10" 
		                  validationQuery="SELECT 1"
		/>
 
</context>

c
4 – Notre projet Finalisé.

Il doit ressemblé à ceci :



Vous pouvez télécharger les sources de ce projet ici.


17 Responses to “Struts 2 et SQL : l’accès aux base de donnée sql avec struts2 (login session mapping requêtes sql )”


  1. slay

    1 year ago

    Très bon tuto.

    merci


  2. Imed

    1 year ago

    merci bcp
    ça ma bcp servi


  3. abdel

    1 year ago

    j’ai un petit bug lors de l’execution sur eclipse le projet m’indique l’erreur suivante :o rg.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot load JDBC driver class ‘com.mysql.jdbc.Driver’ et pourtant j’ai bien respecté ce tutoriel.


  4. admin

    1 year ago

    pour que ton application se connecte a ta base MYSQL il est nécessaire de mettre un connector dans ton server tomcat (répertoire lib ) tu peux en trouver un ici http://www.mysql.com/downloads/mirror.php?id=390509#mirrors


  5. abdel

    1 year ago

    toujours pareil meme en rajoutant le pilote mysql dans la lib il m’indique : org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot load JDBC driver class ‘com.mysql.jdbc.Driver’ bizard .


  6. admin

    1 year ago

    Ton Drivers JDBC mysql n’est pas chargé. Désinstalles l’appli que tu testes. Et mets ce connector dans le répertoire lib de ton server tomcat (enlèves l’ancien), met également le connector en question dans le répertoire lib de ton application. Si cela ne fonctionne toujours pas testes un accès direct a ta base en utilisant ce tutoriel http://www.roseindia.net/jsp/connect-jsp-mysql.shtml, si un accès direct ne fonctionne pas, l’appli struts 2 ne fonctionnera pas non plus. bon courage.


  7. Baracouda

    1 year ago

    Bonjour

    Pour ma part j ai bien le driver de mysql (3.0.17)

    J’ai modifié le server.xml de tomcat en ajoutant ceci

    puis ceci

    Voici l’erreur :
    récupération du ServletContextorg.apache.catalina.core.ApplicationContextFacade@21cc5069
    récupération de la dataSourceJNDIjava:/comp/env/jdbcMysql
    8 janv. 2011 00:08:34 org.apache.catalina.core.StandardContext listenerStart
    GRAVE: Exception lors de l’envoi de l’évènement contexte initialisé (context initialized) à l’instance de classe d’écoute (listener) services.DatasourceListener
    java.lang.RuntimeException: Echec d’initialisation :
    at services.DatasourceListener.contextInitialized(DatasourceListener.java:41)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4135)
    at org.apache.catalina.core.StandardContext.start(StandardContext.java:4630)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
    at org.apache.catalina.core.StandardHost.start(StandardHost.java:785)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
    at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:445)
    at org.apache.catalina.core.StandardService.start(StandardService.java:519)
    at org.apache.catalina.core.StandardServer.start(StandardServer.java:710)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:581)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)
    Caused by: javax.naming.NameNotFoundException: Le Nom jdbcMysql n’est pas lié à ce Contexte
    at org.apache.naming.NamingContext.lookup(NamingContext.java:770)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:140)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:781)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:140)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:781)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:153)
    at org.apache.naming.SelectorContext.lookup(SelectorContext.java:152)
    at javax.naming.InitialContext.lookup(InitialContext.java:392)
    at services.DatasourceListener.contextInitialized(DatasourceListener.java:29)
    … 15 more
    8 janv. 2011 00:08:34 org.apache.catalina.core.StandardContext startrécupération du ServletContextorg.apache.catalina.core.ApplicationContextFacade@21cc5069
    récupération de la dataSourceJNDIjava:/comp/env/jdbcMysql
    8 janv. 2011 00:08:34 org.apache.catalina.core.StandardContext listenerStart
    GRAVE: Exception lors de l’envoi de l’évènement contexte initialisé (context initialized) à l’instance de classe d’écoute (listener) services.DatasourceListener
    java.lang.RuntimeException: Echec d’initialisation :
    at services.DatasourceListener.contextInitialized(DatasourceListener.java:41)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4135)
    at org.apache.catalina.core.StandardContext.start(StandardContext.java:4630)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
    at org.apache.catalina.core.StandardHost.start(StandardHost.java:785)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
    at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:445)
    at org.apache.catalina.core.StandardService.start(StandardService.java:519)
    at org.apache.catalina.core.StandardServer.start(StandardServer.java:710)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:581)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)
    Caused by: javax.naming.NameNotFoundException: Le Nom jdbcMysql n’est pas lié à ce Contexte
    at org.apache.naming.NamingContext.lookup(NamingContext.java:770)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:140)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:781)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:140)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:781)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:153)
    at org.apache.naming.SelectorContext.lookup(SelectorContext.java:152)
    at javax.naming.InitialContext.lookup(InitialContext.java:392)
    at services.DatasourceListener.contextInitialized(DatasourceListener.java:29)
    … 15 more
    8 janv. 2011 00:08:34 org.apache.catalina.core.StandardContext start

    Savez vous d’ou vient cette erreur ?

    Merci


  8. Baracouda

    1 year ago

    J ai trouvé l erreur c’est bon.
    Merci


  9. hanane

    1 year ago

    bonjour,

    j’ai un problème:
    qd je tape login/password (admin/admin) le message suivant s’affiche:

    org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot create JDBC driver of class  » for connect URL ‘null’

    avez vous une idée de quoi s’agit-il?
    j’ai remarqué aussi que le projet contient le fichier « log4j.properties » c’est quoi son utilité?

    merci


  10. admin

    1 year ago

    Il faut s’assurer d’avoir bien un connector mysql dans le répertoire lib (WEB-INF/Lib) de l’application. Eventuellement dans votre contexte renommez

    context en Context

    et ressource en Ressource

    Le fichiers log4j.properties est nécessaire pour logguer l’application dans la console ou dans un file. Plus d’information ici : http://www.laliluna.de/articles/log4j-tutorial.html


  11. hanane

    1 year ago

    bonjour,

    je vous remerci sur votre retour.

    le problème est résolue.

    je confirme que le connector doit etre dans le repertoir lib (WEB-INF/Lib) de l’application et non pas dans le lib de Tomcat(comme j’ai trouvé ailleurs).

    merci.


  12. Vincent

    1 year ago

    Très bon tuto certain ! Court et concis, pas comme les autres qui font plusieurs centaines de pages.

    Par contre, en essayant le projet, j’attrape cette erreur au déploiement :
    Deployment Error for module: ProjectNameSql: Exception while loading the app : java.lang.Exception: java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: java.lang.IllegalArgumentException: java.lang.NullPointerException

    Je suis sous Glassfish, je ne vois pas trop d’où ça vient ni quoi faire … Si quelqu’un a une idée, ça serait génial ! Merci par avance.


  13. admin

    12 months ago

    merci. pour ce qui est de votre problème ne connaissant pas l’environnement Glassfish je ne pourrai vous aider. mais c’est surement du à votre environnement. Sur Eclipse helios cet exemple est parfaitement fonctionnel.


  14. Vince

    4 months ago

    Bonjour,
    Ce tuto a l’air vraiment tres bien en effet, mais personnellement j’ai une base de données en PostgreSQL. Quels sont les changements à effectuer par rapport a cette version svp ?
    Je débute avec Struts2 , et je suis assez perdu je dois l’avouer :/

    Merci de vos réponses


  15. admin

    4 months ago

    Il faut changer le connector sql ( mysql-connector-java-x.x.x-bin.jar) dans les librairies :Tu le trouveras ici : http://jdbc.postgresql.org/download.html et il faut également modifier le fichier context.xml en suivant les indications ici : http://confluence.atlassian.com/display/DOC/Configuring+a+PostgreSQL+Datasource+in+Apache+Tomcat

2 Trackbacks For This Post

  1. Les tweets qui mentionnent Struts 2 et SQL : l’accès aux base de donnée sql avec struts2 (login session mapping requêtes sql ) | OneAnCie -- Topsy.com Dit :

    [...] Ce billet était mentionné sur Twitter par altenide et Lionel LOKO, Mathieu Breton. Mathieu Breton a dit: Apprendre #Struts2 http://bit.ly/cAd6ut , http://bit.ly/aIfxGw , http://bit.ly/a3eWjK #Tutoriel #Debutant [...]

  2. Tutoriel Struts 2 – Autocomplétion Struts 2 en Ajax Dojo et SQL : autocompleter Strut 2 et datetimepicker (calendrier) | OneAnCie Dit :

    [...] Ce tutoriel a pour but de vous initier à Ajax et Dojo dans un environnement MVC2 avec le framework Struts 2. Je vous recommande avant de commencer de réaliser le tutoriel l’accès au base de données SQL avec struts 2 [...]

Leave a Reply