Introduzione L'applicazione "TableOnJava" Accesso al database Il modello Conclusioni
Continua la serie di articoli sul potente componente Swing Jtable. Questo mese la visualizzazione di dati provenienti da basi di dati. 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. 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. 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); } … } 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(); } } 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.
Continua la serie di articoli sul potente componente Swing Jtable. Questo mese la visualizzazione di dati provenienti da basi di dati.
IntroduzioneNel 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 databasePer 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();
…
}
int columnsNumber = rsmd.getColumnCount();
columnsName = new String[columnsNumber];
for(int i = 0; i < columnsNumber; i++) {
columnsName[i] = rsmd.getColumnName(i + 1);
}
…
}
Il modelloCome 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 tabellarowCount =
// SimpleConnection.getRecordCount(aTableName);
// Recupero le intestazioni delle colonnecolumnsName =
// SimpleConnection.getColumnsName(aTableName);
// Creo la matrice dei daticells = new String[rowCount][columnsName.length];
// eseguo la queryResultSet rs = SimpleConnection.execute(query);
int counter = 0;
// Contatore usato per l'iterazione su cells
// questo
while viene caricata, riga per riga, la matrice
// dei datiwhile (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
// databaseSimpleConnection.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