PDA

View Full Version : [JAVA] "Ascoltare" modifiche alle righe di una JTable


Manugal
23-09-2009, 12:15
Ciao a tutti!

Ho un problema che non riesco a risolvere. Ho tre tabelle (memorizzate su un DB SQL) che l'utente può modificare a piacimento. Ora vorrei che quando l'utente, per una determinata tabella, preme il tasto "Inserisci", o "Cancella" (una o più righe), oppure quando modifica una determinata riga, il programma salvi i cambiamenti fatti sul DB. Come faccio a capire quali righe l'utente modifica, cancella o aggiunge man mano e poi registrare questi cambiamenti?

Ho provato a vedere i metodi fireTable*() di TableModel ma non saprei come lanciarli.

Grazie, spero di essermi spiegato.

Posto il codice della funzione che crea le tabelle insieme a tutta l'interfaccia grafica (da completare):



final JTable lavTable = db.genTableLav(nome);
/* Questa è da completare */
lavTable.getModel().addTableModelListener(new TableModelListener(){
public void tableChanged(TableModelEvent e) {
int firstRow = e.getFirstRow();
int lastRow = e.getLastRow();
int index = e.getColumn();
Object[][] changedData;
DefaultTableModel model = (DefaultTableModel) e.getSource();

/* Per quanto riguarda insert e delete c'è TableModelEvent.INSERT e TableModelEvent.DELETE, ma per "ascoltare" una riga intera non c'è niente c'è solo TableModelEvent.UPDATE che ascolta i cambiamente di una cella */
switch(e.getType()){
}
}

/* Per inserire una riga faccio così */
insert.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
DefaultTableModel model = (DefaultTableModel) prevTable.getModel();
model.addRow(new Object[2]); /* Inserisco una riga vuota. Non lancio alcun fireTable*() perché prima la riga la inserisco vuota e poi la modifico, per questo mi serve qualcosa che ascolti i cambiamenti di una riga per intero */
prevTable.setModel(model);
prevTable.repaint();
}
});

PGI-Bis
23-09-2009, 12:41
La soluzione è quella che hai già. Il modello della tabella spara un TableModelEvent sia per mutazioni nella struttura - righe o colonne che vanno o vengono - sia per mutazioni nel contenuto - l'utente cambia il valore della cella (x,y).

Manugal
23-09-2009, 12:47
D'accordo e fino a qui ci siamo. Però l'utente potrebbe anche inserire una nuova riga e inserire quindi il contenuto che lui vuole in ogni cella di quella riga. Se TableModelEvent.UPDATE intercetta i cambiamenti in una sola cella, come faccio a dirgli di intercettare i cambiamenti in una riga intera?

PGI-Bis
23-09-2009, 12:56
Lo fa già. Quando l'utente inserisce una riga il modello notifica agli ascoltatori l'inserimento: il tipo dell'evento sarà INSERT e gli indici di riga ti dicono dove si trova la nuova riga.

Se la riga contiene dei valori di default, puoi prendere ed esaminare le singole celle per capire cosa andare a salvare nel database.

Quando l'utente cambia uno dei valori di quella riga il modello spara un altro TableModelEvent, stavolta un UPDATE: qui sono gli indici di riga e l'indice della colonna a dirti quale cella è cambiata.

Attraverso TableModelListener sei già in grado di recuperare tutte ma proprio tutte le mutazioni che coinvolgono il modello della tabella.

Manugal
23-09-2009, 12:58
Ah ok d'accordo. Ora mi è più chiara la situazione. Grazie. :)

Manugal
23-09-2009, 14:10
Un'ultima domanda. Ma se l'utente mettiamo il caso preme tre volte inserisci (magari perché vuole inserire più righe alla volta) come faccio a catturare questo con il TableModeListener? Io ho scritto una cosa simile ma ovviamente non funziona perché ogni volta che inserisce una nuova riga firstRow e lastRow sono sempre uguali.


