IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JDBC Java Discussion :

Requete SQL à la volée (contruite dynamiquement) dans le cas de recherches


Sujet :

JDBC Java

  1. #1
    Membre régulier
    Profil pro
    rfv
    Inscrit en
    Novembre 2006
    Messages
    91
    Détails du profil
    Informations personnelles :
    Localisation : France, Pyrénées Orientales (Languedoc Roussillon)

    Informations professionnelles :
    Activité : rfv

    Informations forums :
    Inscription : Novembre 2006
    Messages : 91
    Points : 115
    Points
    115
    Par défaut Requete SQL à la volée (contruite dynamiquement) dans le cas de recherches
    Bonjour,
    je reste perplexe quant à l'utilisation des statements, la doc jdbc indique :
    Sending SQL statements to a database
    * Statement -- used to send basic SQL statements
    * PreparedStatement -- used to send prepared statements or basic SQL statements (derived from Statement)
    * CallableStatement -- used to call database stored procedures (derived from PreparedStatement)
    * Connection interface -- provides methods for creating statements and managing connections and their properties
    * Savepoint -- provides savepoints in a transaction
    Je souhaite utiliser l'avantage des PreparedStatement pour typer, backquoter en encapsuler mes valeurs.

    Le cas simple est le suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    ps = prepareStatement("SELECT * FROM maTable WHERE 1 AND id = ?");
    ps.setInt(1,456);
    Mais dans le cas d'une recherche, dont le nombre de paramètres peuvent varier, je peux faire comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    String q = "SELECT * FROM maTable WHERE 1 AND id = ?";
    if( rechercheA != null ) {
       q += " AND champA LIKE '"+rechercheA+"'";
    }
    ps = prepareStatement(q);
    ps.setInt(1,456);
    Le problème ici est que je dois moi même backslasher rechercheA pour éviter les erreurs et injections SQL, par exemple si la valeur contient une apostrophe.

    J'ai donc espérer utiliser le setInt(), setString() du PreparedStatement en dehors de cette classe, mais puisque ces méthodes ne retourne rien, cela me sert à rien...

    Car faire 2 fois les tests, 1 pour la construction de la requete et l'autre pour la definition des arguments me semble pas spécialement propre.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    String q = "SELECT * FROM maTable WHERE 1 AND id = ?";
    if( rechercheA != null ) {
       q += " AND champA LIKE ? ";
    }
    ps = prepareStatement(q);
    index=1;
    ps.setInt(index++,456);
    if( rechercheA != null ) {
       ps.setInt(index++,rechercheA);
    }
    Quelqu'un a t'il à ce jour su construire une requete dynamique sans passer par Statement dont est obligé de vérifier le contenu de nos variables ?

  2. #2
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Salut,


    Pourquoi ne pas te créer une classe qui créerait la requête SQL tout en stockant les paramètres. Au final il ne te resterait plus qu'à créer le statement et lui passer les paramètres.

    Sachant que la méthode setObject() des PreparedStatement devrait se débrouiller pour utiliser le bon type dynamiquement, cela donnerait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    class RequeteBuilder {
     
    	private final StringBuilder sql = new StringBuilder();
    	private final List<Object> params = new ArrayList<Object>();
     
    	public RequeteBuilder append(String str, Object...args) {
    		// On vérifie
    		assert check(str, args.length) : "SQL/args count error";
    		// On ajoute la requete SQL
    		sql.append(str);
    		// Et tous les paramètres présents :
    		for (Object obj : args) {
    			params.add(obj);
    		}
    		return this;
    	}
     
    	private boolean check(String str, int length) {
    		int count = 0;
    		int index = 0;
    		// On compte le nombre de '?' dans le code SQL :
    		while ( (index=str.indexOf('?', index)) > 0 ) {
    			count++;
    			index++;
    		}
    		// Et on vérifie que cela correspondent bien au nombre d'argument
    		return count==length;
    	}
     
    	public PreparedStatement createStatement(Connection con) throws SQLException {
    		// On crée le PreparedStatement à partir de la chaine créée
    		PreparedStatement ps = con.prepareStatement(this.sql.toString());
    		// Et on lui ajoute tous les paramètres 
    		int parameterIndex = 1;
    		for (Object param : this.params) {
    			ps.setObject(parameterIndex++, param);
    		}
    		return ps;
    	}
    }

    Exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    	java.sql.Date oneDay = ...;
    	java.sql.Date anotherDay = ...;
     
    	RequeteBuilder rb = new RequeteBuilder();
    	rb.append("select * from matable ");
    	rb.append("where id = ? ", 12);
    	rb.append("and date between ? and ? ", oneDay, anotherDay);
    	PreparedStatement ps = rb.createStatement(con);
    	// Le PreparedStatement est prêt à être utilisé ;)

    a++

  3. #3
    Membre régulier
    Profil pro
    rfv
    Inscrit en
    Novembre 2006
    Messages
    91
    Détails du profil
    Informations personnelles :
    Localisation : France, Pyrénées Orientales (Languedoc Roussillon)

    Informations professionnelles :
    Activité : rfv

    Informations forums :
    Inscription : Novembre 2006
    Messages : 91
    Points : 115
    Points
    115
    Par défaut
    Merci pour la rapidité et l'efficacité de la réponse, c'est ce qui s'appelle prendre un cours
    Pour ce qui est de l'assert, il faut que la JVM soient lancée avec l'option -ea pour qu'elle soit prise en compte.

    Et pour finir en question :
    Il est courant lorsque de l'on fait des recherches et paginations, on fasse 2 requêtes :
    1 pour le nombre total d'enregistrements : SELECT COUNT(*) WHERE critères
    1 autre pour les données : SELECT * WHERE critères
    Quelle serait la méthode la plus simplement efficace ? Voici la mienne :

    En permettant d'accéder au StringBuilder contenant la requête :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public StringBuilder sql = new StringBuilder();
     
    RequeteBuilder rbCriteres = new RequeteBuilder();
    //rb.append("select * from matable ");
    rbCriteres.append("where id = ? ", 12);
    rbCriteres.append("and date between ? and ? ", oneDay, anotherDay);
    //PreparedStatement ps = rb.createStatement(con);
     
    String critereSql = rbCriteres.sql.toString();
     
    rbCriteres.sql = new StringBuilder("SELECT COUNT(*) AS nbr FROM maTable " + critereSql);
    ps = rbCriteres.createStatement(con);
     
    rbCriteres.sql = new StringBuilder("SELECT * FROM maTable " + critereSql);
    ps = rbCriteres.createStatement(con);

  4. #4
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par sjachym Voir le message
    Pour ce qui est de l'assert, il faut que la JVM soient lancée avec l'option -ea pour qu'elle soit prise en compte.
    Oui oui !
    Cela permet d'éviter de vérifier la cohérence de l'appel de méthode en production puisque c'est normalement inutile, mais de bien planter le programme en dev en cas d'erreur

    Citation Envoyé par sjachym Voir le message
    En permettant d'accéder au StringBuilder contenant la requête :
    Oui c'est une solution, même si j'aurais plutôt fait une seconde méthode "createCountStatement()" qui te génèrerait la requête count(*).


    Sinon si ton driver et ta BD le supporte, le plus simple serait d'utiliser des ResultSet de type SCROLL, c'est à dire en créant le PreparedStatement de la manière suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    con.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
    Puis en utilisant la méthode de la FAQ pour récupérer le nombre de ligne retourné : Comment connaître le nombre de lignes/colonnes d'un ResultSet ?


    a++

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 1
    Dernier message: 05/08/2014, 19h51
  2. [Joomla!] Retrouver la requete SQL qui affiche les modules dans la zone admin
    Par vladock dans le forum EDI, CMS, Outils, Scripts et API
    Réponses: 1
    Dernier message: 04/10/2012, 12h12
  3. Réponses: 6
    Dernier message: 12/02/2009, 22h47
  4. [SQL] Recupération Requete SQL dans un tableau dynamique PHP
    Par victor.jbju dans le forum PHP & Base de données
    Réponses: 7
    Dernier message: 13/09/2006, 16h48
  5. Requete sql pour création de table dans une base access
    Par Ben156 dans le forum Bases de données
    Réponses: 1
    Dernier message: 17/01/2006, 22h12

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo