Excepciones en Hibernate 3.x

Saturday March 25th 2006, 1:15 pm
Filed under: Hibernate, Java

Una de las cosas que se criticaba de Hibernate 2 era que sus excepciones (HibernateException para ser específico) eran checked, y por lo tanto no habia mas remedio que catchearlas en todos los métodos que las tiraran, o bien tirarlas explícitamente. Spring presentó una solución a esta situación, ya que si uno usaba el HibernateTemplate provisto con Spring, HibernateException era reemplazada excepciones unchecked, y por lo tanto no era necesario catchearlas en todos lados. Mas aún, HibernateException era reemplazada por toda una jerarquía de excepciones, que permitian tener un conocimiento mas acertado del error ocurrido en la base de datos. Encima de todo, esta jerarquía era la misma para cualquier BD, y para cualquier método de acceso a BD soportado por Spring, ya sea Hibernate, JDBC, JDO, iBatis o TopLink. Asi teníamos por ejemplo DataIntegrityViolationException (cuando intentamos insertar o updatear un registro que viola alguna constraint), BadSqlGrammarException (cuando tratamos de ejecutar algún SQL inválido), etc.

Esto a mi entender tenia dos beneficios: por un lado, no es necesario catchear ni tirar las excepciones en la capa de acesso a datos, se puede hacer en una capa mas alta, y por otro, puedo tener una idea mas acertada de lo que realmente pasó en la BD, todo esto independientemente de la BD o el mecanismo de acceso a la misma que este usando, y sin crear una dependencia a ésta.

Con Hibernate 3, la cosa cambió un poco.  Si bien es perfectamente posible seguir usando el HibernateTemplate de la misma manera, Hibernate 3 ofrece por sí mismo algunas cosas mas.

Para empezar, HibernateException es ahora unchecked y por lo tanto ya no es necesario catcharla en la capa de acceso a datos. Esto que a priori puede parecer una gran ventaja, quizá no lo sea tanto, ya que si dejamos que HibernateException suba por todas las capas y la catcheamos quizá cerca de la vista, estamos introduciendo una dependencia con Hibernate en todas las capas. Puede que esto no nos importe en algún caso, pero si queremos que nuestra aplicación tenga todas sus capas bien diferenciadas e independientes como para que en el futuro podamos reemplazar alguna sin mayores problemas, esto es incorrecto.

Por otro lado, Hibernate 3 agregó también una jerarquía de excepciones que dependen de HibernateException. Tenemos por ejemplo, ConstraintViolationException (cuando intentamos insertar o updatear un registro que viola alguna constraint), JDBCConnectionException (si perdimos la conexión a la BD), etc

De esta manera, es posible manejar las excepciones de una manera similar a Spring.  Otra novedad de Hibernate 3, pero que escapa un poco al alcance de este artículo, es SessionFactory.getCurrentSession(), que permite mantener una session abierta a lo largo de la aplicación y usarla cuando la necesitemos, sin necesidad de abrirla y cerrarla manualmente cada vez.

Todo esto implica que con Hibernate 3 es posible tener nuestros DAOs como simples POJOs, sin necesariemente tener que usar Spring. Por supuesto que todavia estamos dejamos de lado algunas ventajas, como por ejemplo manejo de transacciones en forma declarativa, pero es una alternativa.

Por ejemplo, en mi proyecto actual no usamos Spring (no porque yo no quiera ni porque no lo considere bueno, sino por otras razones que ya comentaré en otro post), y dentro de mis Hibernate DAOs, el approach que use para manejar las excepciones es algo así:

public void saveCustomers(Customer[] customers) throws PersistenceException {
        String msg = null;

        try {
            msg = getCustomerNames();
            saveOrUpdate(openSession(), customers);
        } catch (ConstraintViolationException cve) {
            throw new PersistenceException("persister.customer.save.constraint.exception", new Object[] {msg, cve.getConstraintName()}, cve);
        } catch (HibernateException e) {
            throw new PersistenceException("persister.customer.save.failed", new Object[] {msg}, e);
        }
    }

Yo particularmente prefiero que las excepciones de Hibernate no salgan de la implementación de los DAOs y por lo tanto, las reemplazo con PersistenceException (que forma parte de una jerarquia de excepciones propia de mi aplicación), pero les pongo el mensaje que yo quiero, de manera de informarle el error al usuario de la manera mas amigable y acertada posible. Noten que ConstraintViolationException provee un método getConstraintName(), que devuelve el nombre de la constraint violada. Si en la base de datos uno se toma el trabajo de ponerle nombres lo suficientemente descriptivos a las constraint, el usuario va a terminar viendo una descripción bastante correcta de cual fue el problema, y hasta quizá pueda resolverlo por si mismo, o en el peor de los casos un administrador no va a tener que andar buscando en enormes logs cual fue el problema para poder resolverlo. 

