MokaByte 63 - Maggio 2002 
Swing JTable
Dall'accesso alla visualizzazione dei dati
di
Nicola Colonna
Continua la serie di articoli sul potente componente Swing Jtable. Questo mese la visualizzazione di dati provenienti da basi di dati.

Introduzione
Nel mese precedente abbiamo introdotto il componente swing JTable, un potente strumento per la visualizzazione dei dati. Questo mese prenderemo spunto da una piccola applicazione realizzata "ad hoc", che usa JTable per visualizzare dati provenienti da tabelle e query di database.

 

L'applicazione "TableOnJava"
Questa piccola applicazione di esempio mostra come si possa, e come sia semplice farlo, visualizzare tabelle o query di database attraverso il componente swing JTable.
Proseguendo logicamente con quanto detto la volta scorsa si è usato un modello che si occupa di caricare i dati nella JTable, e per il momento non ci è preoccupati di come questi dati vengono visualizzati.
Il risultato è una serie di pannelli che contengono oggetti JTable che visualizzano, così come viene prelevato dalla query SQL, i dati relativi alle tre tabelle ed alla query presenti nella base dati:


Figura 1 - la base dati

La base dati di partenza è abbastanza semplice e generica. La situazione ricreata è molto simile ad una semplice gestione di un videonoleggio in cui ogni persona riceve dal gestore un abbonamento (fisicamente una tessera), ricarica ogni volta che è esaurito il suo saldo, e quest'ultimo viene decrementato ogni qualvolta il cliente preleva un film a noleggio.
Le tabelle usate per descrivere questa situazione sono:


Figura 2

Quello che vogliamo mostrare di questo database è, oltre al contenuto delle tre tabelle Anagrafica, Ricariche e Prelievi, anche il saldo di ogni abbonamento, ovvero il totale delle ricariche, il totale dei prelievi e la loro differenza. Per far questo utilizziamo la query:

SELECT A.CodiceAbbonamento, (
Select Sum(ricariche.Importo) from ricariche where ricariche.CodiceAbbonamento = A.CodiceAbbonamento ) AS Ricariche, (
Select Sum(prelievi.Importo) from prelievi where prelievi.CodiceAbbonamento = A.CodiceAbbonamento ) AS Prelievi, ([Ricariche]-[Prelievi]) AS Totale
FROM anagrafica AS A;

Adesso siamo pronti per scrivere il codice di TableOnJava.


Accesso al database
Per accedere al database di esempio (realizzato con MSAccess 2000) viene utilizzato il bridge jdbc/odbc fornito dalla Sun. Sicuramente l'uso di un driver nativo per il database con cui si deve sviluppare la propria applicazione rende le operazioni di accesso al database molto più performanti.
La classe SimpleConnection è una classe molto semplice, priva di controlli approfonditi e di metodi complessi, che sarebbero, per questo esempio, superflui.
Consente comunque di gestire una connessione statica, verso un DSN passato come parametro in ingresso al metodo openConnection:

private static Connection cn; // La connessione al DB private static Statement stm; // statement usato per l'esecuzione di query

I cinque metodi statici contenuti consentono di:

Aprire la connessione al database:
public static void openConnection(String aDSN) { …   Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
  cn = DriverManager.getConnection("jdbc:odbc:" + aDSN,"","");
  stm = cn.createStatement();
  …
}

Chiudere la connessione al database:

public static void closeConnection() {
  …
  stm = null;
  cn.close();
  …
}

Eseguire una query SQl di selezione:

public static ResultSet execute(String anSQLString) {
  …
  rs = stm.executeQuery(anSQLString);
  …
  return rs;
  …
}

 

Ottenere il numero di record presenti in una tabella:

public static int getRecordCount(String aTableName) {
  …
  query = "SELECT COUNT(*) FROM " + aTableName;
  rs = stm.executeQuery(query);
  rs.next();
  result = rs.getInt(1);
  … return result; …
}

Ottenere la lista dei nomi delle colonne di una determinata tabella:

public static String[] getColumnsName(String aTableName){
  String[] columnsName = null;
  …

  ResultSet rs = stm.executeQuery(
               "SELECT * FROM " + aTableName);
  ResultSetMetaData rsmd = rs.getMetaData();

  int columnsNumber = rsmd.getColumnCount();
  columnsName = new String[columnsNumber];

  for (int i=0; i< columnsNumber; i++){
    columnsName[i] = rsmd.getColumnName(i+1);
  }
 …
}


Il modello
Come abbiamo detto l'altra volta, non è la JTable ad andare a cercare i dati da caricare, ma tutta questa parte è delegata ad un modello. Il GenericTableModel (da non confondere con il DefaultTableModel fornito dalla Sun) quindi si occupa esattamente di questo.
Graficamente questa applicazione può essere rappresentata nel seguente modo.

GenericTableModel estende, come previsto, l'AbstractTableModel, e ne ridefinisce i metodi astratti e quelli necessari per la corretta visualizzazione.
La base dati in cui vengono memorizzati i dati della tabella è la seguente:

// Array di stringhe contente i nome delle colonne
String[] columnsName;

// Matrice contenente i dati della tabella
String[][] cells;

A questo punto i metodi è abbastanza semplice scrivere i metodi:

public int getRowCount() {
return cells.length;
}

public int getColumnCount() {
return columnsName.length;
}

public Object getValueAt(int r, int c)
{
return (String)cells[r][c];
}


public String getColumnName(int c)
{
return columnsName[c];
}


Il costruttore del modello riceve in ingresso il nome della tabella o query da caricare ed inizializza cells e columnsName:


public GenericTableModel(String aTableName){
  int rowCount; // Il numero di recod presenti nella tabella
  String query; // La query creata per accedere al database

  // Apre la connesione al database
  SimpleConnection.openConnection("tableonjava");
  // Imposta la query
  query = "SELECT * FROM " + aTableName;

  try{
    // Recupero il numero di record nella tabella
    rowCount = SimpleConnection.getRecordCount(aTableName);
    // Recupero le intestazioni delle colonne
    columnsName = SimpleConnection.getColumnsName(aTableName);
    // Creo la matrice dei dati
    cells = new String[rowCount][columnsName.length];

    // eseguo la query
    ResultSet rs = SimpleConnection.execute(query);

    int counter = 0; // Contatore usato per l'iterazione su cells

    // questo while viene caricata, riga per riga, la matrice
    // dei dati
    while (rs.next()){
      for (int i=0; i <columnsName.length; i++)
        cells[counter][i] = rs.getString(i+1);
      counter++;
    }
  }
  catch(Exception ex){
    System.out.println(ex);
  }
  finally{
    // In ogni caso chiude la connesione al database
    SimpleConnection.closeConnection();
  }
}

 

Conclusioni
Ora siamo in grado di crearci un modello che recupera i dati da una database, cosa molto importante, dato che il naturale scopo delle JTable è appunto quello di visualizzare dati organizzati secondo un modello relazionale.
Si può fare molto di più, ed una volta che abbiamo i dati possiamo decidere come visualizzarli, ovvero il loro particolare layout (si pensi alla colonna dei totali evidenziata in rosso, oppure, all'interno della stessa colonna ai valori positivi in blu ed a quelli negativi in rosso). Oppure prendere in considerazione l'ordinamento di colonne, considerando, a seconda del tipo di dati presente in una colonna un tipo di ordinamento diverso.

 
MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it