2010-04-02 18 views
13

En la referencia de hibernación, se afirma varias veces que¿Cómo romper una sesión de Hibernate?

Todas las excepciones lanzadas por Hibernate son fatales. Esto significa que debe retrotraer la transacción de base de datos y cerrar el Session actual. No puede continuar trabajando con un Session que arrojó una excepción.

Una de nuestras aplicaciones heredadas utiliza una sola sesión para actualizar/insertar muchos registros de archivos en una tabla DB. Cada actualización/inserción de registro se realiza en una transacción separada, que luego se confirma (o se retrotrae en caso de que se haya producido un error). Luego, para el siguiente registro, se abre una nueva transacción, y así sucesivamente. Pero la misma sesión se usa durante todo el proceso, incluso si se atrapó HibernateException en mitad del procesamiento. Estamos utilizando Oracle 9i btw con Hibernate 3.24.sp1 en JBoss 4.2.

Al leer lo anterior en el libro, me di cuenta de que este diseño puede fallar. Así que reformé la aplicación para usar una sesión separada para cada actualización de registro. En una prueba unitaria con una fábrica de simulacros de sesión, puedo verificar que ahora está solicitando una nueva sesión para cada actualización de registro. Hasta aquí todo bien.

Sin embargo, no encontramos forma de reproducir el error de sesión mientras probamos toda la aplicación (¿sería esto una prueba de esfuerzo por cierto, o ...?). Pensamos en cerrar al oyente de la base de datos, pero nos dimos cuenta de que la aplicación mantiene un montón de conexiones abiertas a la base de datos, y el oyente no afectaría esas conexiones. (Esta es una aplicación web, activada una vez por noche por un programador, pero también puede activarse a través del navegador.) Luego intentamos eliminar algunas de esas conexiones en el DB mientras la aplicación estaba procesando las actualizaciones, lo que dio como resultado algunas fallas actualizaciones, pero luego la aplicación felizmente continuó actualizando el resto de los registros. Aparentemente, Hibernate es lo suficientemente inteligente como para reabrir conexiones rotas debajo del capó sin romper toda la sesión.

Por lo tanto, puede que esto no sea un problema crítico, ya que nuestra aplicación parece ser lo suficientemente robusta incluso en su forma original. Sin embargo, el problema sigue molestándome. Me gustaría saber:

  1. ¿En qué circunstancias la sesión de Hibernate realmente quedar inutilizable después de un HibernateException se tiró (Actualización: y cuáles son los síntomas)?
  2. Cómo reproducir esto en una prueba (Actualización: preferiblemente en integración, en lugar de prueba unitaria)?
  3. (¿Qué es el término apropiado para una prueba de este tipo?)

Respuesta

4

¿En qué circunstancias la sesión de Hibernate realmente quedar inutilizable después de un HibernateException fue arrojado?

Pregunta interesante. Estoy bastante seguro de que he visto casos en los que la sesión todavía estaba "funcionando" después de tal excepción. El problema es tal vez que esto no está garantizado. Tendré que cavar esto un poco más.

Actualización: Citando this message de un desarrollador de hibernación en los foros de hibernación:

La sesión de hibernación se debe tirar cuando se produce ninguna excepción.Se puede dejar en un estado incoherente con relación a la base de datos. Este no es el caso con una sesión de solo lectura ya que no emplea ningún caché de nivel de sesión. Alternativamente, podría borrar el caché del nivel de la sesión cuando ocurra una excepción (pero personalmente no me gusta este enfoque, ya que no es una solución limpia y puede generar problemas y problemas en el futuro).

Así que el problema no es realmente si el Session es "técnicamente utilizable" o no, el problema es que podría no comportarse como se esperaba. Y claramente no quieres eso.

¿Cómo reproducir esto en una prueba?

Usted puede violar deliberadamente una condición que causa una subclase de ser lanzado, por ejemplo, por violar una restricción de integridad para obtener una ConstraintViolationException. ¿Qué tal algo así:

Session session = HibernateUtil.getCurrentSession(); 

