17 octubre 2005

Me han fastidiado los de oracle

En el trabajo tenemos y tratamos de mejorar un conjunto de clases java para facilitar los programas que manejan base de datos. Básicamente la idea que tenemos es la siguiente:

Para cada posible tipo de campo de la base de datos tenemos un editor, es decir tenemos varios editores capaces de manejar los datos que nos devuelve un resultSet.getObject(): String, BigDecimal, Double, etc, etc. Todos esos editores implementan una interface común de tomaDatos() y dameDatos(). Estos editores se pueden configurar con las restricciones de los campos en la base de datos (número de cifras, de decimales, es obligatorio, es editable, etc) que se pueden leer automáticamente con los metadatos desde java.

Tenemos una clase que lee un fichero de configuración. Ese fichero contiene los nombres de las tablas de base de datos y el tipo de editor que debemos usar para ese campo, con sus restricciones. Ese fichero se genera automáticamente con un programita java que nos hemos hecho y que consulta los metadatos de la base de datos. Hemos optado por este fichero de configuración por no tener que ir leyendo en tiempo de ejecución los metadatos de la base de datos.

Tenemos también un panel al que se le pasan nombres de campos de la base de datos. El panel consulta el fichero de configuracion (que también se le pasa), instancia el editor adecuado para ese campo, lo configura con sus restricciones y lo coloca en el panel. Con esta clase creamos todos los formularios de forma casi automática.

El panel tienen dos metodos tomaDatos() y dameDatos() que reciben y dan un Hashtable. La clave del Hashtable es el nombre del campo de la base de datos y el valor es el valor que nos devuelve el ResultSet. De esta forma, rellenando el formulario y llamando al dameDatos(), obtenemos un Hashtable con todos los campos del formulario rellenos. El tomaDatos() lo que hace es repartir los valores del Hashtable a los editores individuales que hay dentro del panel.

Por supuesto, tenemos un TableModel que en cada fila admite uno de estos Hashtables y un JTable al que dándole los nombres de los campos que queremos que se vean, añade las columnas correspondientes.

Finalmente, tenemos una clase a la que dándole un String con un select de oracle, es capaz de realizar la consulta y construirnos los Hashtables correspondientes. Esta clase, si le damos cualquier sentencia SQL con nombres de campos entre # y un Hashtable, reemplaza los nombres de campos entre # por los valores del Hashtable. De esta forma se construyen los insert, delete, update e incluso los where de los select de forma más o menos automática. Por ejemplo, si hacemos "select * from tabla where campo=#campo#" y le pasamos un Hashtable con una clave "campo" y con valor Integer=3, esta clase reemplaza el #campo# por un 3. El select quedaría asi "select * from tabla where campo=3".

Con todo esto, para hacer un programa que trabaje con base de datos, creamos un fichero de configuración con nuestro programa independiente. Escribimos los select, insert,update en un fichero de configuracion. Los leemos y lo pasamos a nuestra clase de consultas, que nos devuelve directamente Hashtables. Estos los podemos meter tal cual en una tabla o en un formulario que se construyen solos con un par de líneas de código. De la misma forma, leyendo datos nuevos de un formulario y reemplazando campos entre # en el insert correspondiente, podemos insertar.

¿Cual es el problema?. Para construir el Hashtable con el resultset que obtenemos del select de SQL, necesitamos leer los metadatos del ResultSet. Estos metadatos sólo nos devuelven el nombre del campo, pero no de la tabla ni del esquema al que pertenece dicho campo ( ver última línea en http://www.oracle.com/technology/sample_code/tech/java/codesnippet/jdbc/OracleResultSetMetaData.html). Esto nos plantea el problema que si el select pide dos campos con el mismo nombre en distintas tablas, a través de los metadatos del ResultSet no podemos distinguir qué columna es qué campo. No queda más remedio que hacer los select con cosas como "select tabla1.nombre as nombre_1, tabla2.nombre as nombre_2 ...", que no deja de ser un incordio, puesto que hay que acordarse de hacerlo y no es algo que nos haga el código automáticamente.

No hay comentarios: