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.


SommaireLes RowSetLes RowSet déconnectés (CachedRowSet, etc.) (10)
précédent sommaire suivant
 

Les RowSet déconnectés ne se connectent à la source des données que lorsqu'ils ont besoin de lire ou écrire dans celle-ci. Le reste du temps, ils sont déconnectés de la source. Pour pouvoir se connecter, ils passent par l'intermédiaire d'un SyncProvider. Cette classe abstraite du package javax.sql.rowset.spi oblige à définir, entre autres, un RowSetReader et un RowSetWriter. Ce sont ces deux interfaces qui définissent l'écriture et la lecture de la source de données.

Les implémentations de Sun utilisent une instance de com.sun.rowset.providers.RIOptimisticProvider. Mais, on peut utiliser d'autres implémentations. Les SyncProvider, un peu à la manière des drivers, doivent être enregistrés auprès de SyncFactory. Par exemple :

Code java : Sélectionner tout
1
2
3
4
String nom = "mon.implementation.de.MonSyncProvider"; 
//nom complet de la classe 
SyncFactory.registerProvider(nom); 
//enregistrement du provider
Les RowSet déconnectés peuvent spécifier leur provider de deux façons :

Code java : Sélectionner tout
1
2
3
4
5
6
String nom = "mon.implementation.de.MonSyncProvider"; 
//grâce au constructeur 
CachedRowSet rowset = new CachedRowSetImpl(nom); 
//ou grâce à la méthode setSyncProvider 
CachedRowSet autreRowset = new CachedRowSetImpl(); 
autreRowset.setSyncProvider(nom);

Mis à jour le 11 avril 2013 Ioan

La connexion ou reconnexion à la source des données se fait de manière totalement invisible pour l'utilisateur. Celle-ci se fait lors de l'appel aux méthodes execute et acceptChanges. Toutefois, vous devez toujours vérifier que les trois propriétés suivantes sont définies : user, password et URL. Par exemple, si vous peuplez un CachedRowSet grâce à un ResultSet ces propriétés ne sont pas « transmises ». Voici un code qui pourrait poser problème :

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
String url = "jdbc:mysql://host/maBase"; 
String user = "user"; 
String password = "pass"; 
Connection connection = DriverManager.getConnection(url,user,password); 
//RECUPERATION D'UN RESULTSET 
Statement statement = connection.createStatement( 
                                                      ResultSet.TYPE_FORWARD_ONLY, 
                                                      ResultSet.CONCUR_READ_ONLY); 
String sql = "SELECT * FROM MaTable"; 
ResultSet resultat = statement.executeQuery(sql); 
//CREATION D'UN ROWSET 
CachedRowSet rowset = new CachedRowSetImpl(); 
rowset.populate(resultat); 
//modification des données 
  
//... 
  
//fin des modifications 
rowset.setUrl(url); 
rowset.setUserName(user); 
rowset.setPassword(pass); 
//sans ces trois lignes un appel à acceptChanges lèverait une exception
Si vous ne voulez pas passer ces trois propriétés, les méthodes acceptChanges et execute peuvent être appelées avec une Connection en paramètre. Vous n'aurez pas à vous soucier de la fermeture de celle-ci, les méthodes le feront à votre place. Par exemple :

Code java : Sélectionner tout
1
2
3
4
5
6
7
String url = "jdbc:mysql://host/maBase"; 
String user = "user"; 
String password = "pass"; 
CachedRowSet rowset = new CachedRowSetImpl(); 
rowset.setCommand("SELECT * FROM MaTable WHERE id = ?"); 
rowset.setInt(1,12345); 
rowset.execute(DriverManager.getConnection(url,user,password));

Mis à jour le 11 avril 2013 Ioan

N'étant pas continuellement connectés à la source, les RowSet déconnectés nécessitent l'appel d'une méthode supplémentaire pour valider leurs modifications. Il faut appeler la méthode acceptChanges pour transmettre les modifications internes au RowSet à la source. Cela est valable pour tout type de modification : INSERT, UPDATE, DELETE, etc. 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
CachedRowSet rowset = new CachedRowSetImpl(); 
//propriétés nécessaires à la connexion 
rowset.setUrl("jdbc:mysql://localhost/MaBase"); 
rowset.setUsername("user"); 
rowset.setPassword("pass"); 
//propriétés de la commande SQL 
rowset.setCommand("SELECT id, nom, prenom FROM Annuaire"); 
rowset.execute(); 
//modification des données 
if(rowset.first()){ 
   rowset.updateString("nom", "nouveau nom pour la première ligne"); 
   rowset.updateRow(); 
} 
rowset.moveToInsertRow(); 
rowset.updateInt("id",456); 
rowset.updateString("nom", "un nouveau nom"); 
rowset.updateString("prenom", "un nouveau prénom"); 
rowset.insertRow(); 
rowset.moveToCurrentRow(); 
//à ce stade seulement les données du RowSet ont été modifiées 
  
//la base contient toujours les données 
  
//il faut valider les modifications en appelant acceptChanges 
rowset.acceptChanges();

Mis à jour le 11 avril 2013 Ioan

La validation des modifications, des RowSet déconnectés, est susceptible de poser des problèmes de conflits. Cela peut être le cas si un tiers a déjà modifié une valeur de la base de données. La difficulté est donc de savoir laquelle des modifications est prioritaire. Laquelle doit être inscrite définitivement dans la base ?

L'implémentation de base, RIOptimisticProvider, doit son nom au fait qu'elle suit le modèle d'accès concurrentiel optimiste. C'est-à-dire qu'aucun verrou n'est posé sur les données et qu'elles peuvent être modifiées à tout moment par d'autres utilisateurs (ou programmes). En cas de détection de conflit, les modifications ne sont tout simplement pas validées. Par contre, acceptChanges lève une javax.sql.rowset.spi.SyncProviderException (fille de SQLException).