try { 
    session.beginTransaction();  
    // execute some code that violates a unique constraint 
    ... 
    session.getTransaction().commit(); 
} catch (HibernateException e) { 
    session.getTransaction().rollback(); 
} 

// keep using the session 
try { 
    session.beginTransaction();  
    ... 
    session.getTransaction().commit(); 
} catch (HibernateException e) { 
    session.getTransaction().rollback(); 
} 
// do we reach this point? 

Pero no estoy seguro de que esto pruebe algo. Quiero decir, si la sesión sigue funcionando, solo podemos concluir para este caso particular.

Actualización: Olvide mi sugerencia, no probará nada. Y, de hecho, creo que sería extremadamente complicado (técnica y funcionalmente) probar que las cosas funcionan como se esperaba (y eso podría ser un "accidente") después de un HibernateException. Entonces, en lugar de tratar de probar algo, creo que lo correcto es seguir la recomendación (o la alternativa sugerida, pero solo solicitaría un nuevo Session y close para cada transacción, excepción hecha o no).

¿Cuál es el término correcto para una prueba de este tipo?

Prueba de integración? Prueba de robustez?

1

Mi opinión sobre esto es que

  1. it't casi imposible saber si Hibernate trabajar, se encontró con una situación inesperada y ahora se está ejecutando en un estado indefinido - Básicamente se le invoca UB realizando más operaciones en la sesión.

  2. de la idea - para registrar una interceptor y lanzar una HibernateException en ella - espero que Hibernate no cogerá: D inyección

  3. de fallos?

+0

En cuanto a 1), esto es lo que también entiendo, así que estoy feliz de haber hecho nuestra aplicación más robusta. El problema directo es, ¿cómo verificar esto en una prueba independiente? 2) suena interesante, ya que esto podría hacerse sin modificar la aplicación en sí. –

3

Interesante. La mejor forma de saberlo es ir y echar un vistazo a las fuentes de Hibernate.Por lo que yo entiendo, el Session no lleva mucho estado alrededor, excepto por lo siguiente material:

  • caché de primer nivel (ver StatefulPersistenceContext) - que podría ser en realidad rota después de un fallo de transacción;
  • lista de acciones (ver ActionQueue) - parece estar bien si realiza un enjuague manual;
  • detectores de eventos - sus instancias son conscientes de sesiones (véase EventSource) y, como te habrás dado cuenta, las excepciones JDBC siempre son causadas por el lavado (DefaultFlushEventListener, para ser más precisos), pero un rápido vistazo a AbstractFlushingEventListener muestra que realmente funcionan con la sesión JDBCContext y ActionQueue.

Y lo último, la reversión de la transacción en sí no interfiere con el estado de la sesión de ninguna manera, consulte JDBCTransaction.

Por lo que he jugado, el Session en realidad sobrevive a una transacción defectuosa, excepto que la memoria caché de primer nivel puede ser un poco inexacta; Para evitar esto, puede preferir llamar al session.refresh o session.load para volver a sincronizar explícitamente los estados.

EDIT: la forma más rápida para probar excepciones JDBC es probablemente session.createSQLQuery("something offensive to SQL parser").executeUpdate() - obtendrá una perfecta SQLGrammarException - y una transacción rota =)

0

crear una subclase de la sesión para la prueba. Impleméntelo para lanzar una excepción en algún escenario específico. Use una configuración de hibernación específica de la prueba que configura hibernación para usar su sesión de prueba.

+0

Esto arroja una HibernateException de hecho, pero no afecta el estado de la sesión de Hibernate. En otras palabras, esto es solo una sesión simulada implementada a mano. –

+0

tal vez no entiendo lo que está buscando entonces. ¿Está buscando una manera confiable de poner Hibernate en un estado roto (por ejemplo, indefinido)? ¿y entonces que? – james

+0

Estoy buscando una manera confiable de poner la sesión de Hibernate en un estado roto, de modo que la versión anterior de nuestra aplicación (que reutiliza la misma sesión en todas las transacciones) falla, y la versión más nueva (que obtiene una nueva sesión para cada transacción) pasa la prueba. –

Cuestiones relacionadas