13 de marzo de 2013

Consejos al utilizar JDBC


Evitar consultas SQL SELECT * 
SELECT * FROM... es una forma muy habitual de establecer una consulta en SQL. Sin embargo, muchas veces no es necesario consultar todos los campos. Para cada columna que hay que devolver, el controlador JDBC tiene que hacer el trabajo adicional de enlazar y devolver la fila. Aún en el caso de que la aplicación no llegue a utilizar nunca una columna concreta, el controlador JDBC debe tenerla presente y reservar espacio por si se utiliza. Esta cuestión no supone una actividad general significativa si son pocas las columnas que no se utilizan de las tablas. Sin embargo, si son numerosas las columnas no utilizadas, la actividad general puede llegar a ser significativa. En tal caso, sería mejor listar individualmente las columnas que a la aplicación le interesa consultar, como en este ejemplo:
SELECT COL1, COL2, COL3 FROM...

Utilizar getXXX(int) en vez de getXXX(String)
Utilice los métodos getXXX de ResultSet que toman valores numéricos, en vez de las versiones que toman nombres de columna. Si bien la libertad que supone utilizar nombres de columna en vez de constantes numéricas parece ser una ventaja, la base de datos propiamente dicha solo sabe manejar los índices de las columnas. Por ello, cada vez que se llama a un método getXXX utilizando el nombre de una columna, el controlador JDBC lo debe resolver para que el método se pueda pasar a la base de datos. Normalmente, a los métodos getXXX se les suele llamar dentro de bucles que se pueden ejecutar millones de veces, y ello provocaría rápidamente la acumulación de la pequeña actividad general que supone la resolución de cada uno de los nombres de columna.

Evitar llamadas a getObject para los tipos Java primitivos.
Cuando de la base de datos se obtienen valores de tipos primitivos (que son los tipos int, long, float, etcétera), es mucho más rápido utilizar el método get específico del tipo primitivo (getInt, getLong, getFloat) que utilizar el método getObject. La llamada a getObject realiza el trabajo de obtener el tipo primitivo y luego crea un objeto para devolverlo. Normalmente, esto se hace en los bucles, lo que supone crear millones de objetos de corta vida. Si se emplea getObject para los mandatos de primitivos, existe el inconveniente adicional de que se activa con frecuencia el colector de basura, lo que aún reduce más el rendimiento.

Utilizar PreparedStatement más que Statement
Si escribe una sentencia SQL que se vaya a utilizar más de una vez, el rendimiento será mayor si la sentencia es un objeto PreparedStatement que si es un objeto Statement. Todas las veces que se ejecuta una sentencia, se realiza un proceso en dos pasos: la sentencia se prepara y luego se ejecuta. Si se emplea una sentencia preparada, el paso de preparar la sentencia solo tiene lugar en el momento de construir la sentencia, y no se repite cada vez que se ejecuta la sentencia. Los programadores, aunque saben que el rendimiento de PreparedStatement es mayor que el de Statement, suelen desaprovechar esta ventaja en muchas ocasiones. Debido a la subida de rendimiento que proporcionan las sentencias PreparedStatement, conviene utilizarlas en el diseño de las aplicaciones siempre que sea posible.

Evitar llamadas a DatabaseMetaData
Conviene tener en cuenta que algunas de las llamadas a DatabaseMetaData pueden ser costosas. Concretamente, pueden ser costosos los métodos getBestRowIdentifier(), getCrossReference(), getExportedKeys() y getImportedKeys(). Algunas llamadas a DataBaseMetaData implican complejas condiciones de unión sobre tablas a nivel del sistema. Únicamente se deben emplear cuando se necesita la información que proporcionan, no porque resulte más práctico.

Utilizar el nivel de compromiso correcto para la aplicación
JDBC proporciona varios niveles de compromiso, que determinan cómo se afectan mutuamente múltiples transacciones con respecto al sistema. El valor por omisión es utilizar el nivel de compromiso más bajo. Esto implica que las transacciones pueden ver parte del trabajo de cada una de ellas a través de los límites del compromiso. También implica la posibilidad de que se produzcan ciertas anomalías de base de datos. Algunos programadores aumentan el nivel de compromiso para no tener que preocuparse de si se produce este tipo de anomalías. Tenga en cuenta que los niveles de compromiso más altos implican que la base de datos se cuelgue en bloqueos más bastos. Ello limitará la cantidad de concurrencia que el sistema puede tener, disminuyendo en gran medida el rendimiento de algunas aplicaciones. A menudo, las condiciones de anomalía no se pueden producir debido en primer lugar al diseño de la aplicación. Tómese su tiempo para comprender lo que está tratando de lograr y limite el nivel de aislamiento de las transacciones al mínimo que pueda emplear sin arriesgarse.

