Hibernate.orgCommunity Documentation

Capítulo 13. Transacciones y concurrencia

13.1. Ámbitos de sesión y de transacción
13.1.1. Unidad de trabajo
13.1.2. Conversaciones largas
13.1.3. Consideración de la identidad del objeto
13.1.4. Temas comúnes
13.2. Demarcación de la transacción de la base de datos
13.2.1. Entorno no administrado
13.2.2. Utilización de JTA
13.2.3. Manejo de excepciones
13.2.4. Tiempo de espera de la transacción
13.3. Control de concurrencia optimista
13.3.1. Chequeo de versiones de la aplicación
13.3.2. Sesión extendida y versionado automático
13.3.3. Objetos separados y versionado automático
13.3.4. Personalización del versionado automático
13.4. Bloqueo pesimista
13.5. Modos de liberación de la conexión

El punto más importante sobre Hibernate y el control de concurrencia es que es fácil de comprender. Hibernate usa directamente conexiones JDBC y recursos JTA sin agregar ningún comportamiento de bloqueo adicional. Le recomendamos bastante que tome algo de tiempo con la especificación de JDBC, ANSI y el aislamiento de transacciones de su sistema de gestión de base de datos.

Hibernate no bloquea objetos en la memoria. Su aplicación puede esperar el comportamiento definido por el nivel de aislamiento de sus transacciones de las bases de datos. Gracias a la Session, la cual también es un caché con alcance de transacción, Hibernate proporciona lecturas repetidas para búsquedas del identificador y consultas de entidad y no consultas de reporte que retornan valores escalares.

Además del versionado del control de concurrencia optimista automático, Hibernate también ofrece una API (menor) para bloqueo pesimista de filas, usando la sintáxis SELECT FOR UPDATE. Esta API y el control de concurrencia optimista se discuten más adelante en este capítulo.

Comenzamos la discusión del control de concurrencia en Hibernate con la granularidad de Configuration, SessionFactory y Session, así como las transacciones de la base de datos y las conversaciones largas.

Una SessionFactory es un objeto seguro entre hilos y costoso de crear pensado para que todas las hebras de la aplicación lo compartan. Se crea una sola vez, usualmente en el inicio de la aplicación, a partir de una instancia Configuration.

Una Session es un objeto de bajo costo, inseguro entre hilos que se debe utilizar una sola vez y luego se debe descartar: para un sólo pedido, una sola conversación o una sóla unidad de trabajo. Una Session no obtendrá una Connection JDBC o un Datasource a menos de que sea necesario. No consumirá recursos hasta que se utilice.

Una transacción de la base de datos tiene que ser tan corta como sea posible para reducir la contención de bloqueos en la base de datos. Las transacciones largas de la base de datos prevendrán a su aplicación de escalar a una carga altamente concurrente. Por lo tanto, no se recomienda que mantenga una transacción de la base de datos abierta durante el tiempo para pensar del usuario, hasta que la unidad de trabajo se encuentre completa.

¿Cuál es el ámbito de una unidad de trabajo? ¿Puede una sola Session de Hibernate extenderse a través de varias transacciones de la base de datos o ésta es una relación uno-a-uno de ámbitos? ¿Cuándo debe abrir y cerrar una Session? y ¿cómo demarca los límites de la transacción de la base de datos? En las siguientes secciones abordaremos estas preguntas.

First, let's define a unit of work. A unit of work is a design pattern described by Martin Fowler as “ [maintaining] a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems. ”[PoEAA] In other words, its a series of operations we wish to carry out against the database together. Basically, it is a transaction, though fulfilling a unit of work will often span multiple physical database transactions (see Sección 13.1.2, “Conversaciones largas”). So really we are talking about a more abstract notion of a transaction. The term "business transaction" is also sometimes used in lieu of unit of work.

Primero, no use el antipatrón sesión-por-operación: no abra y cierre una Session para cada llamada simple a la base de datos en un solo hilo. Lo mismo aplica para las transacciones de base de datos. Las llamadas a la base de datos en una aplicación se hacen usando una secuencia planeada; estas se agrupan dentro de unidades de trabajo atómicas. Esto también significa que el auto-commit después de cada una de las declaraciones SQL es inútil en una aplicación ya que este modo está pensado para trabajo ad-hoc de consola SQL. Hibernate deshabilita, o espera que el servidor de aplicaciones lo haga, el modo auto-commit inmediatamente. Las transacciones de las bases de datos nunca son opcionales. Toda comunicación con una base de datos tiene que ocurrir dentro de una transacción. El comportamiento auto-commit para leer datos se debe evitar, ya que hay muy poca probabilidad de que las transacciones pequeñas funcionen mejor que una unidad de trabajo definida claramente. La última es mucho más sostenible y extensible.