En una capa superior de mi aplicación:

    private String saveCustomer(StrutsCustomerForm form) {
        Customer customer = null;
        try {
            validateCustomerForm(form);
            customer = toCustomer(form);
            customerDAO.saveCustomers(new Customer[] {customer});       
        } catch (Exception e) {
            addMessageException(e);
            saveMessages();
            return ERROR;
        }
        return SUCCESS;
    }

Aquí simplemente cacheo Exception, porque sé que todas las otras capas de mi aplicación ya reemplazaron las excepciones respectivas por alguna de mi jerarquia propia y le pusieron un mensaje (que dicho sea de paso, es un key de un resource bundle, ya que mi jerarquia de excepciones sobreescribe getLocalizedMessage() para soportar i18n), que es lo que termina en la pantalla del usuario.


| show comment »

JSTL y EL en Tomcat 5.x

Wednesday March 15th 2006, 3:45 pm
Filed under: Java

Este es un problema que aparentemente le ocurre a bastante gente, incluyéndome.

La mayoría de los artículos sobre JSTL dicen que hay que poner el siguiente tag para poder usar los tags de JSTL con expresiones EL:

< %@ taglib uri="http://java.sun.com/jstl/core" prefix="c"%>

Pero al escribir en el JSP algo asi como:

<c:if test="${param.isPopup}" >

nos encontramos conque el JSP no se muestra, y aparece una linda excepción:

According to TLD or attribute directive in tag file, attribute test does not accept any expressions

Lo qué? No era que se podian usar las expresiones EL? Con esto uno se puede romper la cabeza varias horas, hasta que en algún lugar alguien sugiere usar:

< %@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c"%>

Entonces todo funciona bien, pero cómo puede ser si ese uri es para run time expressions, las antiguas y limitadas expresiones de JSP?

La respuesta a esto yace en Tomcat. La version 5.x soporta JSP 2.0, que incluye soporte para expresiones EL. Entonces, al usar la version EL de los tags JSTL, las expresiones se intentan evaluar dos veces: una por Tomcat, y otra por los tags. La versión RT no intenta evaluar la expresión EL que ya evaluó Tomcat, y por lo tanto funciona.

La solución correcta? Usar el uri de la versión 1.1 de JSTL, que es este:

< %@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

Corolario:

No olvidemos que como Tomcat evalúa las expresiones EL directamente, éstas se pueden escribir en el código HTML sin necesidad de usar el tag <c:out>, por ejemplo:

<h1>${form.pageTitle}</h1>


| show comments »

Es bueno que sea fácil?

Tuesday March 07th 2006, 11:55 pm
Filed under: General

A veces me pregunto si es mejor que algunas cosas sean demasiado fáciles… Por ejemplo, simplemente por nombrar algo, PHP. Es muy simple de instalar, y es muy fácil hacer una página dinámica.

Por qué digo esto? Porque esa misma facilidad hace que cualquiera se ponga a hacer sistemas. No digo una paginita o dos que muestre mensajitos o algo asi, sino sistemas que manejan la operación de una empresa.

Donde estoy trabajando ahora, tienen un sistema en PHP para registrar y manejar los pagos a los ganadores de los concursos. Nosotros estamos haciendo un sistema que eventualmente va a terminar reemplazando al PHP, pero por el momento sigue andando, y hay que mantenerlo. Obviamente, yo trato de tocarlo lo menos posible, pero de vez en cuando hay que hacerle algo. Por ejemplo, recientemente hubo que modificarlo para que además de permitir varios teléfonos para cada cliente, también permita varios clientes para un mismo teléfono. No es una modificación complicada, pero sin embargo, el programador (que no conozco ni sé quien es), decidió que no era necesario poner un solo punto donde se graben los teléfonos, sino que el mismo código esta multiplicado y desparramado por toda la aplicación. O sea que hubo que buscar por todos lados, probar y re probar las diferentes opciones para llegar a lo mismo. Esto sin tener en cuenta que cada archivo .php es una mezcolanza de HTML, JavaScript, CSS, PHP y SQL.

Otro ejemplo, es que este programador “olvidó” ponerle un primary key a una tabla, y ahora descubrí que dicha tabla se llenó de ids duplicados, lo que hace que algunos procesos se ejecuten con errores. Evidentemente esta persona no debería estar haciendo sistemas, quizá el mundo se perdió un excelente médico, un gran músico o simplemente un buen pintor de brocha gorda, pero decididamente no ganó un buen programador.
Entonces, es bueno o no es bueno que sea tan fácil ponerse a hacer un sistema?
Es evidente que si la barrera de entrada es muy baja, cualquier se mete. Y después es cuando vienen los problemas, sistemas difíciles de mantener y de comportamiento impredecible.

Y que conste que no es una queja en particular contra PHP, al que le tengo mucho respeto, hace varios años hice algunas cosas bastante interesantes en PHP, pero cuando busqué mejorar la calidad de lo que hacia, terminé haciendo lo mismo que haria en Java. Entonces directamente me pareció mejor usar Java.


| show comments »