Les SyncProviderException permettent l'accès à un SyncResolver qui permet de gérer les conflits.

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
CachedRowSet rowset = new CachedRowSetImpl(); 
try{ 
   //modification des données 
   rowset.acceptChanges(); 
}catch(SyncProviderException spe){ 
   SyncResolver resolver = spe.getSyncResolver(); 
   int status = resolver.getStatus(); 
   switch(status){ 
      case SyncResovler.UPDATE_ROW_CONFLICT :  
         System.out.println("Conflit lors d'une mise à jour"); 
         break; 
   switch(status){ 
      case SyncResovler.DELETE_ROW_CONFLICT :  
         System.out.println("Conflit lors d'une suppression"); 
         break; 
   switch(status){ 
      case SyncResovler.INSERT_ROW_CONFLICT :  
         System.out.println("Conflit lors d'une insertion"); 
         break;    
   } 
   //ensuite il faut déterminer quelles valeurs doivent être insérées 
   //pour cela l'implémentation de Sun offre quelques méthodes supplémentaires 
}catch(SQLException sqle){ 
   //etc.. 
}
Un exemple plus complet est donné dans la description de la classe SyncResolver. Par contre, la RI (intégrée dans le JDK 5.0) ne semble pas fonctionner correctement. Le problème de synchronisation est bien détecté, mais la méthode getConflictValue renvoie systématiquement null. Il est donc impossible de comparer la valeur du RowSet et celle de la source de données. Par la suite, il est donc impossible de faire appel à la méthode setResolvedValue. Si quelqu'un a une solution, elle est la bienvenue

Mis à jour le 11 avril 2013 Ioan

Pour détecter les modifications faites à un RowSet déconnecté, on peut utiliser un ensemble de cinq méthodes. Une partie de celles-ci appartiennent à ResultSet alors que les autres appartiennent à CachedRowSet.

En utilisant des méthodes de ResultSet :

  • rowDeleted() : cette méthode indique si la ligne courante a été supprimée ;
  • rowInserted() : cette méthode indique si la ligne courante a été insérée ;
  • rowUpdated() : cette méthode indique si la ligne courante a été mise à jour.

Utilisez la méthode setShowDeleted pour permettre au curseur de passer sur les lignes qui ont été supprimées.

En utilisant des méthodes propres à CachedRowSet :

  • columnUpdated(int index) : cette méthode indique si la colonne d'index a été mise à jour ;
  • columnUpdated(String nom) : cette méthode indique si la colonne identifiée par nom a été mise à jour.

Ces méthodes peuvent lever une SQLException si l'index ou le nom ne sont pas valides, ou alors s'il n'y a pas de ligne courante.

Mis à jour le 11 avril 2013 Ioan

En plus des méthodes relatives aux transactions, plusieurs méthodes de CachedRowSet permettent d'annuler certaines modifications. En plus de cela, on peut facilement avoir accès aux valeurs originales. Par valeurs originales, on entend les valeurs contenues dans le RowSet lors de son peuplement ou juste après le dernier appel à la méthode acceptChanges.

Comment annuler certaines modifications ? :

  • undoDelete() : cette méthode permet d'annuler la suppression de la ligne courante ;
  • undoInsert() : cette méthode permet d'annuler l'insertion de la ligne courante ;
  • undoUpdate() : cette méthode permet d'annuler la modification de la ligne courante.

On peut noter que ces trois méthodes peuvent être appelées à n'importe quel moment précédant un appel à acceptChanges. Toutes trois lèvent une SQLException si la ligne pointée n'a pas été insérée, supprimée ou modifiée (selon la méthode). Une exception est aussi levée si le curseur pointe sur une ligne comme beforeFirst, afterLast ou insertRow.

Une dernière méthode permet l'annulation de toutes les modifications : c'est la méthode restoreOriginal. Cette méthode annule toutes les insertions, suppressions et mises à jour et place le curseur avant la première ligne.

Comment retrouver les valeurs originales ? :

  • getOriginal() : cette méthode retourne un ResultSet contenant les valeurs originales du RowSet. Ce ResultSet répond au standard des RowSet, c'est-à-dire ResultSet.TYPE_SCROLL_INSENSITIVE et ResultSet.CONCUR_UPDATABLE. Le curseur est placé avant la première ligne ;
  • getOriginalRow() : cette méthode retourne un ResultSet contenant les valeurs originales de la ligne courante. Ce ResultSet répond au standard des RowSet, c'est-à-dire ResultSet.TYPE_SCROLL_INSENSITIVE et ResultSet.CONCUR_UPDATABLE. Le curseur est placé avant la première ligne. De plus une SQLException est levée s'il n'y a pas de ligne courante (ie. beforeFirst, afterLast ou insertRow).


Une dernière méthode permet l'annulation de toutes les modifications : c'est la méthode restoreOriginal. Cette méthode annule toutes les insertions, suppressions et mises à jour et place le curseur avant la première ligne.

Mis à jour le 11 avril 2013 Ioan

Contrairement à l'interface ResultSet, RowSet propose une méthode toute faite pour avoir accès au nombre de tuples qu'elle contient. Par exemple :

Code java : Sélectionner tout
1
2
3
4
5
ResultSet resultat = ... ;  
CachedRowSet cachedRowSet = new CachedRowSetImpl(); 
cachedRowSet.populate(resultat); 
int nombreDeLignes = cachedRowSet.size(); 
System.out.println("Nombre de lignes : "+nombreDeLignes);
Naturellement les méthodes proposées dans la réponse à la question Comment connaître le nombre de lignes/colonnes d'un ResultSet ? restent valables. Mais plus très utiles ;-)

Mis à jour le 11 avril 2013 Ioan

Si l'espace mémoire à allouer à une instance de RowSet déconnecté est limité, il est possible de traiter les données par page. Il suffit d'indiquer le nombre de lignes que l'on désire par page.

Code java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CachedRowSetImpl rowset = new CachedRowSetImpl(); 
//mise en place des propriétés du RowSet 
rowset.setUrl("jdbc:mysql://localhost/MaBase"); 
rowset.setUsername("user"); 
rowset.setPassword("password"); 
rowset.setCommand("SELECT * FROM MaTable"); 
//on indique au RowSet de mettre 100 lignes par page 
rowset.setPageSize(100); 
//peuplement du RowSet 
rowset.execute(); 
int index = 0; 
while(rowset.nextPage()) { 
   //traitement de chaque page 
   System.out.println(" Page numéro "+(++index)); 
   while(rowset.next()) { 
      //traitement des données de la page 
   } 
}
La méthode setPageSize peut lever une SQLException si le paramètre est inférieur à zéro ou supérieur à la valeur retournée par getMaxRows.

Mis à jour le 11 avril 2013 Ioan

Pour faire cela, il faut utiliser la méthode release de CachedRowSet. Par exemple :

Code java : Sélectionner tout
1
2
3
4
5
6
7
8
CachedRowSet rowset = new CachedRowSetImpl(); 
//mise en place des propriétés  
rowset.execute(); 
//divers traitements 
rowset.release(); 
//maintenant le RowSet est vide de toutes données 
  
//par contre les propriétés (URL, user, password, commande...) sont maintenues
La méthode release envoie un RowSetEvent de « type » rowSetChanged;

Il ne faut pas confondre cette méthode avec la méthode close. Cette dernière en plus de vider le RowSet le rend disponible pour le GarbageCollector.

Mis à jour le 11 avril 2013 Ioan

L'interface CachedRowSet permet de faire quatre types de copie d'une instance particulière. Voici les méthodes utilisées et leurs particularités :

  • createCopy() : crée une copie en profondeur de l'instance. Les modifications faites sur la copie ne sont pas répercutées sur l'original. Les éventuels RowSetListener à l'écoute de l'original ne prennent pas en compte les modifications de la copie. La copie aura par contre les mêmes caractéristiques que l'original en ce qui concerne la possibilité d'être mise à jour, les mouvements possibles du curseur et les différents attributs (URL, datasource, etc.) ;
  • createCopyNoConstraints() : crée une copie en profondeur des données de l'instance. Les caractéristiques de la copie sont les mêmes que pour la méthode createCopy ;
  • createShared() : crée un « clone » de l'instance copiée. Toutes les modifications faites au clone sont répercutées sur l'original, mais aussi sur tous les autres clones existants. De même, tout RowSetListener à l'écoute des événements relatifs à l'original est enregistré auprès du (des) clone(s). Inversement tout nouveau listener mis à l'écoute d'un clone l'est aussi sur l'original et les autres clones ;
  • createCopySchema() : crée une copie vide de l'instance. C'est-à-dire une copie ne contenant aucune donnée. Cette copie ne conserve que le schéma (la structure) du RowSet. Les modifications de la copie ne sont pas répercutées sur l'original. Cette méthode peut facilement être utilisée pour stocker le schéma d'un WebRowSet sous forme de fichier XML.

Toutes ces méthodes retournent des instances de CachedRowSet sauf createCopySchema qui renvoie simplement un RowSet.

Attention : ces copies peuvent lever des SQLException en fonction de l'implémentation. La javadoc est très évasive sur le sujet. Par exemple, pour l'implémentation de Sun, une SQLException est levée pour toute copie si l'original a déjà un RowSetListener (???).

Mis à jour le 11 avril 2013 Ioan

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 ça


Réponse à la question

Liens sous la question
précédent sommaire suivant
 

Les 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 © 2017 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.

 
Responsables bénévoles de la rubrique Java : Mickael Baron - Robin56 -