El patrón más común en una aplicación multiusuario cliente/servidor es sesión-por-petición. En este modelo, una petición del cliente se envia al servidor, en donde se ejecuta la capa de persistencia de Hibernate. Se abre una nueva Session de Hibernate y todas las operaciones de la base de datos se ejecutan en esta unidad de trabajo. Una vez completado el trabajo, y una vez se ha preparado la respuesta para el cliente, se limpia la sesión y se cierra. Use una sóla transacción de la base de datos para servir la petición del cliente, dándole inicio y guardándola cuando abre y cierra la Session. La relación entre las dos es uno-a-uno y este modelo es a la medida perfecta de muchas aplicaciones.

El reto se encuentra en la implementación. Hibernate brinda administración incorporada de la "sesión actual" para simplificar este patrón. Inicie una transacción cuando se tiene que procesar un pedido del servidor y termine la transacción antes de que se envie la respuesta al cliente. Las soluciones más comunes son ServletFilter, un interceptor AOP con un punto de corte en los métodos del servicio o un contenedor proxy/intercepción. Un contenedor EJB es una manera estandarizada de implementar aspectos de doble filo como demarcación de transacción en beans de sesión EJB, declarativamente con CMT. Si decide utilizar la demarcación de transacción programática, use el API Transaction de Hibernate de fácil uso y portable que se muestra más adelante en este capítulo.

Your application code can access a "current session" to process the request by calling sessionFactory.getCurrentSession(). You will always get a Session scoped to the current database transaction. This has to be configured for either resource-local or JTA environments, see Sección 2.3, “Sesiones contextuales”.

Puede extender el ámbito de una Session y transacción de la base de datos hasta que "se ha presentado la vista". Esto es bastante útil en aplicaciones de servlet que utilizan una fase de entrega separada después de que se ha procesado el pedido. El extender la transacción de la base de datos hasta que la entrega de la vista se encuentre completa es fácil de lograr si implementa su propio interceptor. Sin embargo, no se logra fácilmente si depende de EJBs con transacciones administradas por el contenedor. Una transacción se completará cuando un método EJB retorna, antes de que pueda empezar la entrega de cualquier vista. Vea el sitio web de Hibernate y el foro para encontrar consejos y ejemplos sobre este patrón de sesión abierta en vista.

El patrón sesión-por-petición no es la única forma de diseñar unidades de trabajo. Muchos procesos empresariales requieren una serie completa de interacciones con el usuario intercaladas con accesos a la base de datos. En aplicaciones empresariales y web no es aceptable que una transacción de la base de datos abarque la interacción de un usuario. Considere el siguiente ejemplo:

Desde el punto de vista del usuario, llamamos a esta unidad de trabajo, una larga conversación o transacción de aplicación. Hay muchas formas de implementar esto en su aplicación.

Una primera implementación ingenua podría mantener abierta la Session y la transacción de la base de datos durante el tiempo para pensar del usuario, con bloqueos en la base de datos para prevenir la modificación simultánea y para garantizar el aislamiento y la atomicidad. Esto es un antipatrón, ya que la contención de bloqueo no permitiría a la aplicación escalar con el número de usuarios simultáneos.

Tiene que usar varias transacciones de la base de datos para implementar la conversación. En este caso, mantener el aislamiento de los procesos empresariales se vuelve una responsabilidad parcial de la capa de la aplicación. Una sóla conversación usualmente abarca varias transacciones de la base de datos. Será atómica si sólo una de estas transacciones de la base de datos (la última) almacena los datos actualizados. Todas las otras simplemente leen datos (por ejemplo, en un diálogo de estilo-asistente abarcando muchos ciclos petición/respuesta). Esto es más fácil de implementar de lo que suena, especialmente si usa las funcionalidades de Hibernate:

Tanto la sesión-por-petición-con-objetos-separados como la sesión-por-conversación tienen ventajas y desventajas. Estas desventajas las discutimos más adelante en este capítulo en el contexto del control optimista de concurrencia.

