FAQ JDBCConsultez toutes les FAQ
Nombre d'auteurs : 8, nombre de questions : 162, dernière mise à jour : 3 juin 2015 Ajouter une question
Cette FAQ a été réalisée à partir des questions fréquemment posées sur les forums de http://www.developpez.com et de l'expérience personnelle des auteurs.
Nous tenons à souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose sont correctes. Les auteurs font leur maximum, mais l'erreur est humaine. Cette FAQ ne prétend pas non plus être complète. Si vous trouvez une erreur, ou que vous souhaitez nous aider en devenant rédacteur, lisez ceci.
- Qu'est-ce qu'un ResultSet ? Que sont les lignes, les colonnes et le curseur ?
- Quels sont les différents types de ResultSet ?
- Comment retrouver les valeurs contenues dans un ResultSet ?
- Comment obtenir la valeur la plus récente d'une ligne ?
- Comment savoir si une valeur correspond au type SQL NULL ?
- Comment parcourir un ResultSet ?
- Comment déplacer le curseur sur une ligne précise ?
- Comment connaître la position du curseur ?
- Comment mettre à jour un ResultSet ?
- Comment insérer une ligne dans un ResultSet ?
- Comment calculer le nombre d'enregistrements d'un ResultSet ?
- Doit-on fermer un ResultSet ?
- Comment connaître les caractéristiques (type) d'un ResultSet depuis le programme ?
- Comment supprimer une ligne dans un ResultSet ?
- Comment retrouver l'index d'une colonne à partir de son nom ?
- Que sont les fetch size et fetch direction ?
Les instances de l'interface ResultSet contiennent les résultats d'une requête SQL. Ils contiennent les tuples (lignes) satisfaisant aux conditions de la requête. On peut noter que les ResultSet sont des objets couramment retournés par de nombreuses méthodes des classes appartenant aux packages java.sql et javax.sql. Cette utilisation se voit notamment dans les interfaces de métadonnées, qui renvoient beaucoup d'informations sous forme de ResultSet (cf. Que sont les métadonnées ? ).
La structure des ResultSet est très semblable à celle d'une Table dans une base de données relationnelle. C'est-à-dire :
- les colonnes (column) : ce sont les éléments verticaux de la table (ou ResultSet). Ils symbolisent les attributs des différents enregistrements de la table. Ils sont caractérisés par un nom et un domaine dans lequel ils puisent leur valeur (par exemple pour les entiers INTEGER ou VARCHAR pour les chaînes de caractères).
- les lignes (row) : ce sont les éléments horizontaux de la table. On les nomme aussi tuples ou n-uplets.Ils sont les différents enregistrementscontenus dans la table (ou ResultSet). Chaque ligne renseigne les attributs définis par les colonnes.
- le curseur (cursor) : cet objet pointe sur une « ligne ». Cette ligne peut être soit une ligne comme défini ci-dessus (contenant les données), soit une ligne spéciale comme afterLast, insertRow, etc. C'est le curseur qui permet le déplacement dans le ResultSet, pour l'accès aux différents enregistrements.
Voir Quels sont les différents types de ResultSet ? , Comment parcourir un ResultSet ? et Comment retrouver les valeurs contenues dans un ResultSet ? pour plus d'informations.
Le « type » d'un ResultSet est défini lors de la création de l'instruction (Statement, PreparedStatement ou CallableStatement). Le « type » d'un ResultSet est défini par trois caractéristiques, qui définissent les possibilités de déplacement, les possibilités de mise à jour et le maintien des curseurs lors des transactions. Par curseur, on entendra ici le ResultSet lui-même.
Les méthodes , createStatement, prepareStatement et prepareCall, présentent toutes trois la possibilité de spécifier ces attributs.
Code java : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 | Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) |
Pour les possibilités de déplacement (int resultSetType) :
- ResultSet.TYPE_FORWARD_ONLY : c'est la valeur par défaut (et la seule possible pour JDBC 1.0). Elle indique que les déplacements du curseur ne peuvent se faire « qu'en avant » (de la première à la dernière ligne). Une fois la dernière ligne atteinte (la méthode last renvoyant true) le ResultSet est fermé et les données ne sont plus accessibles ;
- ResultSet.TYPE_SCROLL_INSENSITIVE : cette valeur indique que le curseur peut être déplacé dans les deux sens, mais aussi arbitrairement (de manière absolue ou relative). Le terme insensitive indique que le ResultSet est insensible aux modifications des valeurs dans la base de données. Cela définit en fait une vue statique des données contenues dans le ResultSet ;
- ResultSet.TYPE_SCROLL_SENSITIVE : cette valeur indique que le curseur peut être déplacé dans les deux sens, mais aussi arbitrairement (de manière absolue ou relative). Le terme sensitive indique que le ResultSet est sensible aux modifications des valeurs dans la base de données. Cela définit en fait une vue dynamique des données contenues dans le ResultSet.
Pour les possibilités de mise à jour (int resultSetConcurrency) :
- ResultSet.CONCUR_READ_ONLY : c'est la valeur par défaut (et la seule possible pour JDBC 1.0). Elle indique que les données contenues dans le ResultSet ne peuvent qu'être lues ;
- ResultSet.CONCUR_UPDATABLE : cette valeur indique que l'on peut modifier les données de la base via le ResultSet.
Pour le maintien des curseurs (int resultSetHoldability) :
- ResultSet.HOLD_CURSORS_OVER_COMMIT : les objets ResultSet ne sont pas fermés. Ils restent ouverts lorsqu'une validation est effectuée implicitement ou explicitement ;
- ResultSet.CLOSE_CURSORS_AT_COMMIT : les objets ResultSet sont fermés lorsqu'une validation est effectuée implicitement ou explicitement.
Pour retrouver les valeurs contenues dans les lignes d'un ResultSet, on a à notre disposition une dizaine de méthodes getXXX. Un exemple vaut mieux qu'un long discours, voici la structure de la table étudiée :
Code sql : | Sélectionner tout |
1 2 3 4 5 6 | CREATE TABLE MaTable ( id INTEGER PRIMARY KEY, nom VARCHAR(50), prix DECIMAL, date DATE ) |
Code java : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Statement statement = connection.createStatement( ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); String sql = "SELECT * FROM MaTable"; ResultSet resultat = statement.executeQuery(sql); while(resultat.next()){ int id = resultat.getInt(1); String nom = resultat.getString(2); double prix = resultat.getDouble(3); java.sql.Date date = resultat.getDate(4); int row = resultat.getRow(); System.out.println("Données contenues dans la ligne "+row); System.out.println("id : "+id+" nom : "+nom+ " prix : "+prix+" date : "+date); } |
Code java : | Sélectionner tout |
1 2 3 4 | int id = resultat.getInt("id"); String nom = resultat.getString("nom"); double prix = resultat.getDouble("prix"); java.sql.Date date = resultat.getDate("date"); |
Pour connaître les correspondances entre les types Java et les types SQL, regardez : Tableau de relations .
ResultSet nous propose la méthode refreshRow pour mettre à jour les valeurs contenues dans une ligne donnée. Cette fonctionnalité peut être intéressante lors de traitements longs pendant lesquels les données sont susceptibles de changer (à cause d'autres utilisateurs par exemple).
Code java : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Statement statement = connection.createStatement( ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); String sql = "SELECT * FROM MaTable"; ResultSet resultat = statement.executeQuery(sql); resultat.first(); //on récupère le "prix" de la première ligne double d1 = resultat.getDouble("prix"); //un traitement quelconque resultat.fisrt(); //on vérifie que le prix n'a pas changé durant le traitement resultat.refreshRow(); double d2 = resultat.getDouble("prix"); if(d1!=d2){ //le prix a changé } |
Comme pour les paramètres de retour des procédures stockées, les ResultSet renvoient des valeurs par défaut lorsqu'ils rencontrent une valeur SQL NULL. Pour vérifier que 0 ou la chaîne vide ne sont pas en fait de type SQL NULL, il faut utiliser la méthode wasNull.
Code java : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | Statement statement = connection.createStatement( ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); String sql = "SELECT * FROM MaTable"; ResultSet resultat = statement.executeQuery(sql); while(resultat.next()){ double prix = resultat.getDouble(3); String text = (resultat.wasNull())?"pas de prix spécifié(NULL)":String.valueOf(prix); System.out.println(" prix : "+txt); |
L'interface ResultSet fournit plusieurs méthodes pour parcourir les données. Ces différentes méthodes sont bien sûr disponibles selon le type de ResultSet. Notons qu'au contraire de la plupart des structures de données en Java (tableaux, vector, List, etc.) les index des lignes commencent à 1.
- next : passe le curseur à l'élément suivant ;
- previous : passe le curseur à l'élément précédent ;
- first : passe le curseur sur le premier élément ;
- last : passe le curseur sur le dernier élément ;
- beforeFirst : passe le curseur avant le premier élément (position par défaut du curseur) ;
- afterLast : passe le curseur après le dernier élément.
Voici un exemple :
Code java : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | Connection connection = DriverManager.getConnection(url,user,password); Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE ,ResultSet.CONCUR_READ_ONLY); ResultSet resultat = statement.executeQuery("SELECT * FROM MaTable"); System.out.println(resultat.isbeforeFirst()); //true resultat.next(); //on se retrouve ici sur la première ligne //traitement de la première ligne ... while(resultat.next()){ //traitement des autres lignes } resultat.first(); //on a replacé ici le curseur sur la première ligne resultset.afterlast(); //on a replacé le curseur après la dernière ligne while(resultat.previous()){ // on parcourt ici le ResultSet de la dernière à la première ligne } //etc. |
En dehors des méthodes first, beforeFirst, last et afterlast, ResultSet propose quatre autres méthodes relatives au positionnement du curseur. Les deux premières, absolute et relative, permettent de mouvoir le curseur sur une ligne précise, soit de manière absolue (en indiquant un numéro de ligne), soit de manière relative à la ligne actuellement pointée.
Code java : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | Connection connection = DriverManager.getConnection(url,user,password); Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE ,ResultSet.CONCUR_READ_ONLY); ResultSet resultat = statement.executeQuery("SELECT * FROM MaTable"); resultat.next(); //on se trouve ici sur la première ligne resultat.absolute(40); //on se trouve ici sur la 40e ligne resultat.absolute(1); //on se trouve sur la première ligne == resultat.first() resultat.absolute(-1); //on se trouve sur la dernière ligne == resultat.last() resultat.absolute(-2); //on se trouve sur l'avant-dernière ligne == resultat.last()+reusultat.previous() resultat.first(); //on revient sur la première ligne resultat.relative(1); //on se trouve sur la deuxième ligne == resultat.next(); resultat.relative(50); //on se trouve sur la 52e ligne resultat.relative(-1); //on se trouve sur la 51e ligne == resultat.previous() |
ResultSet propose la méthode int getRow()qui récupère l'index du curseur. De plus, les méthodes isFirst, isLast, isBeforeFirst et isAfterLast nous permettent de déterminer des positions bien précises du curseur.
Voici le corps d'une méthode nous permettant de déterminer la position du curseur :
Code java : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public void afficherInfosCurseur(ResultSet resultat) throws SQLException{ String curseur = "row = "+resultat.getRow(); if(resultat.isBeforeFirst()){ curseur += "(avant la première ligne)"; } if(resultat.isAfterLast()){ curseur += "(après la dernière ligne)"; } if(resultat.isFirst()){ curseur += "(première ligne)"; } if(resultat.isLast()){ curseur += "(dernière ligne)"; } System.out.println(curseur); } |
Code java : | Sélectionner tout |
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 | Statement statement = connection.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); String sql = "SELECT * FROM Annuaire"; ResultSet resultat = statement.executeQuery(sql); afficherInfosCurseur(resultat); //on parcourt le ResultSet de la première à la dernière ligne while(resultat.next()){ afficherInfosCurseur(resultat); } afficherInfosCurseur(resultat); //on parcourt le ResultSet de la dernière à la première ligne while(resultat.previous()){ afficherInfosCurseur(resultat); } afficherInfosCurseur(resultat); //quelques mouvements absolus resultat.absolute(-1); afficherInfosCurseur(resultat); resultat.absolute(1); afficherInfosCurseur(resultat); //quelques mouvements relatifs resultat.relative(5); afficherInfosCurseur(resultat); resultat.relative(-3); afficherInfosCurseur(resultat); resultat.relative(-3); afficherInfosCurseur(resultat); |
Code java : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | row = 0(avant la première ligne) row = 1(première ligne) row = 2 //... row = 7 row = 8(dernière ligne) row = 0(après la dernière ligne) row = 8(dernière ligne) row = 7 //... row = 2 row = 1(première ligne) row = 0(avant la première ligne) row = 8(dernière ligne) row = 1(première ligne) row = 6 row = 3 row = 0(avant la première ligne) |
Les ResultSet vérifiant ResultSet.CONCUR_UPDATABLE peuvent être mis à jour. Pour mettre à jour plusieurs méthodes update sont à notre disposition. Les premières sont de la forme updateXXX où XXX est le type de la donnée devant être mise à jour. La dernière est updateRow qui permet la mise à jour effective de la ligne visée. Par exemple :
Code java : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | Connection connection = ...; //création d'une instruction renvoyant des résultats //pouvant être mis à jour Statement statement = connection.createStatement( ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); //on sélectionne tous les tuples de la table Annuaire String sql = "SELECT * FROM Annuaire"; ResultSet resultat = statement.executeQuery(sql); // on se place sur le premier tuple récupéré resultat.first(); //on récupère la valeur de la colonne "nom" String nom1 = resultat.getString("nom"); //on met à jour la valeur de la colonne "nom" resultat.updateString("nom", "nouveauNom"); //on met à jour la valeur dans la table resultat.updateRow(); String nom2 = resultat.getString("nom"); System.out.println("Ancien nom = "+nom1+ "Nouveau nom = "+nom2); |
Si vous changez la position du curseur avant l'appel de la méthode updateRow, toutes les mises à jour sont perdues.
Les méthodes updateXXX (où XXX indique le type des données) sont aussi utilisées lors de l'insertion de nouvelles lignes dans le ResultSet.
Pour insérer une nouvelle ligne dans un ResultSet, il faut d'abord positionner le curseur avec la méthode moveToInsertRow. Ensuite, il faut définir les valeurs de la nouvelle ligne avec les méthodes updateXXX (où XXX indique le type des données). Finalement, il faut faire appel à la méthode insertRow, qui insérera la nouvelle ligne.
Code java : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | Connection connection = ...; Statement statement = connection.createStatement( ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); String sql = "SELECT * FROM Annuaire"; ResultSet resultat = statement.executeQuery(sql); // on se place sur la ligne à insérer resultat.moveToInsertRow(); //on renseigne les différents champs resultat.updateInt("id",456); resultat.updateString("nom","nouveauNom"); resultat.updateString("prenom","nouveauPrenom"); //on insère effectivement la nouvelle ligne resultat.insertRow(); |
La méthode employée permet :
- de ne pas le parcourir en entier ;
- de ne pas faire de requête de type SELECT COUNT.
Il faut définir un ResultSet scrollable et de se positionner sur le dernier élément, d'obtenir son rang et de revenir à la place initiale.
Code Java : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public static int countItem(Connection contact, String query) throws SQLException { Statement stmt = contact.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); ResultSet resultset = stmt.executeQuery(query); int nbItem = 0; if (resultset!=null) { resultset.last(); nbItem = resultset.getRow(); resultset.beforeFirst(); } return nbItem; } |
Le problème est le même que pour une Connection ou un Statement. Un ResultSet sera automatiquement fermé lors de la fermeture du Statement dont il dépend. Cependant, il est conseillé de le fermer explicitement.
Code java : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Connection connection = null; Statement statement = null; ResultSet resultat = null; try{ //initialisation des trois éléments //divers traitements sur la base de données } catch(Exception){ //gestion des différents types d'erreurs pouvant survenir } finally{ try{if(resultat!=null){resultat.close();}}catch(Exception e){} try{if(statement!=null){statement.close();}}catch(Exception e){} try{if(connection!=null){connection.close();}}catch(Exception e){} } |
L'interface ResultSet met à notre disposition toutes les méthodes nécessaires pour découvrir, depuis le programme, les caractéristiques de ceux-ci. On peut aussi utiliser le Statement correspondant au ResultSet pour obtenir ces informations. Par exemple :
Code java : | Sélectionner tout |
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 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | public void afficherCaracteristiques(ResultSet resultat)throws SQLException{ int type = resultat.getType(); //ou à partir du Statement //int type = resultat.getStatement().getResultSetType(); int concurrency = resultat.getConcurrency(); //ou à partir du Statement //int concurrency = resultat.getStatement().getResultSetConcurrency(); int holdability = resultat.getStatement().getResultSetHoldability(); String sType = ""; String sConcurrency = ""; String sHoldability = ""; switch(type){ case ResultSet.TYPE_FORWARD_ONLY: sType = "Type : FORWARD_ONLY"; break; case ResultSet.TYPE_SCROLL_INSENSITIVE: sType = "Type : SCROLL_INSENSITIVE"; break; case ResultSet.TYPE_SCROLL_SENSITIVE: sType = "Type : SCROLL_SENSITIVE"; break; default : sType = "Type inconnu ??? Bug ???"; break; } switch(concurrency){ case ResultSet.CONCUR_READ_ONLY: sConcurrency = "Concurrency : CONCUR_READ_ONLY"; break; case ResultSet.CONCUR_UPDATABLE: sConcurrency = "Concurrency : CONCUR_UPDATABLE"; break; default : sConcurrency = "Concurrency inconnue ??? Bug ???"; break; } switch(holdability){ case ResultSet.HOLD_CURSORS_OVER_COMMIT : sHoldability = "Holdability : HOLD_CURSORS_OVER_COMMIT"; break; case ResultSet.CLOSE_CURSORS_AT_COMMIT: sHoldability = "Holdability : CLOSE_CURSORS_AT_COMMIT"; break; default : sHoldability = "Holdability inconnue ??? Bug ???"; break; } System.out.println("Les caractéristiques de ce ResultSet sont : "); System.out.println(sType); System.out.println(sConcurrency); System.out.println(sHoldability); } |
Pour supprimer une ligne dans un ResultSet, il suffit d'utiliser la méthode deleteRow. Par exemple pour supprimer la ligne 32 d'un ResultSet, on pourrait faire comme ceci :
Code java : | Sélectionner tout |
1 2 3 | ResultSet resultat = ...; resultat.absolute(32); resultat.deleteRow(); |
Code java : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 | Connection connection = ...; Statement statement = connection.createStatement( ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); String sql = "SELECT * FROM MaTable"; ResultSet resultat = statement.executeQuery(sql); while(resultat.next()){ resultat.deleteRow(); System.out.println("row : "+resultat.getRow()); System.out.println(" deleted : "+resultat.rowDeleted()); } |
Pour retrouver l'index d'une colonne à partir de son nom, on peut utiliser la méthode findColumn(String nom). Par exemple :
Code java : | Sélectionner tout |
1 2 3 4 | ResultSet resultat = ...; String nom = "nomDeLaColonne"; int index = resultat.findColumn(nom); System.out.println("La colonne "+nom+" a pour index "+index); |
N.B. Pour faire l'opération inverse, c'est-à-dire retrouver le nom d'une colonne à partir d'un index, il est nécessaire de passer par ResultSetMetaData.
Les fetch size et fetch direction sont des caractéristiques des ResultSet.
Oui, mais qu'est-ce que c'est ?
Fetch size et fetch direction représentant respectivement le nombre de lignes que le driver devra garder en mémoire lors d'une requête et la « direction » de traitement de ces lignes.
Pour bien comprendre ce que désigne la fetch size, on va prendre l'exemple d'une requête retournant plusieurs milliers de lignes. Dans ce cas, l'intégralité des données n'est pas gardée en mémoire par le ResultSet. Les données sont gardées en mémoire par « blocs ». Une fois qu'un bloc est parcouru (avec la méthode next(), par exemple), le driver va chercher le bloc suivant. Et ainsi de suite jusqu'au traitement complet des données. La taille de ces blocs est tout simplement la fetch size.
Attention, ces deux valeurs sont seulement des indications données au driver. En pratique, selon les implémentations, le driver peut en tenir compte ou non.
Comment récupérer ces valeurs ?
Vous pouvez récupérer ces valeurs grâce aux méthodes getFetchSize et getFetchDirection de l'interface ResultSet.
Pour récupérer les valeurs par défaut (indiquées par le driver), vous pouvez utiliser les mêmes méthodes, mais de l'interface Statement.
Comment indiquer ces valeurs ?
Vous pouvez indiquer les valeurs de fetch size et fetch direction avec les méthodes setFetchSize et setFetchDirection.
Pour la méthode setFetchSize, le paramètre à indiquer est le nombre de lignes (int) que le driver devra chercher. Une SQLException est levée si ce paramètre est strictement inférieur à 0 ou supérieur à Statement.getMaxRows(). Dans le cas où ce paramètre vaut 0, le driver décide lui-même du nombre de lignes à garder en mémoire.
Pour la méthode setFetchDirection, le paramètre à indiquer est un de ceux-ci : ResultSet.FETCH_FORWARD, ResultSet.FETCH_REVERSE ou ResultSet.FETCH_UNKNOWN.
Vous pouvez aussi utiliser ces deux méthodes, mais dans la classe Statement, pour indiquer les valeurs par défaut à utiliser pour les ResultSet crée.
Proposer une nouvelle réponse sur la FAQ
Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour çaLes sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2024 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.