lavTable.getModel().addTableModelListener(new TableModelListener(){
public void tableChanged(TableModelEvent e) {
int first = e.getFirstRow();
int lastRow = e.getLastRow();
int column = e.getColumn();
Object[][] changedData = new Object[lastRow-first][3];
DefaultTableModel model = (DefaultTableModel) e.getSource();
switch(e.getType()){
case TableModelEvent.UPDATE:
for(int i=first; i<=lastRow; ++i){
changedData[i][column]= model.getValueAt(i,column);
System.out.println("changedData[" + i + "][" + column + "]: " + changedData[i][column]);
}
break;
case TableModelEvent.DELETE:
break;
}
}
});

Manugal
28-09-2009, 20:39
Rieccomi di nuovo! :D

Sono riuscito a gestire la situazione dei cambiamenti fatti nelle righe delle tabelle scrivendo in un array tutte le modifiche che ho fatto e in quali tabelle. E' un array di String in cui inserisco, per ogni elemento:

"<tipo operazione> <tabella> <indice di riga> <ID di riga nel DB>"

dove <tipo operazione> può essere Add,Upd o Del.

Ora sorge un problema. L'utente quando inserisce una riga tramite il tasto Inserisci, in realtà inserisce una riga vuota che lui poi deve popolare. Supponiamo che inserisca la prima riga nella tabella 0 (ometto l'ID di riga del DB). In quel caso il TableModelListener associato alla tabella intercetta tale cambiamento e aggiunge nell'array la stringa "Add 0 0 ID"; ora l'utente vorrà popolare la riga appena aggiunta e quando finisce di popolarla viene intercettato il cambiamento e viene aggiunta la stringa "Upd 0 0 ID".

In questo modo nasce il problema che quando vado a salvare i cambiamenti fatti, nel DB inserisce prima la riga vuota e poi aggiorna la riga stessa (facendo 2 query invece che magari 1 sola). Come posso risolvere questo inconveniente?

Spero di essere stato chiaro.

PGI-Bis
28-09-2009, 20:46
Scarti l'evento INSERT se la riga inserita è vuota. Per verificare se la riga è vuota controlli il contenuto di ogni cella di quella riga. Il numero di riga lo conosci tramite il TableModelEvent.

Manugal
28-09-2009, 21:18
Ok, grazie. Ci avevo pensato infatti ma poi sorgeva il problema di distinguere tra una nuova riga inserita e una riga esistente solamente aggiornata. Ora che ci penso potrei aggiungere un'ulteriore informazione nell'array, ad esempio scrivo N se la riga è nuova e E se è una riga esistente che ho modificato.

PGI-Bis
28-09-2009, 21:23
L'aggiornamento lo ricevi come UPDATE. Se la riga è nuova allora l'evento della tabella sarà di tipo INSERT. Ricevi due notifiche diverse.

Manugal
28-09-2009, 21:27
Si quello lo so. Io intendevo proprio che l'utente una volta che inserisce una riga nuova (evento INSERT), dopo che è stata inserita viene popolata con i dati (evento UPDATE). Il problema è che in questo caso è come se trattassi le due cose come un evento UPDATE (scarto l'evento INSERT) e quindi c'è il problema di distinguere se sto aggiornando una riga nuova o una riga già esistente.

banryu79
29-09-2009, 08:27
Si quello lo so. Io intendevo proprio che l'utente una volta che inserisce una riga nuova (evento INSERT), dopo che è stata inserita viene popolata con i dati (evento UPDATE). Il problema è che in questo caso è come se trattassi le due cose come un evento UPDATE (scarto l'evento INSERT) e quindi c'è il problema di distinguere se sto aggiornando una riga nuova o una riga già esistente.
Prima di utilizzare le info raccolte nell'array per eseguire le query sul db sottostante (se ho capito bene l'intera faccenda) puoi processare l'array stesso per arrivare a produrne uno isomorfo che invece di conservare le coppie di elementi INSERT - UPDATE le condensa in un singolo elemento di tipo INSERT con i valori dei campi uguali a quello dell'UPDATE relativo. Rispetto all'array originale è l'elemento corrispondente all'indice dell'UPDATE che viene eliminato e quello corrispondente all'indice dell'INSERT che viene conservato.
A questo punto hai l'array pronto per produrre le query.

Manugal
29-09-2009, 22:02
Grazie, ma ora sto provando a crearmi un mio TableModel e vedere se così riesco a risolvere. Altrimenti proverò la tua soluzione.