Una aplicación puede acceder simultáneamente al mismo estado persistente en dos Sessiones diferentes. Sin embargo, una instancia de una clase persistente nunca se comparte entre dos instancias de Session. Por lo tanto, existen dos nociones diferentes de identidad:

Para los bjetos unidos a una Session en particular (por ejemplo, en el ámbito de una Session) las dos nociones son equivalentes y la identidad de la MVJ para la identidad de la base de datos se encuentra garantizada por Hibernate. Mientras la aplicación acceda simultáneamente al "mismo" objeto empresarial (identidad persistente) en dos sesiones diferentes, las dos instancias serán realmente "diferentes" (identidad MVJ). Los conflictos se resuelven usando un enfoque optimista y el versionado automático en tiempo de vaciado/ al guardar.

Este enfoque deja que Hibernate y la base de datos se preocupen de la concurrencia. Además provee la mejor escalabilidad, ya que garantizando la identidad en unidades de trabajo monohilo no se necesitan bloqueos caros u otros medios de sincronización. La aplicación no necesita sincronizar sobre ningún objeto empresarial, siempre que se mantenga un solo hilo por Session. Dentro de una Session la aplicación puede usar con seguridad == para comparar objetos.

Sin embargo, una aplicación que usa == fuera de una Session, podría ver resultados inesperados. Esto podría ocurrir incluso en sitios algo inesperados. Por ejemplo, si pone dos instancias separadas dentro del mismo Set ambas podrían tener la misma identidad de la base de datos (por ejemplo, representar la misma fila). Sin embargo, la identidad MVJ, por definición, no está garantizada para las instancias en estado separado. El desarrollador tiene que sobrescribir los métodos equals() y hashCode() en las clases persistentes e implementar su propia noción de igualdad de objetos. Hay una advertencia: nunca use el identificador de la base de datos para implementar la igualdad. Use una clave de negocio, una combinación de atributos únicos, usualmente inmutables. El identificador de la base de datos cambiará si un objeto transitorio es hecho persistente. Si la instancia transitoria (usualmente junto a las instancias separadas) es mantenida en un Set, cambiar el código hash rompe el contrato del Set. Los atributos para las claves empresariales no tienen que ser tan estables como las claves principales de la base de datos, sólo tiene que garantizar estabilidad en tanto los objetos estén en el mismo Set. Mire el sitio web de Hibernate para obetener una discusión más profunda de este tema. Note también que éste no es problema de Hibernate, sino que simplemente se tiene que implementar la identidad y la igualdad de los objetos Java.

No use los antipatrones sesión-por-sesión-de-usuario o sesión-por-aplicación (hay excepciones raras a esta regla). Algunos de los siguientes temas también podrían aparecer con los patrones recomendados así que asegúrese de que entiende las implicaciones antes de tomar una decisión de diseño:

Los límites de las transacciones de la base de datos o el sistema son siempre necesarios. Ninguna comunicación con la base de datos puede darse fuera de una transacción de la base de datos (esto parece confundir a muchos desarrolladores acostumbrados al modo auto-commit). Siempre use límites de transacción claros, incluso para las operaciones de sólo lectura. Dependiendo del nivel de aislamiento y las capacidades de la base de datos, esto podría requerirse o no, pero no hay inconvenientes si siempre demarca explícitamente las transacciones. Con seguridad, una transacción única de base de datos va a funcionar mejor que muchas transacciones pequeñas, inclusive para leer datos.

Una aplicación Hibernate puede ejecutarse en entornos no administrados (por ejemplo, aplicaciones simples Web o Swing autónomas) y entornos administrados por J2EE. En un entorno no administrado, Hibernate es usualmente responsable de su propio pool de conexiones de la base de datos. El desarrollador de aplicaciones tiene que establecer manualmente los límites de transacción (inicar, guardar o deshacer las transacciones de la base de datos) por sí mismo. Un entorno administrado usualmente proporciona transacciones gestionadas por contenedor, con el ensamble de transacción definido declarativamente (por ejemplo, en descriptores de despliegue de beans de sesión EJB). La demarcación programática de transacciones ya no es necesaria.

