Excepciones en Hibernate 3.x
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 { try {
String msg = null;
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 » 1 Comment so far
Leave a comment
Leave a comment
Line and paragraph breaks automatic, e-mail address never displayed, HTML allowed:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>




