io sono neofita, te lo premetto....

non ho capito che cosa fa il tuo odice, in particolare come dovrebbero funzionare i vector rows e columns e i relativi add.... che usi.

quello che ti posso dire è che io faccio in un altro modo. Probabilmente è scritto tipo "Io Tarzan tu Cita" :-) però funziona.
Ti copio qui un pezzetto di una mia applicazione che appunto fa quanto cerchi. Ovviamente ha le mie variabili e i miei campi ;-)

codice:
//questa parte va in un try-catch che gestisca le eccezioni sql
jTable1.setModel(new javax.swing.table.DefaultTableModel(        
                    getTabella(rs),
                    new String [] {
                        "ID_persona","Nome","Cognome","Data_nascita","Luogo_Nascita","C.F.", "Sesso", "Cittadinanza"
                    }
                ));

//in new String[] coem vedi ci sono i titoli delle colonne
poi getTabella è questo:
codice:
private Object[][] getTabella (ResultSet rs) throws SQLException {
                nessunRisultato.setVisible(false);
                int count = 0;
                while(rs.next()) {
                    count++;
                }        
                Object[][] ob = new Object[count][];
                if (count < 1) {
                    nessunRisultato.setVisible(true); /* questa è una label che dice che non ci sono righe che soddisfano le condizione date per la query */

                    return ob;
                    }
                else {
                    rs.beforeFirst(); //riporto il puntatore nella posizione di partenza
                    int i = 0;
                        while(rs.next()) {
                            ob[i] = new Object[8];
                            ob[i][0] = rs.getString(1);
                            ob[i][1] = rs.getString(2);
                            ob[i][2] = rs.getString(3);
                            ob[i][3] = rs.getString(5);
                            ob[i][4] = rs.getString(6);
                            ob[i][5] = rs.getString(7);
                            ob[i][6] = rs.getString(8);
                            ob[i][7] = rs.getString(9);
                            i++;
                        }
                    return ob;
                }
            }


In poche parole lui ti conta quanto è lungo il resultSet (che è il risultato della query) e prepara una tabella che abbia tante righe quante le righe restituite dal resultset e tante colonne quante sono i campi che hai chiesto di visualizzare (e che costruisci uno per uno, come vedi). Quella sfilza di rs.getString naturalmente la puoi usare in modo diverso, cioè se i tuoi campi non sono String ma di altro tipo puoi usare metodi diversi oppure fare il casting nel tipo che ti sta più comodo, i nmodo da avere nelle variabili "cella" della tua tabella i tipo che ti serve. Per tipo intendo chiaramente String, int, Date e così via...