Sin embargo, comúnmente se quiere mantener su capa de persistencia portátil entre entornos locales- de recursos no-administrados y sistemas que pueden confiar en JTA, pero utilizar BMT en vez de CMT. En ambos casos utilizaría la demarcación de transacción programática. Hibernate ofrece una API de envoltura llamada Transaction que se traduce al sistema de transacciones nativo de su entorno de despliegue. Esta API es de hecho opcional, pero le recomendamos bastante su uso salvo que esté en un bean de sesión CMT.

Usualmente, el finalizar una Session implica cuatro fases distintas:

Anteriormente se discutió el vacido de la sesión así que ahora vamos a mirar más de cerca la demarcación de transacciones y el manejo de excepciones en los dos entornos administrado y no administrado.

Si una capa de persistencia Hibernate se ejecuta en un entorno no administrado, las conexiones de la base de datos se manejan usualmente por simples pools de conexión (por ejemplo, no-DataSource) del cual Hibernate obtiene conexiones al ser necesario. El idioma de manejo de sesión/transacción se ve así:

// Non-managed environment idiom

Session sess = factory.openSession();
Transaction tx = null;
try {
    tx = sess.beginTransaction();
    // do some work
    ...
    tx.commit();
}
catch (RuntimeException e) {
    if (tx != null) tx.rollback();
    throw e; // or display error message
}
finally {
    sess.close();
}

No tiene que vaciar con flush() la Session explícitamente: la llamada a commit() automáticamente dispara la sincronización dependiendo del FlushMode para la sesión. Una llamada a close() marca el final de una sesión. La implicación principal de close() es que la conexión JDBC será abandonada por la sesión. Este código Java es portátil y ejecuta en entornos tanto no-administrados como JTA.

Como se mencionó anteriormente, una solución mucho más flexible es la administración de contexto "sesión actual" incorporada en Hibernate:

// Non-managed environment idiom with getCurrentSession()

try {
    factory.getCurrentSession().beginTransaction();
    // do some work
    ...
    factory.getCurrentSession().getTransaction().commit();
}
catch (RuntimeException e) {
    factory.getCurrentSession().getTransaction().rollback();
    throw e; // or display error message
}

No verá estos pedazos de código en una aplicación normal; las excepciones fatales (del sistema) siempre deben ser capturadas en la "cima". En otras palabras, el código que ejecuta las llamadas de Hibernate en la capa de persistencia y el código que maneja RuntimeException (y usualmente sólo puede limpiar y salir) se encuentran en capas diferentes. La administración de contexto actual de Hibernate puede simplificar de manera importante este diseño, ya que todo lo que necesita hacer es acceder a SessionFactory. El manejo de excepciones se discute más adelante en este capítulo.

Debe seleccionar org.hibernate.transaction.JDBCTransactionFactory, el cual es el predeterminado, y para el segundo ejemplo seleccionar "thread" como su hibernate.current_session_context_class.

Si su capa de persistencia se ejecuta en un servidor de aplicaciones (por ejemplo, detrás de los beans de sesión EJB), cada conexión de fuente de datos obtenida por Hibernate será parte de la transacción JTA global de manera automática. También puede instalar una implementación JTA autónoma y utilizarla sin EJB. Hibernate ofrece dos estrategias para esta integración JTA.

Si usa transacciones gestionadas-por-bean (BMT) Hibernate le dirá al servidor de aplicaciones que comience y finalice una transacción BMT si usa la API de Transaction. De modo que, el código de gestión de la transacción es idéntico al de un entorno no administrado.

// BMT idiom

Session sess = factory.openSession();
Transaction tx = null;
try {
    tx = sess.beginTransaction();
    // do some work
    ...
    tx.commit();
}
catch (RuntimeException e) {
    if (tx != null) tx.rollback();
    throw e; // or display error message
}
finally {
    sess.close();
}

Si quiere utilizar un vínculo de transacción Session, es decir, la funcionalidad getCurrentSession() para propagación de contexto de manera fácil, tendrá que utilizar el API UserTransaction del JTA directamente:

// BMT idiom with getCurrentSession()

try {
    UserTransaction tx = (UserTransaction)new InitialContext()
                            .lookup("java:comp/UserTransaction");
    tx.begin();
    // Do some work on Session bound to transaction
    factory.getCurrentSession().load(...);
    factory.getCurrentSession().persist(...);
    tx.commit();
}
catch (RuntimeException e) {
    tx.rollback();
    throw e; // or display error message
}

Con CMT, la demarcación de transacción se realiza en los descriptores de implementacion bean de sesión, no programáticamente. Por lo tanto el código se reduce a:

// CMT idiom

 Session sess = factory.getCurrentSession();
 // do some work
 ...

En un CMT/EJB incluso el deshacer sucede de forma automática. Un RuntimeException lanzado por un método bean de sesión le dice al contenedor que establezca una transacción global para deshacer. No necesita utilizar el API Transaction de Hibernate con BMT o CMT y obtiene la propagación automática de sesión"actual" vinculada a la transacción.

Al configurar la fábrica de transacciones de Hibernate, escoja org.hibernate.transaction.JTATransactionFactory si utiliza JTA directamente (BMT) y org.hibernate.transaction.CMTTransactionFactory en una bean de sesión CMT. Recuerde establecer también hibernate.transaction.manager_lookup_class. Asegúrese de que su hibernate.current_session_context_class no se encuentra configurado (compatibilidad retrasada) o configurada como "jta".

La operación getCurrentSession() tiene un inconveniente en un entorno JTA. Hay una desventaja en el uso del modo de liberación de la conección after_statement, la cual luego se utiliza por defecto. Debido a una limitación de la especificación JTA, no le es posible a Hibernate limpiar automáticamente cualquier instancia ScrollableResults o Iterator no cerradas y retornadas por scroll() o iterate(). Tiene que liberar el cursor de la base de datos subyacente llamando a ScrollableResults.close() o Hibernate.close(Iterator) explícitamente desde un bloque finally. La mayoría de las aplicaciones pueden evitar fácilmente el utilizar scroll() o iterate() del código JTA o CMT.

Si la Session lanza una excepción, incluyendo cualquier SQLException, debe deshacer inmediatamente la transacción de la base de datos, llamar a Session.close() y descartar la instancia de Session. Ciertos métodos de Session no dejarán la sesión en un estado consistente. Ninguna excepción lanzada por Hibernate puede ser tratada como recuperable. Asegúrese de que la Session se cierre llamando a close() en un bloque finally.

La HibernateException, que envuelve a la mayoría de los errores que pueden ocurrir en la capa de persistencia de Hibernate, es una excepción no chequeada. No lo era en versiones anteriores de Hibernate. En nuestra opinión, no debemos forzar al desarrollador de aplicaciones a capturar una excepción irrecuperable en una capa baja. En la mayoría de los sistemas, las excepciones no chequeadas y fatales son manejadas en uno de los primeros cuadros de la pila de llamadas a métodos (por ejemplo, en las capas más altas) y presenta un mensaje de error al usuario de la aplicación o se toma alguna otra acción apropiada. Note que Hibernate podría también lanzar otras excepciones no chequeadas que no sean una HibernateException. Estas no son recuperables y debe tomarse una acción apropiada.

Hibernate envuelve SQLExceptions lanzadas mientras se interactúa con la base de datos en una JDBCException. De hecho, Hibernate intentará convertir la excepción en una subclase de JDBCException más significativa. La SQLException subyacente siempre está disponible por medio de JDBCException.getCause(). Hibernate convierte la SQLException en una subclase de JDBCException apropiada usando el SQLExceptionConverter adjunto a la SessionFactory. Por defecto, el SQLExceptionConverter está definido por el dialecto configurado. Sin embargo, también es posible enchufar una implementación personalizada . Consulte los javadocs de la clase SQLExceptionConverterFactory para obtener más detalles. Los subtipos estándar de JDBCException son:

El único enfoque consistente con una alta concurrencia y una alta escalabilidad es el control de concurrencia optimista con versionamiento. El chequeo de versión utiliza números de versión, o sellos de fecha (timestamps), para detectar actualizaciones en conflicto y para prevenir la pérdida de actualizaciones. Hibernate proporciona tres enfoques posibles de escribir código de aplicación que utilice concurrencia optimista. Los casos de uso que mostramos se encuentran en el contexto de conversaciones largas, pero el chequeo de versiones tiene además el beneficio de prevenir la pérdida de actualizaciones en transacciones individuales de la base de datos.

En una implementación que no tiene mucha ayuda de Hibernate, cada interacción con la base de datos ocurre en una nueva Session y el desarrollador es el responsable de recargar todas las intancias persistentes desde la base de datos antes de manipularlas. Este enfoque fuerza a la aplicación a realizar su propio chequeo de versiones para asegurar el aislamiento de transacciones de conversaciones. Este enfoque es el menos eficiente en términos de acceso a la base de datos. Es el enfoque más similar a los EJBs de entidad.