Considerar la posibilidad de almacenar datos en Unicode
En Java, todos los datos de tipo carácter con los que se trabaja (objetos String) deben tener el formato Unicode. Por lo tanto, para las tablas cuyos datos no tengan el formato Unicode, se necesitará que el controlador JDBC convierta los datos a ese formato y desde él al ponerlos en la base de datos y al recuperarlos de la base de datos. Si la tabla ya tiene los datos en Unicode, el controlador JDBC no tendrá que convertirlos, por lo que será más rápido colocar en ella los datos de la base de datos. Fíjese, sin embargo, que los datos en Unicode pueden no funcionar con las aplicaciones no Java, ya que estas no saben cómo manejar el formato Unicode. Tenga presente también que el rendimiento no se ve afectado para los datos que no son de tipo carácter, ya que esos datos nunca se tienen que convertir. Aún hay que tener en cuenta otra particularidad, y es que los datos almacenados en Unicode ocupan el doble de espacio que los datos de un solo byte. Sin embargo, si son numerosas las columnas de tipo carácter que se leen muchas veces, puede llegar a ser notable el aumento de rendimiento que supone almacenar los datos en Unicode.

Utilizar procedimientos almacenados
El uso de procedimientos almacenados está soportado en Java. El rendimiento de los procedimientos almacenados puede ser mayor al permitir que el controlador JDBC ejecute SQL estático en vez de SQL dinámico. No cree procedimientos almacenados para cada sentencia SQL individual que ejecute en el programa. No obstante, cuando sea posible, cree un procedimiento almacenado que ejecute un grupo de sentencias SQL.

Utilizar BigInt en vez de Numérico/Decimal
En vez de utilizar campos numéricos o decimales cuya escala sea 0, utilice el tipo de datos BigInt. BigInt se convierte directamente al tipo Java primitivo Long, mientras que los tipos de datos numéricos o decimales se convierten en objetos String o BigDecimal. Como se ha indicado anteriormente es preferible utilizar tipos de datos primitivos que utilizar tipos que requieren la creación de objetos.

Cerrar explícitamente los recursos JDBC cuando ya no se necesitan
Los objetos ResultSet, Statement y Connection se deben cerrar explícitamente por medio de la aplicación cuando ya no se necesitan. Así se hace una limpieza de recursos del modo más eficaz posible, y el rendimiento puede aumentar. Además, los recursos de base de datos que no se cierran de manera explícita pueden provocar fugas de recursos, y los bloqueos de base de datos se pueden prolongar más de lo debido. Ello puede producir anomalías de aplicación o reducir la concurrencia en las aplicaciones.

Utilizar la agrupación de conexiones
La agrupación de conexiones es una estrategia que permite reutilizar los objetos Connection de JDBC por parte de múltiples usuarios, en vez de dejar que cada usuario solicite crear su propio objeto Connection. Cuesta mucho crear objetos Connection. En vez de hacer que cada usuario cree una conexión nueva, conviene que las aplicaciones sensibles al rendimiento compartan una agrupación de conexiones. Ya son muchos los productos (como WebSphere) que proporcionan soporte para la agrupación de conexiones y cuyo uso requiere muy poco esfuerzo adicional por parte del usuario. Si no desea utilizar un producto que tenga soporte para la agrupación de conexiones o si prefiere construir una propia con objeto de controlar mejor su funcionamiento y su ejecución, piense que es relativamente fácil hacerlo.

Considerar la posibilidad de utilizar la agrupación de sentencias PreparedStatement
La agrupación de sentencias funciona de manera muy parecida a la agrupación de conexiones. En vez de poner en la agrupación tan solo las conexiones, en ella se pone un objeto que contenga la conexión y las sentencias PreparedStatement. Luego se recupera ese objeto y se accede a la sentencia concreta que se quiere usar. Esta estrategia puede aumentar drásticamente el rendimiento.

Utilizar SQL eficaz
Dado que JDBC se construye encima de SQL, cualquier técnica que mejore la eficacia de SQL mejorará también la eficacia de JDBC. Por lo tanto, JDBC se beneficiará de las consultas optimizadas, de los índices acertadamente elegidos y de otros aspectos que mejoren el diseño de SQL