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

 Java Discussion :

Ajout de ligne dans une JTable


Sujet :

Java

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Juin 2009
    Messages
    130
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2009
    Messages : 130
    Par défaut Ajout de ligne dans une JTable
    Bonjour,

    Je cherche depuis plusieurs heures comment afficher les lignes que je récupère depuis ma requête SQL (via mon ResultSet) sur ma JTable.

    J'ai créer une méthode qui exécute une requête SQL et retourne une List<List<Object>>(2 cases). La première case (0) contient une liste avec le nom des colonnes et la seconde case (1) contient la seconde liste qui contient des Object[] correspondant aux champs de la base (les lignes).
    Voilà comment elle se remplissent :
    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
    List<List<Object>> result = new ArrayList<List<Object>>(2);
    List<Object> dataLines = new ArrayList<Object>();
    List<Object> nomColonne = new ArrayList<Object>();
    ResultSet rs = c.createStatement().executeQuery(sql_command);
    int numColumns = rs.getMetaData().getColumnCount();
    for ( int i = 0 ; i < numColumns ; i++ )
    	nomColonne.add(rs.getMetaData().getColumnLabel(i+1));
    result.add(nomColonne);
    // Read SQL rows
    while(rs.next())
    {
    	Object[] obj = new Object[numColumns];
    	for ( int i = 0 ; i < numColumns ; i++ ) {
    		// Column numbers start at 1.
    		obj[i] = rs.getObject(i+1);
    	}
    	dataLines.add(obj);
    }
    result.add(dataLines);

    Ensuite dans la fonction qui récupère cette liste de liste, j'essaye de le mettre dans ma JTable.
    Pour les colonnes c'est bon car je fais un :
    liste.get(0).toArray() -> Object[]
    et
    model.setColumnIdentifiers(liste.get(0).toArray());

    En revanche pour ajouter les lignes, j'utilise model.addRow(Object[]);
    Donc j'ai essayer :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    for(int i = 0; i < liste.get(1).size(); i++)
    {
           Object[] o = liste.get(1).get(i). // Je n'ai pas accès à toArray() ici
           model.addRow(o);
    }

    Comment faire pour ajouter les lignes à la JTable ?
    Est-ce que une List<List<Object>> est une bonne solution ?
    (Bien entendu, il ne faut pas les mettre dans la méthode qui exécute la requête...)

  2. #2
    Membre émérite
    Inscrit en
    Mars 2006
    Messages
    848
    Détails du profil
    Informations personnelles :
    Âge : 41

    Informations forums :
    Inscription : Mars 2006
    Messages : 848
    Par défaut
    Bonjour,

    si ta List<List<Object>> n'a pas d'autre but que d'être mis dans une table, peut-être serait-il plus judicieux de créer un TableModel à la place. Comme ça, tu pourra le fournir directement à ta JTable et tu n'auras plus de soucis de conversion.

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Juin 2009
    Messages
    130
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2009
    Messages : 130
    Par défaut
    En gros je veux faire une méthode qui retourne la liste des colonnes et la liste des lignes d'une requête SQL passé en paramètre.

    Puis remplir ma JTable ensuite. Rien de bien compliquer mais je n'y arrive pas !

    Ensuite je pense qu'on peux s'en sortir avec le DefaultTableModel.
    Sauf que je n'arrive pas à récupérer mes champs lorsque la liste contient des tableaux d'objets. Car il ne voit pas le type Object[] à la lecture !

  4. #4
    Membre émérite
    Inscrit en
    Mars 2006
    Messages
    848
    Détails du profil
    Informations personnelles :
    Âge : 41

    Informations forums :
    Inscription : Mars 2006
    Messages : 848
    Par défaut
    Citation Envoyé par Aure7780 Voir le message
    Sauf que je n'arrive pas à récupérer mes champs lorsque la liste contient des tableaux d'objets. Car il ne voit pas le type Object[] à la lecture !
    Qu'entends-tu par "il ne voit pas le type Object[]"?

    Egalement, peux-tu être un peu plus précis sur ce qui te pose problème:
    - Lire le ResultSet,
    - Remplir le TableModel,
    - autre chose?

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Juin 2009
    Messages
    130
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2009
    Messages : 130
    Par défaut
    Eh bien :
    modelTab.setColumnIdentifiers(...
    Cette méthode du model attends un Object[] en paramètre.
    Donc pour faire ça je fais l.get(0).toArray(); -> je récupère le premier élément de ma liste qui contient une liste de nom de colonnes (List) puis je fais toArray() pour qu'il retourne un Object[]


    En revanche modelTab.addRow(...
    Cette méthode du model attend aussi un Object[] en paramètre.
    Or le second élément de ma liste contient une liste (List) qui contient des Object[] représentant les champs récupérés de la bdd.
    Donc dans une boucle je fais :
    for(int i=0; i < l.get(1).size(); i++)
    l.get(1).get(i);

    Le problème c'est que j'ai pas la méthode toArray() pour avoir un type Object[] et par défaut l.get(1).get(i) retourne un Object.

    Le problème c'est remplir le TableModel.


    EDIT :
    J'ai trouvé : j'ai casté l.get(1).get(i); en Object[]
    Ma boucle deviens donc :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    for(int i = 0; i < l.get(1).size() ; i++)
       modelTab.addRow((Object[])l.get(1).get(i));
    Après je sais pas si c'est propre ?

  6. #6
    Membre émérite
    Inscrit en
    Mars 2006
    Messages
    848
    Détails du profil
    Informations personnelles :
    Âge : 41

    Informations forums :
    Inscription : Mars 2006
    Messages : 848
    Par défaut
    Citation Envoyé par Aure7780 Voir le message
    Après je sais pas si c'est propre ?
    Il y a un point que je trouve "pas très propre" : Dans ta liste, tes éléments ne représentent pas des objets similaires. La valeur à l'index 0 n'est pas du tout utilisé de la même manière que la valeur à l'index 1.
    Conceptuellement, ça me gène.

    Comme ces valeurs ont des rôles différents, il serait plus propre de te créer un objet Resultat (par exemple) qui contiendra deux champs : les en-têtes (List<String> -> ta première valeur) et les données (List<Object[]> -> ta seconde valeur).

    Dèjà, tu vois que le cast que tu as été obligé de faire disparaitrait, c'est plutôt bon signe.

    Après, en regardant cette classe Resultat de plus près, on peut se rendre compte qu'elle est assez proche d'un TableModel. Donc pourquoi ré-inventer la roue? Il n'y a qu'à utiliser une des implémentations du TableModel directement.

    Donc ce que je te suggère, c'est de remplir directement un TableModel, sans créer de List<List<Object>>.

  7. #7
    Membre confirmé
    Profil pro
    Inscrit en
    Juin 2009
    Messages
    130
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2009
    Messages : 130
    Par défaut
    Merci ! C'est un peux plus claire.
    C'est ce que j'aurai du faire une classe résultat au lieu de List<List...

    Voici une solution :
    Classe TableModel :
    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
    public class TableModelList extends AbstractTableModel {
    	private final List<Object[]> donnees = new ArrayList<Object[]>();
    	private final List<String> nomColonnes = new ArrayList<String>();
     
    	public TableModelList() {
    		super();
    	}
     
    	public int getRowCount() {
    		return donnees.size();
    	}
     
    	public int getColumnCount() {
    		return nomColonnes.size();
    	}
     
    	public String getColumnName(int columnIndex) {
    		return nomColonnes.get(columnIndex).toString();
    	}
     
    	public Object getValueAt(int rowIndex, int columnIndex) {
    		return donnees.get(rowIndex)[columnIndex];
    	}
     
    	public void setColumnIdentifiers(List<String> nomsColumn) {
    		for(String nom : nomsColumn)
    			nomColonnes.add(nom);
    	}
     
    	public void addRow(List<Object[]> data) {
    		for(Object[] obj : data)
    			donnees.add(obj);
    	}
    }
    et 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
    25
    26
    27
    28
    29
    30
    public TableModelList executeSQLQuery(Connection c, String sql_command){
    	TableModelList modelTab = new TableModelList();
    	List<Object[]> dataLines = new ArrayList<Object[]>();
    	List<String> nomColonne = new ArrayList<String>();
    	try {
    		ResultSet rs = c.createStatement().executeQuery(sql_command);
    		int numColumns = rs.getMetaData().getColumnCount();
    		for ( int i = 0 ; i < numColumns ; i++ )
    			nomColonne.add(rs.getMetaData().getColumnLabel(i+1));
    		modelTab.setColumnIdentifiers(nomColonne);
    		// Read SQL rows
    		while(rs.next())
    		{
    			Object[] obj = new Object[numColumns];
    			for ( int i = 0 ; i < numColumns ; i++ ) {
    				// Column numbers start at 1.
    				obj[i] = rs.getObject(i+1);
    			}
    			dataLines.add(obj);
    		}
    		modelTab.addRow(dataLines);
    		rs.close();
    	} 
    	catch (SQLException sqle) {
    		sqle.printStackTrace();
    		System.out.println("SQLException: " + sqle.getMessage());
    		System.out.println("SQLState: " + sqle.getSQLState());
    	}
    	return modelTab;
    }

    et l'appel dans mon Action sur un bouton :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Connection c = getSQLConnection(...);
    String sql_cmd = "SELECT * FROM test";
    tableau.setModel(executeSQLQuery(c, sql_cmd)); // tableau -> JTable

  8. #8
    Membre chevronné

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2010
    Messages
    246
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Mai 2010
    Messages : 246
    Par défaut
    Quand tu dis "voici une solution", sous entends-tu que ton problème est résolu ?

    Et sinon un détail me chagrine dans ton code (ou peut être est-ce la fatigue...).

    Dans ta classe TableModelList tu as le constructeur suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    public TableModelList() {
          super();
    }
    Donc, "super()" va appelé le constructeur de la classe "AbstractTableModel". Je trouve cela un peu bizarre, d'appeler le constructeur d'une classe Abstraite...

    Enfin si tu n'as pas d'erreur de compilation je suppose que "AbstractTableModel" n'est pas déclarlé comme étant une classe Abstraite, mais dans ce cas, peut être serait-il préférable de mettre un nom qui correspond mieux...

  9. #9
    Membre émérite
    Inscrit en
    Mars 2006
    Messages
    848
    Détails du profil
    Informations personnelles :
    Âge : 41

    Informations forums :
    Inscription : Mars 2006
    Messages : 848
    Par défaut
    C'est cela.

    En plus de s'intégrer parfaitement dans une JTable, cette structure a le mérite d'être plus explicite que la précédente.

    Pour paufiner un peu les choses, il manque un détail:
    Quand la structure ou les données de la table changent, il faut appeler la méthode fireXXX adaptée pour prévenir les listeners.
    Cela permettra notament au composant JTable de se mettre à jour automatiquement après un changement.

  10. #10
    Membre émérite
    Inscrit en
    Mars 2006
    Messages
    848
    Détails du profil
    Informations personnelles :
    Âge : 41

    Informations forums :
    Inscription : Mars 2006
    Messages : 848
    Par défaut
    Citation Envoyé par michon Voir le message
    Je trouve cela un peu bizarre, d'appeler le constructeur d'une classe Abstraite...
    Cela n'a rien d'étrange, une classe abstraite laisse juste libre l'implémentation de certaines méthodes, mais en aucun cas des constructeurs.

    Qu'elle écrive le "super" ou non, le constructeur de la classe mère sera de toute façon appelé.

    Pour ma part, je préfère mettre explicitement l'appel au constructeur, même s'il s'agit de celui par défaut. Cela permet de se rappeler qu'il y a du code exécuté avant celui situé juste en-dessous. Mais après, chacun ses goûts

  11. #11
    Membre confirmé
    Profil pro
    Inscrit en
    Juin 2009
    Messages
    130
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2009
    Messages : 130
    Par défaut
    Merci pour ton aide Deaf.

    Concernant le super pour la classe abstraite, je me suis inspirée d'un tuto sur developpez : http://baptiste-wicht.developpez.com...ing/jtable/#L3

    Pour paufiner un peu les choses, il manque un détail:
    Quand la structure ou les données de la table changent, il faut appeler la méthode fireXXX adaptée pour prévenir les listeners.
    Cela permettra notament au composant JTable de se mettre à jour automatiquement après un changement.
    J'ai pas encore attaqué les évènements maintenant je sais ce que sont les méthodes avec fire devant.
    Dès que je ferai mes évènements j'aurais juste à faire les méthodes dans la classe. Par contre je vois pas encore comment marche les fireXXX.

    J'ai aussi rajouter les méthodes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    public void removeRow(int rowIndex) { // pour supprimer une ligne
    	if(rowIndex >= 0 && rowIndex <= getRowCount())
    		donnees.remove(rowIndex);
    }
    et
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    public void removeAllRows() { // pour vider ma liste...
    	if(!donnees.isEmpty())
    		donnees.clear();
    }

  12. #12
    Membre chevronné

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2010
    Messages
    246
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Mai 2010
    Messages : 246
    Par défaut
    euh...

    Le propre d'une classe Abstraite n'est-il pas qu'elle n'est pas instanciable ? Donc comment pourrait-on appeler son constructeur ?

    Sur ce, si cela parait normal à tout le monde... je pense que je dois avoir besoin de sommeil... pas bien de coder trop tard

    Edit : toutes mes excuses de flooder ce topic...

  13. #13
    Membre émérite
    Inscrit en
    Mars 2006
    Messages
    848
    Détails du profil
    Informations personnelles :
    Âge : 41

    Informations forums :
    Inscription : Mars 2006
    Messages : 848
    Par défaut
    En fait, les listeners vont s'enregistrer auprès de ton modèle via la méthode :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     void addTableModelListener(TableModelListener l)
    Cette méthode est implémentée par l'AbstractTableModel, tu n'as pas à t'en soucier.

    Ensuite, quand tu fais une modif, tu dois appeler la méthode fireXXX adaptée. Cette méthode (implémentée elle aussi par l'AbstractTableModel), va prévenir tous les listeners déjà enregistrés. Tu n'a pas à te soucis du mécanisme en-dessous.

    Exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    public void addRow(List<Object[]> data) {
        // Code pour ajouter les données
        ...
     
        // On notifie
        fireTableRowsInserted(getRowCount(), getRowCount());
    }

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

Discussions similaires

  1. Ajout d'une ligne dans une JTable
    Par d_hazem dans le forum Composants
    Réponses: 3
    Dernier message: 22/09/2008, 10h56
  2. [AJAX] Ajout de lignes dans une table avec Ajax
    Par Tententai dans le forum Général JavaScript
    Réponses: 4
    Dernier message: 01/11/2007, 15h54
  3. ajout/suppression d'une ligne dans une jTable
    Par amelA dans le forum Composants
    Réponses: 1
    Dernier message: 16/05/2007, 08h42
  4. [web] Wxperl -> ajout de ligne dans une grid
    Par Airmoi dans le forum Interfaces Graphiques
    Réponses: 1
    Dernier message: 08/11/2006, 15h55
  5. Réponses: 14
    Dernier message: 22/09/2005, 16h49

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