// foo is an instance loaded by a previous Session

session = factory.openSession();
Transaction t = session.beginTransaction();
int oldVersion = foo.getVersion();
session.load( foo, foo.getKey() ); // load the current state
if ( oldVersion != foo.getVersion() ) throw new StaleObjectStateException();
foo.setProperty("bar");
t.commit();
session.close();

La propiedad version se mapea utilizando <version>, e Hibernate la incrementará automáticamente durante la limpieza si la entidad está desactualizada.

Si está operando un entorno de baja-concurrencia-de-datos y no requiere chequeo de versiones, puede usar este enfoque y simplemente saltarse el chequeo de versiones. En ese caso, el último que guarda gana y será la estrategia por defecto para conversaciones largas. Tenga en mente que esto podría confundir a los usuarios de la aplicación, pues podrían experimentar pérdidas de actualizaciones sin mensajes de error ni oportunidad de fusionar los cambios conflictivos.

El chequeo manual de versiones es factible sólamente en circunstancias muy triviales y no es práctico para la mayoría de las aplicaciones. Con frecuencia se tienen que chequear no sólamente las intancias sólas, sino también grafos completos de objetos modificados. Hibernate ofrece el chequeo de versiones automático con el paradigma de diseño de Session larga o de instancias separadas.

Una sóla instancia de Session y sus instancias persistentes se utilizan para toda la convervsación conocida como sesión-por-conversación. Hibernate chequea las versiones de instancia en el momento de vaciado, lanzando una excepción si se detecta una modificación concurrente. Le concierne al desarrollador capturar y manejar esta excepción. Las opciones comunes son la oportunidad del usuario de fusionar los cambios, o de recomenzar el proceso empresarial sin datos desactualizados.

La Session se desconecta de cualquier conexión JDBC subyacente a la espera de una interacción del usuario. Este enfoque es el más eficiente en términos de acceso a la base de datos. La aplicación no necesita por sí misma tratar con el chequeo de versiones, ni re-unir instancias separadas, ni tiene que recargar instancias en cada transacción de la base de datos.

// foo is an instance loaded earlier by the old session

Transaction t = session.beginTransaction(); // Obtain a new JDBC connection, start transaction
foo.setProperty("bar");
session.flush();    // Only for last transaction in conversation
t.commit();         // Also return JDBC connection
session.close();    // Only for last transaction in conversation

El objeto foo sabe en qué Session fue cargado. El dar inicio a una nueva base de datos en una sesión vieja obtiene una nueva conexión y reanuda la sesión. El guardar una transacción de la base de datos desconecta una sesión de la conexion JDBC y devuelve la conexión al pool. Después de la reconexión, para poder forzar una verificación de versión sobre datos que usted no está actalizando, puede llamar a Session.lock() con LockMode.READ en cualquier objeto que pueda haber sido actualizado por otra transacción. No necesita bloquear ningún dato que sí esté actualizando. Usualmente configuraría FlushMode.MANUAL en una Session extendida, de manera que de hecho sólamente se permite persistir el último ciclo de transacción de la base de datos de todas las modificaciones realizadas en esta conversación. Sólamente esta última transacción de la base de datos incluiría la operación flush() y luego cierra -close()- la sesión para dar fin a la conversación.

Este patrón es problemático si la Session es demasiado grande para almacenarla durante el tiempo para pensar del usuario, por ejemplo, una HttpSession se debe mantener tan pequeña como sea posible. Como la Session también lo es el caché de primer nivel (obligatorio) y comprende todos los objetos cargados, probablemente podemos utilizar esta estrategia sólamente para unos pocos ciclos de pedido/respuesta. Debe utilizar una Session sólamente para una conversación única ya que pronto también tendrá datos añejos.

Mantenga la Session desconectada cerca a la capa de persistencia. Use un bean de sesión EJB con estado para mantener la Session en un entorno de tres capas . No la transfiera a la capa web ni la serialice en una capa separada para almacenarla en la HttpSession.

El patrón de sesión extendido, o sesión-por-conversación, es más dificil de implementar con la administración de contexto de sesión actual. Necesita proporcionar su propia implementación de la CurrentSessionContext para esto, vea el Wiki de Hibernate para obtener más ejemplos.

