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

 Java Discussion :

requete insert into qui ne fonctionne pas


Sujet :

Java

  1. #1
    Membre habitué
    requete insert into qui ne fonctionne pas
    Bonjour,

    Je me remets doucement à java et impossible de faire fonctionner une "bête " insertion en BDD...
    Je précise que mes données sont récupérées de 2 JTextField d'une fenêtre swing (nom et prénom pour le test).
    Voici ma méthode :
    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
     
    public void addPersonn(Personne p) {
    		PreparedStatement pstmt;
    		String request = PersonneQuery.QUERY_INSERT_PERSONNE;
     
    		try {
    			pstmt = connection.prepareStatement(request);
    			pstmt.setString(1, p.getNom());
    			pstmt.setString(2, p.getPrenom());
     
    			int flag = pstmt.executeUpdate();
    			if (flag >= 1) {
    				System.out.println(flag + "ligne(s) ajoutée(s)");
    				connection.commit();
    			} else {
    				System.out.println("pas d'insertion");
    			}
    			pstmt.close();
     
    		} catch (SQLException e) {
    			e.printStackTrace();
    			ConnexJDBC.rollback();
    		} 
    	}


    voici la pile d'erreur :
    "Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException;
    at com.cesi.dao.PersonneDao.addPersonn(PersonneDao.java:17); la ligne 17 correspond à la ligne 7 ici; Je ne comprends pas comment ma référence pstmt est nulle.

    Merci

  2. #2
    Modérateur

    Salut,

    Citation Envoyé par olivier252 Voir le message
    la ligne 17 correspond à la ligne 7 ici; Je ne comprends pas comment ma référence pstmt est nulle.
    Ce n'est pas pstmt qui est null, mais connection !
    Une NullPointerException ne concerne jamais ce qui est affecté, mais l'expression évaluée pour déterminer la valeur à affecter.
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  3. #3
    Membre habitué
    En effet, j'aurais du dire "pstmt pointe vers null" ?

  4. #4
    Modérateur

    Citation Envoyé par olivier252 Voir le message
    En effet, j'aurais du dire "pstmt pointe vers null" ?
    Non plus. pstmt n'existe plus, parce que c'est connection.prepareStatement(request) qui provoque la NullPointerException, et que ceci provoque l'arrêt immédiat de l'exécution, sortie de la méthode addPersonn, et donc pstmt n'existe plus, donc ne pointe sur rien du tout.
    C'est connection qui est null. C'est la seule possibilité avec une NPE sur cette ligne. Il faut donc chercher pourquoi. A priori, aucun code exécuté ne lui affecte une valeur.

    Au passage, je viens de le voir, pourquoi fait-on un rollback sur ConnexJDBC, alors qu'on exécute la requête et on la commit par connection ?

    A noter également que le statement n'est pas fermé en cas d'exception SQL (dans le cas où le statement est bien créé évidemment). Utilise un try-with-resource.
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  5. #5
    Membre habitué
    Ok,

    Au passage, je viens de le voir, pourquoi fait-on un rollback sur ConnexJDBC, alors qu'on exécute la requête et on la commit par connection ?
    En fait, c'est en cas d'erreur dans le try que le rollback se produit, mais le sns de ta remarque m'échappe sans doute
    Voici la méthode rollback de ConnexJDBC
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    public static void rollback() {
    		try {
    			cnx.rollback();
    		} catch (SQLException e) {
    			System.out.println(e.getMessage());
    		}
    		System.out.println("rollback effectué");
    	}




    A priori, aucun code exécuté ne lui affecte une valeur.
    L'appel de la constante QUERY_INSERT_PERSONNE fait référence à l'interface PersonneQuery suivante :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    public interface PersonneQuery {
    	static String QUERY_INSERT_PERSONNE = "insert into personne_table (nom, prenom) values(?,?)";
    }


    Utilise un try-with-resource.
    Comme ceci ?
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
     


    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    public void addPersonn(Personne p) {
    		String request = PersonneQuery.QUERY_INSERT_PERSONNE;
     
    		try (PreparedStatement pstmt = connection.prepareStatement(request)){
     
    			pstmt.setString(1, p.getNom());
    			pstmt.setString(2, p.getPrenom());
     
    			int flag = pstmt.executeUpdate();
    			if (flag >= 1) {
    				System.out.println(flag + "ligne(s) ajoutée(s)");
    				connection.commit();
    			} else { ...............................


    Du coup le try-with-ressources permet de se passer de fermer le PreparedStatement (
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    pstmt.close()
    grace à l'interface Autoclosable (si j'ai compris ?)

    MErci

  6. #6
    Modérateur

    Citation Envoyé par olivier252 Voir le message

    En fait, c'est en cas d'erreur dans le try que le rollback se produit, mais le sns de ta remarque m'échappe sans doute
    Voici la méthode rollback de ConnexJDBC
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    public static void rollback() {
    		try {
    			cnx.rollback();
    		} catch (SQLException e) {
    			System.out.println(e.getMessage());
    		}
    		System.out.println("rollback effectué");
    	}

    Le sens de ma remarque est que
    1. normalement, on fait une transaction à partir d'une même connexion : tous les ordres doivent être lancés avec la même instance de java.sql.Connection. Sinon on fait des insert dans une transaction et qu'on en rollback une autre en cas d'erreur, ça ne sert à rien.
    2. si ConnexJDBC est une connexion active (ou plutôt cnx exactement dans ton cas) et que tu utilises connection pour faire l'insert, peut-être que connection n'est pas initialisée, même si cnx l'est. Rien ne me permet de savoir si cnx et connection pointent vers la même instance de Connection, ou si même, l'une ou l'autre pointe bien vers une instance de Connection (donc est null ou pas). Le sens de ma remarque était que cette distinction de variables pouvaient être un indice pour comprendre pourquoi connection est null. C'est courant lorsqu'on multiplie les variables, surtout pour représenter la même chose : on affecte une variable et on oublie de le faire pour l'autre. Pire, on affecte une valeur différente aux deux, ou c'est ce qui finit par se passer au cours de l'exécution, alors qu'elles sont censées représenter la même chose.
    3. et pourquoi faire une méthode static dans une autre pour faire le rollback sur une autre variable, et pas faire directement connection.rollback() dans la méthode de traitement, au moins par cohérence.
    4. en résumé, tout ça pour continuer à dire qu'il faut s'intéresser à cette variable connection pour savoir pourquoi elle vaut null lors de l'invocation de preparedStatement().



    Citation Envoyé par olivier252 Voir le message


    L'appel de la constante QUERY_INSERT_PERSONNE fait référence à l'interface PersonneQuery suivante :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    public interface PersonneQuery {
    	static String QUERY_INSERT_PERSONNE = "insert into personne_table (nom, prenom) values(?,?)";
    }

    Ok, mais ce n'est pas pertinent dans la problématique du NullPointerException qui nous occupe.
    Cependant, faire des interfaces pour uniquement définir des constantes a été en usage certes, y'a une vingtaine d'années (et je l'ai utilisée moi-même), mais ça fait longtemps qu'on sait qu'il ne faut pas procéder comme ça. Une interface n'est pas faite pour ça. Si il est tentant de faire une interface en se disant qu'on peut implémenter de l'interface pour hériter des "constantes", il est préférable de faire une classe normale (éventuellement avec constructeur private pour éviter une instanciation contre productive), et de faire des imports static éventuellement. Avec une classe standard, on fait référence à la constante avec la même facilité que si c'était une interface, donc on ne gagne rien à faire une interface. Eventuellement, si la "constante" a besoin d'être qualifiée, un enum est envisageable, ce qui facilite par ailleurs le traitement masse.


    Citation Envoyé par olivier252 Voir le message


    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    public void addPersonn(Personne p) {
    		String request = PersonneQuery.QUERY_INSERT_PERSONNE;
     
    		try (PreparedStatement pstmt = connection.prepareStatement(request)){
     
    			pstmt.setString(1, p.getNom());
    			pstmt.setString(2, p.getPrenom());
     
    			int flag = pstmt.executeUpdate();
    			if (flag >= 1) {
    				System.out.println(flag + "ligne(s) ajoutée(s)");
    				connection.commit();
    			} else { ...............................


    Du coup le try-with-ressources permet de se passer de fermer le PreparedStatement (
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    pstmt.close()
    grace à l'interface Autoclosable (si j'ai compris ?)
    En réalité, le try-with-resources ferme de lui-même l'ensemble des ressources crées, ici le PreparedStatement en effet (grâce à l'interface AutoCloseable oui). C'est le développeur du coup qui peut se passer d'invoquer explicitement la méthode close().
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  7. #7
    Membre habitué
    Merci d'avoir pris le temps de m'expliquer

    Rien ne me permet de savoir si cnx et connection pointent vers la même instance de Connection, ou si même, l'une ou l'autre pointe bien vers une instance de Connection
    Du coup tant pis si je dis une énormité, mais est-ce que ça veut dire qu'il faut faire un pattern singleton avec une instance unique de Connection ?

    faire des interfaces pour uniquement définir des constantes a été en usage certes, y'a une vingtaine d'années
    J'ai supprimé l'interface

    Sinon pour le problème de départ, à savoir pourquoi connection vaut null, j'ai l'impression que c'est la classe de mon driver de connection qui a l'air de créer un soucis dans le forname.
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    Class.forName("com.mysql.jdbc.Driver");


    je vais chercher

  8. #8
    Modérateur

    Citation Envoyé par olivier252 Voir le message

    Sinon pour le problème de départ, à savoir pourquoi connection vaut null, j'ai l'impression que c'est la classe de mon driver de connection qui a l'air de créer un soucis dans le forname.
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    Class.forName("com.mysql.jdbc.Driver");

    Si le nom de la classe est incorrect, tu devrais avoir une exception (si tu l'affiches bien tu devrais la voir). Ensuite, effectivement, si c'est le cas, il y a aucune chance pour qu'une instance de Connection puisse être obtenue, et donc la variable qui devrait pointer sur cette instance vaut null.

    Avec les pilotes de type 4 (donc depuis longtemps), il n'est plus indispensable de charger la classe de pilote. Il suffit d'invoquer DriverManager.getConnection() avec la bonne URI JDBC.

    Mais ce n'est pas seul raison possible. Souvent, les implémentations de base qu'abordent les débutants, c'est une sorte de Singleton, avec une variable static (mauvaise pratique), mais ils utilisent une autre variable. La variable dans le "singleton" est bien initialisée, mais pas l'autre. Sans code, impossible de savoir si tu es ou non dans ce cas. Mais déjà le fait d'avoir deux types d'accès (un vers connection dans la méthode d'insert et un vers cnx pour le rollback me fait penser à ce cas d'implémentation.

    Citation Envoyé par olivier252 Voir le message

    Du coup tant pis si je dis une énormité, mais est-ce que ça veut dire qu'il faut faire un pattern singleton avec une instance unique de Connection ?
    1. première solution : créer pour chaque requête une nouvelle connexion et la fermer après

      Le problème est que créer une connexion consomme relativement pas mal de ressources, mémoire et temps, dans ton application et dans le sgbd, et que donc ça introduit de possibles latences dans l'exécution. Dans un environnement multiprocess on peut se trouver avec de nombreuses connexions concurrentes que le SGBD doit gérer. Il peut y avoir des limites dans le nombre de connexions possibles qui vont faire qu'il va falloir soit accepter que des requêtes plantent (parce que plus de connexion disponible) et donc procéder éventuellement à un système de "retry".
    2. Le singleton, pour peu qu'il soit correctement implémenté permet de supprimer le problème de latence (excepté à l'établissement de la connexion unique), mais va empêcher toute possibilité de multiprocess (requêtes simultanées). Dans un programme simple, un petit exercice de TP, on peut l'admettre.
    3. La meilleure solution est le pool de connexions, qui permet d'avoir plusieurs connexions qu'on établit au démarrage, et qui permettent de faire plusieurs requêtes simultanées. On règle le nombre de connexions dans le pool en fonction des besoins de l'application et des ressources disponibles.
      Un processus (une requête ou une séquence de requêtes) n'ouvre plus de connexion puis la ferme quand il a terminé, mais demande une connexion au pool puis la rend au pool. Lorsqu'un processus demande une connexion au pool, il peut être mis en attente tant qu'il n'y a plus de connexion disponible. C'est là que le dimensionnement du pool intervient pour obtenir dans un comportement moyen un taux d'attention acceptable.
      Un bon moyen d'implémenter une Connection dans ce cadre est de faire que close() rend la connexion au pool, ce qui permet d'avoir exactement la même forme de code avec ou sans pool :
      Code :Sélectionner tout -Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      try(Connection connection = pool.getConnection()) {
            // PreparedStatement statement = connection.preparedStatement ...
            // ResultSet rs = statement.executeQuery();
            // ...
      }


      On peut implémenter son propre système de pool (ce n'est pas très compliqué) mais il existe aussi des bibliothèques, comme Apache DBCP par exemple, ou HikariCP.
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  9. #9
    Membre habitué
    Hello,

    Si le nom de la classe est incorrect, tu devrais avoir une exception
    C'est bien le cas j'avais ClassNotFoundException en ajoutant un try/catch à ma méthode qui retourne l'instance de connexion.

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    il n'est plus indispensable de charger la classe de pilote

    je l'ai enlevée

    Par ailleurs après galère et recherche intensives, j'ai appris qu'il fallait préciser le fuseau horaire à utiliser par le serveur en passant le paramètre serverTimezone dans l’url de connexion.
    Sait-on jamais si ça peut aider des débutants comme moi :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    jdbc:mysql://localhost:3306/maBase?serverTimezone=GMT


    J'ai aussi ajouté le jar suivant mysql-java-connector-8.0.17.
    Tout ça pour dire que ça fonctionne..Merci à toi, j'ai appris bien plus que ma question de départ, et je vais m'intéresser au pool de connexion.