Foro sobre Java SE > de resulset a un defaultTableModel sin usar el ciclo
El método add(<Object[]> del DefaultTableModel es especialmente lento, sobre todo en lazos cerrados como éste.
1.- DefaultTableModel usa internamente Vector, que aunque sea thread-safe, es más lento que otras colecciones
2.- add(<Object[]> convierte internamente el array a Vector
3.- Añade el elemento al Vector interno.
4.- Justifica el Vector, por si acaso DefaultTableModel se usa para crear subclases, en vez de AbstractTableModel
5.- Dispara un fireTableRowsInserted
¡Es mucho "trabajo" para cada fila que se añade! :D
Si quieres usar el DefaultTableModel en vez de crear tu propio modelo, quizás lo más recomendable sería:
1.- Construir los Vectores en tu lazo, con todas las filas
2.- Crear el DefaultTableModel con esos Vectores, fuera del lazo.
Puedes echarle un ojo a los constructores de DefaultTableModel, para ver que opciones son las que más te pueden interesar.
http://docs.oracle.com/javase/7/docs/api/javax/swing/table/DefaultTableModel.html
choces, no entendí los dos ultimo puntos que me propones
lo entendi asi, apile todas las filas en el vector y luego despues del ciclo inicialice el modelo con dichas filas
Sí, la idea consiste en crear la estructura de datos a partir del ResulSet, y a continuación crear el modelo de una sola vez con ella, y declarar el modelo en la tabla.
El "problema" de DefaultTableModel es que funciona internamente con un Vector de Vectores, y que si se declara con arrays bidimensionales, debe convertirlos internamente a su estructura interna.
Esa estructura interna es un Vector en el que cada posición se corresponde con una fila de la tabla, y cada elemento de la posición es un Vector con el contenido de las columnas de la fila correspondiente.
La estructura de Java que mejor se asemeja a una tabla de una base de datos es un Map.
Por eso se suele convertir el ResultSet a un Map, y crear un modelo específico que use un Map. Con un DefaultTableModel no es posible hacerlo así.
Échale un ojo a estas dos entradas de este mismo foro:
http://www.javahispano.org/java-se/post/1879373
http://www.javahispano.org/java-se/post/1868448
Guenas.
Quizá una solución mas efectiva aunque bastante mas laboriosa sería obtener un CachedRowSet (creo recordar que se llama asi) y contruir un TableModel que lo utilice directamente en lugar de convertir a vectores o arrays y usar el DefaultTableModel.
Salut,
Paposo
El CachedRowSet puede ser útil cuando se realizan siempre las mismas consultas, puesto que al almacenarlas en memoria se evita la conexión y acceso a la base de datos. Tiene el inconveniente de tener que mantener la coherencia entre el CachedRowSet y las tablas, lo que sin duda complica el código de todas las operaciones de update.
Puesto que todas (creo) las bases de datos implementan sus propias cachés de consultas, la utilidad del CachedRowSet es cuando menos discutible.
Sin embargo, creo que el problema principal (y habitual) consiste en mezclar la consulta, con la creación y declaración del modelo en la JTable.
Si por cada fila que se recibe del ResultSet se debe modificar la JTable (el añadido de cada fila al modelo dispara un listener que actualiza la vista de la JTable en el UI), nos encontramos con un serio problema de rendimiento:
1.- Las actualizaciones de la vista se deben realizar en el EDT
2.- Luego, el código del ResultSet también debe ejecutarse en el EDT
3.- Luego, el UI (no solo el de la JTable, sino todo el interface gráfico) depende de la velocidad con que se ejecute el código del ResultSet.
De ahí las quejas habituales sobre "Swing es lento" ;)
Lo que, en mi opinión es mejor:
1.- Construir el modelo en una tarea paralela al EDT, a partir del código del ResultSet
2.- Declarar el modelo en la JTable, dentro del EDT, una vez esté completado el modelo.
Otra cuestión es qué tipo de modelo utilizar. Personalmente prefiero crear uno a medida, como por ejemplo éste que uso para las JTable:
public class JPTableModel<T> implements TableModel, Serializable
private static final long serialVersionUID = 8521178060564486490L;
private transient final String[] tableHeader;
private transient final Map<Integer, List<T>> tableData;
private transient final int rows, columns, headerID;
private EventListenerList listenerList;
public JPTableModel(final String[] header, final Map<Integer, List<T>> tableData) {
this.tableHeader = Arrays.copyOf(header, header.length);
this.tableData = new HashMap<>(tableData);
this.rows = this.tableData.size();
this.columns = this.tableHeader.length;
this.headerID = JPCoreServices.getHashing().hash(this.tableHeader);
}
// aquí va el código que implementa el TableModel
}
Hola, gracias por toda la informacion, estoy digiriendo lo que me escribieron y poniendo en practica para ver si aumento el rendimiento de mi consultas... de nuevo gracias
Guenas.
CachedRowSet es extremadamente útil y mucho mas simple que métodos manuales para actualizaciones. Mírate la documentación del mismo y lo veras claro. Si necesitas una rabiosa actualización, algo que en ningún momento has comentado, necesitas algo como un RowSet equivalente a un cursor y actuar físicamente, pero eso es muy dependiente de la BD subyacente dado que no todas soportan igual JDBC. Al igual que choces concuerda conmigo en la creación de un TableModel a medida, concuerdo con el en lo mismo. Jajajajaja. Lo de no ejecutar los procesos largos dentro del EDT lamento no haberlo indicado porque lo daba por asumido a cualquiera que trabaje con swing (o cualquier otro entorno gráfico, dado que absolutamente todos, directa o indirectamente hacen exactamente lo mismo) tiene mas que claro como funcionan los GUI y que para que sean útiles es indispensable separar la ejecución del interfaz de la actualización de datos a menos que esta ultima sea mas rápida de lo que el usuario pueda percibir.
Tal como dice choces, swing no es lento en absoluto. El problema es que la gente no sabe utilizarlo. El tema de los accesos a la BD es mas peludo y depende mucho de los requerimientos que tengas, la estrategia empleada y la implementación del JDBC correspondiente.
Un saludo,
Paposo
Salut,
Paposo
Saludos a todos.
Siempre que deseo pasar los datos de un resulset a un defaultTableModel uso el procedimiento clasico siguiente:
while (rs.next()){
Object [] fila = new Object[numeroColumnas];
for (int i=0;i<numeroColumnas;i++)
fila[i] = rs.getObject(i+1);
defaultTableModel.addRow (fila);
}
Cuando la consulta genera muchas tuplas, el llenado de la tabla demora mucho, usando programas como pgadmin o sqliteman me doy cuanta que las consultas select por muchas tuplas que tenga la informacion se muestra casi que instantaneamente,
¿Alguien sabe como pasar la informacion de resulset a un defaultTableModel sin usar el ciclo?