Puede deshabilitar el incremento de la versión automática de Hibernate para ciertas propiedades y colecciones en particular estableciendo el atributo de mapeo optimistic-lock como false. Hibernate entonces ya no incrementará más las versiones si la propiedad se encuentra desactualizada.

Los esquemas heredados de la base de datos con frecuencia son estáticos y no pueden ser modificados. Inclusive otras aplicaciones podrían también acceder la misma base de datos y no saber cómo manejar los números de versión ni los sellos de fecha. En ambos casos, el versionado no puede confiarse a una columna en particular en una tabla. Para forzar un chequeo de versiones sin un mapeo de propiedad de versión o sello de fecha, con una comparación del estado de todos los campos en una fila, active optimistic-lock="all" en el mapeo de <class>. Esto funciona conceptualmente sólamente si Hibernate puede comparar el estado viejo y el nuevo, es decir, si usa una sóla Session larga y no sesión-por-petición-con-instancias-separadas.

Las modificaciones simultáneas pueden permitirse en instancias en tanto los cambios que se hayan realizado no se superpongan. Si establece optimistic-lock="dirty" al mapear la <class>, Hibernate sólo comparará los campos desactualizados durante el vaciado.

En ambos casos, con columnas de versión/sello de fecha dedicadas o con comparación de campos completos/desactualizados, Hibernate utiliza una sóla declaración UPDATE (con una cláusula WHERE apropiada) por entidad para ejecutar el chequeo de versiones y actualizar la información. Si utiliza una persistencia transitiva para la re-unión en cascada de entidades asociadas, Hibernate podría ejecutar actualizaciones innecesarias. Esto usualmente no es problema, pero podrían ejecutarse disparadores (triggers) enactualizazción en la base de datos incluso cuando no se haya hecho ningún cambio a las instancias separadas. Puede personalizar este comportamiento estableciendo select-before-update="true" en el mapeo de <class>, forzando a Hibernate a SELECT la instancia para asegurar que las actualizaciones realmente ocurran, antes de actualizar la fila.

No se pretende que los usuarios tomen mucho tiempo preocupándose de las estrategias de bloqueo. Usualmente es suficiente con especificar un nivel de aislamiento para las conexiones JDBC y entonces simplemente dejar que la base de datos haga todo el trabajo. Sin embargo, los usuarios avanzados a veces pueden obtener bloqueos exclusivos pesimistas, o reobtener bloqueos al comienzo de una nueva transacción.

Hibernate siempre usará el mecanismo de bloqueo de la base de datos, nunca el bloqueo de objetos en memoria.

La clase LockMode define los diferentes niveles de bloqueo que Hibernate puede adquirir. Un bloqueo se obtiene por medio de los siguientes mecanismos:

La "petición explícita del usuario" se expresa en una de las siguientes formas:

Si se llama a Session.load() con UPGRADE o UPGRADE_NOWAIT, y el objeto pedido no ha sido cargado todavía por la sesión, el objeto es cargado usando SELECT ... FOR UPDATE. Si se llama a load() para un objeto que ya esté cargado con un bloqueo menos restrictivo que el pedido, Hibernate llama a lock() para ese objeto.

Session.lock() realiza un chequeo de número de versión si el modo de bloqueo especificado es READ, UPGRADE o UPGRADE_NOWAIT. En el caso de UPGRADE o UPGRADE_NOWAIT, se usa SELECT ... FOR UPDATE.

Si la base de datos no soporta el modo de bloqueo solicitado, Hibernate usa un modo opcional apropiado en lugar de lanzar una excepción. Esto asegura que las aplicaciones serán portátiles.

La herencia (2x) de Hibernate en relación con la administración de la conexion JDBC fue que una Session obtendría una conexión cuando se necesitara por primera vez y luego la mantendría hasta que se cerrara la sesión. Hibernate 3.x introdujo la noción de modos de liberación de conexión para decirle a la sesión como manejar sus conexiones JDBC. La siguiente discusión sólamente es pertinente para las conexiones provistas por medio de un ConnectionProvider configurado. Las conexiones provistas por el usuario no se discuten aquí. Los diferentes modos de liberación se identifican por los valores numerados de org.hibernate.ConnectionReleaseMode:

El parámetro de configuración hibernate.connection.release_mode se utiliza para especificar el modo de liberación a utilizar. Los valores posibles son los siguientes: