hibernate-orm/reference/es/modules/transactions.xml

926 lines
56 KiB
XML

<chapter id="transactions" revision="1">
<title>Transacciones y Concurrencia</title>
<para>
El punto m&#x00e1;s importante sobre Hibernate y el control de concurrencia es que muy f&#x00e1;cil
de comprender. Hibernate usa directamente conexiones JDBC y recursos JTA sin agregar
ning&#x00fa;n comportamiento de bloqueo adicional. Recomendamos altamente que gastes algo de
tiempo con la especificaci&#x00f3;n de JDBC, ANSI, y el aislamiento de transacciones de tu sistema
de gesti&#x00f3;n de base de datos. Hibernate s&#x00f3;lo a&#x00f1;ade versionado autom&#x00e1;tico pero no bloquea
objetos en memoria ni cambia el nivel de aislamiento de tus transacciones de base de datos.
B&#x00e1;sicamente, usa Hibernate como usar&#x00ed;as JDBC directo (o JTA/CMT) con tus recursos de base de
datos.
</para>
<para>
Sin embargo, adem&#x00e1;s del versionado autom&#x00e1;tico, Hibernate ofrece una API (menor) para
bloqueo pesimista de filas, usando la sint&#x00e1;xis <literal>SELECT FOR UPDATE</literal>.
Esta API se discute m&#x00e1;s adelante en este cap&#x00ed;tulo:
</para>
<para>
Comenzamos la discusi&#x00f3;n del control de concurrencia en Hibernate con la granularidad
de <literal>Configuration</literal>, <literal>SessionFactory</literal>, y
<literal>Session</literal>, as&#x00ed; como la base de datos y las transacciones de aplicaci&#x00f3;n
largas.
</para>
<sect1 id="transactions-basics">
<title>&#x00c1;mbitos de sesi&#x00f3;n y de transacci&#x00f3;n</title>
<para>
Una <literal>SessionFactory</literal> es un objeto seguro entre hebras caro-de-crear
pensado para ser compartido por todas las hebras de la aplicaci&#x00f3;n. Es creado una sola vez,
usualmente en el arranque de la aplicaci&#x00f3;n, a partir de una instancia de <literal>Configuration</literal>.
</para>
<para>
Una <literal>Session</literal> es un objeto barato, inseguro entre hebras que debe
ser usado una sola vez, para un solo proceso de negocio, una sola unidad de trabajo,
y luego descartado. Una <literal>Session</literal> no obtendr&#x00e1; una <literal>Connection</literal>
JDBC (o un <literal>Datasource</literal>) a menos que sea necesario, de modo que puedas
abrir y cerrar seguramente una <literal>Session</literal> incluso si no est&#x00e1;s seguro
que se necesitar&#x00e1; acceso a los datos para servir una petici&#x00f3;n en particular. (Esto se
vuelve importante en cuanto est&#x00e9;s implementando alguno de los siguientes patrones usando
intercepci&#x00f3;n de peticiones).
</para>
<para>
Para completar este cuadro tienes que pensar tambi&#x00e9;n en las transacciones de base de
datos. Una transacci&#x00f3;n de base de datos tiene que ser tan corta como sea posible, para
reducir la contenci&#x00f3;n de bloqueos en la base de datos. Las transacciones largas de base de
datos prevendr&#x00e1;n a tu aplicaci&#x00f3;n de escalar a una carga altamente concurrente.
</para>
<para>
&#x00bf;Qu&#x00e9; es el &#x00e1;mbito de una unidad de trabajo? &#x00bf;Puede una sola <literal>Session</literal> de Hibernate
extenderse a trav&#x00e9;s de varias transacciones de base de datos o es &#x00e9;sta una relaci&#x00f3;n uno-a-uno
de &#x00e1;mbitos? &#x00bf;Cu&#x00e1;ndo debes abrir y cerrar una <literal>Session</literal> y c&#x00f3;mo demarcas los
l&#x00ed;mites de la transacci&#x00f3;n de base de datos?
</para>
<sect2 id="transactions-basics-uow">
<title>Unidad de trabajo</title>
<para>
Primero, no uses el antipatr&#x00f3;n <emphasis>sesi&#x00f3;n-por-operaci&#x00f3;n</emphasis>, esto es,
&#x00a1;no abras y cierres una <literal>Session</literal> para cada simple llamada a la base
de datos en una sola hebra! Por supuesto, lo mismo es verdad para transacciones de base de
datos. Las llamadas a base de datos en una aplicaci&#x00f3;n se hacen usando una secuencia
prevista, que est&#x00e1;n agrupadas dentro de unidades de trabajo at&#x00f3;micas. (Nota que esto
tambi&#x00e9;n significa que el auto-commit despu&#x00e9;s de cada una de las sentencias SQL es in&#x00fa;til
en una aplicaci&#x00f3;n, este modo est&#x00e1; pensado para trabajo ad-hoc de consola SQL.
Hibernate deshabilita, o espera que el servidor de aplicaciones lo haga, el modo
auto-commit inmediatamente.)
</para>
<para>
El patr&#x00f3;n m&#x00e1;s com&#x00fa;n en una aplicaci&#x00f3;n mutiusuario cliente/servidor es
<emphasis>sesi&#x00f3;n-por-petici&#x00f3;n</emphasis>. En este modelo, una petici&#x00f3;n del cliente
es enviada al servidor (en donde se ejecuta la capa de persistencia de Hibernate),
se abre una nueva <literal>Session</literal> de Hibernate, y todas las operaciones
de base de datos se ejecutan en esta unidad de trabajo. Una vez completado el trabajo
(y se ha preparado la respuesta para el cliente) la sesi&#x00f3;n es limpiada y cerrada.
Podr&#x00ed;as usar una sola transacci&#x00f3;n de base de datos para servir a petici&#x00f3;n del cliente,
comenz&#x00e1;ndola y comprometi&#x00e9;ndola cuando abres y cierras la <literal>Session</literal>.
La relaci&#x00f3;n entre las dos es uno-a-uno y este modelo es a la medida perfecta de muchas
aplicaciones.
</para>
<para>
El desaf&#x00ed;o yace en la implementaci&#x00f3;n: no s&#x00f3;lo tienen que comenzarse y terminarse correctamente
la <literal>Session</literal> y la transacci&#x00f3;n, sino que adem&#x00e1;s tienen que estar accesibles
para las operaciones de acceso a datos. La demarcaci&#x00f3;n de una unidad de trabajo se implementa
idealmente usando un interceptor que se ejecuta cuando una petici&#x00f3;n llama al servidor y anter que
la respuesta sea enviada (es decir, un <literal>ServletFilter</literal>). Recomendamos ligar la
<literal>Session</literal> a la hebra que atiende la petici&#x00f3;n, usando una variable
<literal>ThreadLocal</literal>. Esto permite un f&#x00e1;cil acceso (como acceder a una variable static)
en t&#x00f3;do el c&#x00f3;digo que se ejecuta en esta hebra. Dependiendo del mecanismo de demarcaci&#x00f3;n de
transacciones de base de datos que elijas, podr&#x00ed;as mantener tambi&#x00e9;n el contexto de la transacci&#x00f3;n
en una variable <literal>ThreadLocal</literal>. Los patrones de implementaci&#x00f3;n para esto son
conocidos como <emphasis>Sesi&#x00f3;n Local de Hebra (ThreadLocal Session)</emphasis> y
<emphasis>Sesi&#x00f3;n Abierta en Vista (Open Session in View)</emphasis>. Puedes extender f&#x00e1;cilmente
la clase de ayuda <literal>HibernateUtil</literal> mostrada anteriormente para encontrar
una forma de implementar un interceptor e instalarlo en tu entorno. Ver el sitio web de Hibernate
para consejos y ejemplos.
</para>
</sect2>
<sect2 id="transactions-basics-apptx">
<title>Transacciones de aplicaci&#x00f3;n</title>
<para>
El patr&#x00f3;n sesi&#x00f3;n-por-petici&#x00f3;n no es el &#x00fa;nico concepto &#x00fa;til que puedes usar para dise&#x00f1;ar unidades
de trabajo. Muchos procesos de negocio requiere una serie completa de interacciones con el
usuario intercaladas con accesos a base de datos. En aplicaciones web y de empresa no es aceptable
que una transacci&#x00f3;n de base de datos se extienda a trav&#x00e9;s de la interacci&#x00f3;n de un usuario.
Considera el siguiente ejemplo:
</para>
<itemizedlist>
<listitem>
<para>
Se abre la primera pantalla de un di&#x00e1;logo, los datos vistos por el usuario han sido
cargados en una <literal>Session</literal> y transacci&#x00f3;n de base de datos particular.
El usuario es libre de modificar los objetos.
</para>
</listitem>
<listitem>
<para>
El usuario hace click en "Salvar" despu&#x00e9;s de 5 minutos y espera que sus modificaciones
sean hechas persistentes. Tambi&#x00e9;n espera que &#x00e9;l sea la &#x00fa;nica persona editando esta
informaci&#x00f3;n y que no puede ocurrir ninguna modificaci&#x00f3;n en conflicto.
</para>
</listitem>
</itemizedlist>
<para>
Llamamos a esto unidad de trabajo, desde el punto de vista del usuario, una larga
<emphasis>transacci&#x00f3;n de aplicaci&#x00f3;n</emphasis> ejecut&#x00e1;ndose. Hay muchas formas en
que puedes implementar esto en tu aplicaci&#x00f3;n.
</para>
<para>
Una primera implementaci&#x00f3;n ingenua podr&#x00ed;a mantener abierta la <literal>Session</literal>
y la transacci&#x00f3;n de base de datos durante el tiempo de pensar del usuario, con bloqueos
tomados en la base de datos para prevenir la modificaci&#x00f3;n concurrente, y para garantizar
aislamiento y atomicidad. Esto es, por supuesto, un antipatr&#x00f3;n, ya que la contenci&#x00f3;n de
bloqueo no permitir&#x00ed;a a la aplicaci&#x00f3;n escalar con el n&#x00fa;mero de usuarios concurrentes.
</para>
<para>
Claramente, tenemos que usar muchas transacciones de base de datos para implementar la transacci&#x00f3;n
de aplicaci&#x00f3;n. En este caso, mantener el aislamiento de los procesos de negocio se vuelve una
responsabilidad parcial de la capa de aplicaci&#x00f3;n. Una sola transacci&#x00f3;n de aplicaci&#x00f3;n usualmente
abarca varias transacciones de base de datos. Ser&#x00e1; at&#x00f3;mica si s&#x00f3;lo una de estas transacciones de
base de datos (la &#x00fa;ltima) almacena los datos actualizados, todas las otras simplemente leen datos
(por ejemplo, en un di&#x00e1;logo estilo-asistente abarcando muchos ciclos petici&#x00f3;n/respuesta).
Esto es m&#x00e1;s f&#x00e1;cil de implementar de lo que suena, especialmente si usas las funcionalidades de
Hibernate:
</para>
<itemizedlist>
<listitem>
<para>
<emphasis>Versionado Autom&#x00e1;tico</emphasis> - Hibernate puede llevar un control autom&#x00e1;tico de
concurrencia optimista por ti, puede detectar autom&#x00e1;ticamente si una modificaci&#x00f3;n concurrente
ha ocurrido durante el tiempo de pensar del usuario.
</para>
</listitem>
<listitem>
<para>
<emphasis>Objetos Separados</emphasis> - Si decides usar el ya discutido patr&#x00f3;n
de <emphasis>sesi&#x00f3;n-por-petici&#x00f3;n</emphasis>, todas las instancias cargadas estar&#x00e1;n
en estado separado durante el tiempo de pensar del usuario. Hibernate te permite
volver a unir los objetos y hacer persistentes las modificaciones. El patr&#x00f3;n se
llama <emphasis>sesi&#x00f3;n-por-petici&#x00f3;n-con-objetos-separados</emphasis>. Se usa
versionado autom&#x00e1;tico para aislar las modificaciones concurrentes.
</para>
</listitem>
<listitem>
<para>
<emphasis>Sesi&#x00f3;n Larga</emphasis> - La <literal>Session</literal> de Hibernate puede ser
desconectada de la conexi&#x00f3;n JDBC subyacente despu&#x00e9;s que se haya sido comprometida la
transacci&#x00f3;n de base de datos, y reconectada cuando ocurra una nueva petici&#x00f3;n del cliente.
Este patr&#x00f3;n es conocido como <emphasis>sesi&#x00f3;n-por-transacci&#x00f3;n-de-aplicaci&#x00f3;n</emphasis>
y hace la re-uni&#x00f3;n innecesaria. Para aislar las modificaciones concurrentes se usa el
versionado autom&#x00e1;tico.
</para>
</listitem>
</itemizedlist>
<para>
Tanto <emphasis>sesi&#x00f3;n-por-petici&#x00f3;n-con-objetos-separados</emphasis> como
<emphasis>sesi&#x00f3;n-por-transacci&#x00f3;n-de-aplicaci&#x00f3;n</emphasis>, ambas tienen
ventajas y desventajas, las discutimos m&#x00e1;s adelante en este cap&#x00ed;tulo en el contexto
del control optimista de concurrencia.
</para>
</sect2>
<sect2 id="transactions-basics-identity">
<title>Considerando la identidad del objeto</title>
<para>
Una aplicaci&#x00f3;n puede acceder concurrentemente a el mismo estado persistente en dos
<literal>Session</literal>s diferentes. Sin embargo, una instancia de una clase
persistente nunca se comparte entre dos instancias de <literal>Session</literal>.
Por lo tanto existen dos nociones diferentes de identidad:
</para>
<variablelist spacing="compact">
<varlistentry>
<term>Identidad de Base de Datos</term>
<listitem>
<para>
<literal>foo.getId().equals( bar.getId() )</literal>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Identidad JVM</term>
<listitem>
<para>
<literal>foo==bar</literal>
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
Entonces para objetos unidos a una <literal>Session</literal> <emphasis>en particular</emphasis>
(es decir en el &#x00e1;mbito de una <literal>Session</literal>) las dos nociones son equivalentes, y
la identidad JVM para la identidad de base de datos est&#x00e1; garantizada por Hibernate. Sin embargo,
mientras la aplicaci&#x00f3;n acceda concurrentemente al "mismo" (identidad persistente) objeto de negocio
en dos sesiones diferentes, las dos instancias ser&#x00e1;n realmente "diferentes" (identidad JVM).
Los conflictos se resuelven (con versionado autom&#x00e1;tico) en tiempo de limpieza (flush) usando un
enfoque optimista.
</para>
<para>
Este enfoque deja que Hibernate y la base de datos se preocupen sobre la concurrencia. Adem&#x00e1;s
provee la mejor escalabilidad, ya que garantizando la identidad un unidades de trabajo monohebra
no se necesitan bloqueos caros u otros medios de sincronizaci&#x00f3;n. La aplicaci&#x00f3;n nunca necesita
sincronizar sobre ning&#x00fa;n objeto de negocio, siempre que se apegue a una sola hebra por
<literal>Session</literal>. Dentro de una <literal>Session</literal> la aplicaci&#x00f3;n puede usar
con seguridad <literal>==</literal> para comparar objetos.
</para>
<para>
Sin embargo, una aplicaci&#x00f3;n que usa <literal>==</literal> fuera de una <literal>Session</literal>,
podr&#x00ed;a ver resultados inesperados. Esto podr&#x00ed;a ocurrir incluso en sitios algo inesperados,
por ejemplo, si pones dos instancias separadas dentro del mismo <literal>Set</literal>.
Ambas podr&#x00ed;an tener la misma identidad de base de datos (es decir, representar la misma fila),
pero la identidad JVM, por definici&#x00f3;n, no est&#x00e1; garantizada para las instancias en estado separado.
El desarrollador tiene que sobrescribir los m&#x00e9;todos <literal>equals()</literal> y
<literal>hashCode()</literal> en las clases persistentes e implementar su propia noci&#x00f3;n de igualdad
de objetos. Hay una advertencia: Nunca uses el identificador de base de datos para implementar
la igualdad, usa una clave de negocio, una combinaci&#x00f3;n de atributos &#x00fa;nicos, usualmente inmutables.
El identificador de base de datos cambiar&#x00e1; si un objeto transitorio es hecho persistente.
Si la instancia transitoria (usualmente junta a instancias separadas) es mantenida en un
<literal>Set</literal>, cambiar el c&#x00f3;digo hash rompe el contrato del <literal>Set</literal>.
Los atributos para las claves de negocio no tienen que ser tan estables como las claves primarias
de base de datos, s&#x00f3;lo tienes que garantizar estabilidad en tanto los objetos est&#x00e9;n en el mismo
<literal>Set</literal>. Mira el sitio web de Hibernate para una discusi&#x00f3;n m&#x00e1;s cuidadosa de este
tema. Nota tambi&#x00e9;n que &#x00e9;ste no es un tema de Hibernate, sino simplemente c&#x00f3;mo la identidad y la igualdad
de los objetos Java tiene que ser implementada.
</para>
</sect2>
<sect2 id="transactions-basics-issues">
<title>Temas comunes</title>
<para>
Nunca uses los antipatrones <emphasis>sesi&#x00f3;n-por-sesi&#x00f3;n-de-usuario</emphasis> o
<emphasis>sesi&#x00f3;n-por-aplicaci&#x00f3;n</emphasis> (por supuesto, hay raras excepciones a esta
regla). Nota que algunis de los siguientes temas podr&#x00ed;an tambi&#x00e9;n aparecer con los patrones
recomendados. Aseg&#x00fa;rate que entiendes las implicaciones antes de tomar una decisi&#x00f3;n de
dise&#x00f1;o:
</para>
<itemizedlist>
<listitem>
<para>
Una <literal>Session</literal> no es segura entre hebras. Las cosas que se suponen
que funcionan concurrentemente, como peticiones HTTP, beans de sesi&#x00f3;n, o workers de
Swing, provocar&#x00e1;n condiciones de competencia si una instancia de <literal>Session</literal>
fuese compartida. Si guardas tu <literal>Session</literal> de Hibernate en tu
<literal>HttpSession</literal> (discutido m&#x00e1;s adelante), debes considerar sincronizar
el acceso a tu sesi&#x00f3;n HTTP. De otro modo, un usuario que hace click lo suficientemente
r&#x00e1;pido puede llegar a usar la misma <literal>Session</literal> en dos hebras ejecut&#x00e1;ndose
concurrentemente.
</para>
</listitem>
<listitem>
<para>
Una excepci&#x00f3;n lanzada por Hibernate significa que tienes que deshacer (rollback) tu
transacci&#x00f3;n de base de datos y cerrar la <literal>Session</literal> inmediatamente
(discutido en m&#x00e1;s detalle luego). Si tu <literal>Session</literal> est&#x00e1; ligada a la
aplicaci&#x00f3;n, tienes que parar la aplicaci&#x00f3;n. Deshacer (rollback) la transacci&#x00f3;n de base
de datos no pone a tus objetos de vuelta al estado en que estaban al comienzo de la
transacci&#x00f3;n. Esto significa que el estado de la base de datos y los objetos de negocio
quedan fuera de sincron&#x00ed;a. Usualmente esto no es un problema, pues las excepciones no
son recuperables y tienes que volver a comenzar despu&#x00e9;s del rollback de todos modos.
</para>
</listitem>
<listitem>
<para>
La <literal>Session</literal> pone en cach&#x00e9; todo objeto que est&#x00e9; en estado persistente
(vigilado y chequeado por estado sucio por Hibernate). Esto significa que crece sin
fin hasta que obtienes una OutOfMemoryException, si la mantienes abierta por un largo
tiempo o simplemente cargas demasiados datos. Una soluci&#x00f3;n para esto es llamar a
<literal>clear()</literal> y <literal>evict()</literal> para gestionar el cach&#x00e9; de la
<literal>Session</literal>, pero probalemente debas considerar un procedimiento almacenado
si necesitas operaciones de datos masivas. Se muestran algunas soluciones en
<xref linkend="batch"/>. Mantener una <literal>Session</literal> abierta por la duraci&#x00f3;n
de una sesi&#x00f3;n de usuario significa tambi&#x00e9;n una alta probabilidad de datos a&#x00f1;ejos.
</para>
</listitem>
</itemizedlist>
</sect2>
</sect1>
<sect1 id="transactions-demarcation">
<title>Demarcaci&#x00f3;n de la transacci&#x00f3;n de base de datos</title>
<para>
Los l&#x00ed;mites de las transacciones de base de datos (o sistema) son siempre necesarios. Ninguna comunicaci&#x00f3;n
con la base de datos puede darse fuera de una transacci&#x00f3;n de base de datos (esto parece confundir muchos
desarrolladores acostumbrados al modo auto-commit). Siempre usa l&#x00ed;mites de transacci&#x00f3;n claros, incluso
para las operaciones de s&#x00f3;lo lectura. Dependiendo del nivel de aislamiento y las capacidades de base de
datos, esto podr&#x00ed;a o no ser requerido, pero no hay un merma si siempre demarcas expl&#x00ed;citamente
las transacciones.
</para>
<para>
Una aplicaci&#x00f3;n Hibernate puede ejecutarse en entornos no manejados (es decir, como independiente,
Web simple, o aplicaciones Swing) y entornos manejados J2EE. En un entorno no manejado, Hibernate es
usualmente responsable de su propio pool de conexiones de base de datos. El desarrollador de aplicaciones
tiene que establecer manualmente los l&#x00ed;mites de transacci&#x00f3;n, en otras palabras, hacer begin, commit, o
rollback las transacciones de base de datos por s&#x00ed; mismo. Un entorno manejado usualmente provee transacciones
gestionadas por contenedor, con el ensamble de transacci&#x00f3;n definido declarativamente en descriptores de
despliegue de beans de sesi&#x00f3;n EJB, por ejemplo. La demarcaci&#x00f3;n program&#x00e1;tica de transacciones no es m&#x00e1;s
necesario, incluso limpiar (flush) la <literal>Session</literal> es hecho autom&#x00e1;ticamente.
</para>
<para>
Sin embargo, frecuentemente es deseable mantener portable tu capa de persistencia. Hibernate ofrece
una API de envoltura llamada <literal>Transaction</literal> que se traduce al sistema de transacciones
nativo de tu entorno de despliegue. Esta API es realmente opcional, pero recomendamos fuertemente su uso
salvo que est&#x00e9;s en un bean de sesi&#x00f3;n CMT.
</para>
<para>
Usualmente, finalizar una <literal>Session</literal> implica cuatro fases distintas:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
limpiar (flush) la sesi&#x00f3;n
</para>
</listitem>
<listitem>
<para>
comprometer la transacci&#x00f3;n
</para>
</listitem>
<listitem>
<para>
cerrar la sesi&#x00f3;n
</para>
</listitem>
<listitem>
<para>
manejar excepciones
</para>
</listitem>
</itemizedlist>
<para>
Limpiar la sesi&#x00f3;n ha sido discutido anteriormente, tendremos ahora una mirada m&#x00e1;s de cerca
a la demarcaci&#x00f3;n de transacciones y manejo de excepciones en sendos entornos manejado y no manejados.
</para>
<sect2 id="transactions-demarcation-nonmanaged">
<title>Entorno no manejado</title>
<para>
Si una capa de persistencia Hibernate se ejecuta en un entorno no manejado, las conexiones
de base de datos son manejadas usualmente por el mecanismo de pooling de Hibernate. El idioma
manejo de sesi&#x00f3;n/transacci&#x00f3;n se ve as&#x00ed;:
</para>
<programlisting><![CDATA[//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();
}]]></programlisting>
<para>
No tienes que limpiar con <literal>flush()</literal> la <literal>Session</literal> expl&#x00ed;citamente -
la llamada a <literal>commit()</literal> autom&#x00e1;ticamente dispara la sincronizaci&#x00f3;n.
</para>
<para>
Una llamada a <literal>close()</literal> marca el fin de una sesi&#x00f3;n. La principal implicaci&#x00f3;n
de <literal>close()</literal> es que la conexi&#x00f3;n JDBC ser&#x00e1; abandonada por la sesi&#x00f3;n.
</para>
<para>
Este c&#x00f3;digo Java es portable y se ejecuta tanto en entornos no manejados como en entornos JTA.
</para>
<para>
Muy probablemente nunca veas este idioma en c&#x00f3;digo de negocio en una aplicaci&#x00f3;n normal;
las excepciones fatales (sistema) deben siempre ser capturadas en la "cima". En otras palabras,
el c&#x00f3;digo que ejecuta las llamadas de Hibernate (en la capa de persistencia) y el c&#x00f3;digo que
maneja <literal>RuntimeException</literal> (y usualmente s&#x00f3;lo puede limpiar y salir) est&#x00e1;n en
capas diferentes. Esto puede ser un desaf&#x00ed;o de dise&#x00f1;arlo t&#x00fa; mismo y debes usar los servicios
de contenedor J2EE/EJB en cuanto estuviesen disponibles. El manejo de excepciones se dicute
m&#x00e1;s adelante en este cap&#x00ed;tulo.
</para>
<para>
Nota que debes seleccionar <literal>org.hibernate.transaction.JDBCTransactionFactory</literal>
(que es el por defecto).
</para>
</sect2>
<sect2 id="transactions-demarcation-jta">
<title>Usando JTA</title>
<para>
Si tu capa de persistencia se ejecuta en un servidor de aplicaciones (por ejemplo, detr&#x00e1;s
de beans de sesi&#x00f3;n EJB), cada conexi&#x00f3;n de datasource obtenida por Hibernate ser&#x00e1; parte
autom&#x00e1;ticamente de la transacci&#x00f3;n JTA global. Hibernate ofrece dos estrategias para esta
integraci&#x00f3;n.
</para>
<para>
Si usas transacciones gestionadas-por-bean (BMT) Hibernate le dir&#x00e1; al servidor de aplicaciones
que comience y finalice una transacci&#x00f3;n BMT si usas la API de <literal>Transaction</literal>.
De modo que, el c&#x00f3;digo de gesti&#x00f3;n de la transacci&#x00f3;n es id&#x00e9;ntico al de un entorno no manejado.
</para>
<programlisting><![CDATA[// 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();
}]]></programlisting>
<para>
Con CMT, la demarcaci&#x00f3;n de la transacci&#x00f3;n se hace en descriptores de despliegue de beans de sesi&#x00f3;n,
no program&#x00e1;ticamente. Si no quieres limpiar (flush) y cerrar manualmente la <literal>Session</literal>
por ti mismo, solamente establece <literal>hibernate.transaction.flush_before_completion</literal> a
<literal>true</literal>, <literal>hibernate.connection.release_mode</literal> a
<literal>after_statement</literal> o <literal>auto</literal> y
<literal>hibernate.transaction.auto_close_session</literal> a <literal>true</literal>. Hibernate
limpiar&#x00e1; y cerrar&#x00e1; entonces autom&#x00e1;ticamente la <literal>Session</literal> para ti. Lo &#x00fa;nico que resta
es deshacer (rollback) la transacci&#x00f3;n cuando ocurra una excepci&#x00f3;n. Afortunadamente, en un bean CMT,
incluso esto ocurre autom&#x00e1;ticamente, ya que una <literal>RuntimeException</literal> no manejada
disparada por un m&#x00e9;todo de un bean de sesi&#x00f3;n le dice al contenedor que ponga a deshacer la transacci&#x00f3;n
global. <emphasis>Esto significa que, en CMT, no necesitas usar en absoluto la API de
<literal>Transaction</literal> de Hibernate.</emphasis>
</para>
<para>
Nota que debes elegir <literal>org.hibernate.transaction.JTATransactionFactory</literal> en un
bean de sesi&#x00f3;n BMT, y <literal>org.hibernate.transaction.CMTTransactionFactory</literal> en un
bean de sesi&#x00f3;n CMT, cuando configures la f&#x00e1;brica de transacciones de Hibernate. Recuerda adem&#x00e1;s
establecer <literal>org.hibernate.transaction.manager_lookup_class</literal>.
</para>
<para>
Si trabajas en un entorno CMT, y usas limpieza (flushing) y cierre autom&#x00e1;ticos de la sesi&#x00f3;n,
podr&#x00ed;as querer tambi&#x00e9;n usar la misma sesi&#x00f3;n en diferentes partes de tu c&#x00f3;digo. T&#x00ed;picamente,
en un entorno no manejado, usar&#x00ed;as una variable <literal>ThreadLocal</literal> para tener la sesi&#x00f3;n,
pero una sola petici&#x00f3;n de EJB puede ejecutarse en diferentes hebras (por ejemplo, un bean de sesi&#x00f3;n
llamando a otro bean de sesi&#x00f3;n). Si no quieres molestarte en pasar tu <literal>Session</literal>
por alrededor, la <literal>SessionFactory</literal> provee el m&#x00e9;todo
<literal>getCurrentSession()</literal>, que devuelve una sesi&#x00f3;n que est&#x00e1; pegada al contexto de
transacci&#x00f3;n JTA. &#x00a1;Esta es la forma m&#x00e1;s f&#x00e1;cil de integrar Hibernate en una aplicaci&#x00f3;n!
La sesi&#x00f3;n "actual" siempre tiene habilitados limpieza, cierre y liberaci&#x00f3;n de conexi&#x00f3;n autom&#x00e1;ticos
(sin importar la configuraci&#x00f3;n de las propiedades anteriores). Nuestra idioma de gesti&#x00f3;n de
sesi&#x00f3;n/transacci&#x00f3;n se reduce a:
</para>
<programlisting><![CDATA[// CMT idiom
Session sess = factory.getCurrentSession();
// do some work
...
]]></programlisting>
<para>
En otras palabras, todo lo que tienes que hacer en un entorno manejado, es llamar a
<literal>SessionFactory.getCurrentSession()</literal>, hacer tu trabajo de acceso a datos,
y dejar el resto al contenedor. Los l&#x00ed;mites de transacci&#x00f3;n se establecen declarativamente
en los descriptores de despliegue de tu bean de sesi&#x00f3;n. El ciclo de vida de la sesi&#x00f3;n es
manejado completamente por Hibernate.
</para>
<para>
Existe una advertencia al uso del modo de liberaci&#x00f3;n de conexi&#x00f3;n <literal>after_statement</literal>.
Debido a una limitaci&#x00f3;n tonta de la especificaci&#x00f3;n de JTA, no es posible para Hibernate
limpiar autom&#x00e1;ticamente ning&#x00fa;n <literal>ScrollableResults</literal> no cerrado ni
instancias de <literal>Iterator</literal> devueltas por <literal>scroll()</literal> o
<literal>iterate()</literal>. <emphasis>Debes</emphasis> liberar el cursor de base de datos
subyacente llamando a <literal>ScrollableResults.close()</literal> o
<literal>Hibernate.close(Iterator)</literal> expl&#x00ed;citamente desde un bloque <literal>finally</literal>.
(Por supuesto, la mayor&#x00ed;a de las aplicaciones pueden evitarlo f&#x00e1;cilmente no usando en absoluto ning&#x00fa;n
<literal>scroll()</literal> o <literal>iterate()</literal> desde el c&#x00f3;digo CMT.)
</para>
</sect2>
<sect2 id="transactions-demarcation-exceptions">
<title>Manejo de excepciones</title>
<para>
Si la <literal>Session</literal> lanza una excepci&#x00f3;n (incluyendo cualquier
<literal>SQLException</literal>), debes inmediatamente deshacer (rollback) la
transacci&#x00f3;n de base de datos, llamar a <literal>Session.close()</literal> y
descartar la instancia de <literal>Session</literal>. Ciertos m&#x00e9;todos de
<literal>Session</literal> <emphasis>no</emphasis> dejar&#x00e1;n la sesi&#x00f3;n en un
estado consistente. Ninguna excepci&#x00f3;n lanzada por Hibernate puede ser tratada
como recuperable. Aseg&#x00fa;rate que la <literal>Session</literal> sea cerrada llamando
a <literal>close()</literal> en un bloque <literal>finally</literal>.
</para>
<para>
La <literal>HibernateException</literal>, que envuelve la mayor&#x00ed;a de los errores que
pueden ocurrir en la capa de persistencia de Hibernate, en una excepci&#x00f3;n no chequeada
(no lo era en versiones anteriores de Hibernate). En nuestra opini&#x00f3;n, no debemos forzar
al desarrollador de aplicaciones a capturar una excepci&#x00f3;n irrecuperable en una capa baja.
En la mayor&#x00ed;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&#x00e9;todos (es decir, en las capas
m&#x00e1;s altas) y se presenta un mensaje de error al usuario de la aplicaci&#x00f3;n (o se toma alguna
otra acci&#x00f3;n apropiada). Nota que Hibernate podr&#x00ed;a tambi&#x00e9;n lanzar otras excepciones no chequeadas
que no sean una <literal>HibernateException</literal>. Una vez m&#x00e1;s, no son recuperables y debe
tomarse una acci&#x00f3;n apropiada.
</para>
<para>
Hibernate envuelve <literal>SQLException</literal>s lanzadas mientras se interact&#x00fa;a con la base
de datos en una <literal>JDBCException</literal>. De hecho, Hibernate intentar&#x00e1; convertir la excepci&#x00f3;n
en una subclase de <literal>JDBCException</literal> m&#x00e1;s significativa. La <literal>SQLException</literal>
est&#x00e1; siempre disponible v&#x00ed;a <literal>JDBCException.getCause()</literal>. Hibernate convierte la
<literal>SQLException</literal> en una subclase de <literal>JDBCException</literal> apropiada usando
el <literal>SQLExceptionConverter</literal> adjunto a la <literal>SessionFactory</literal>. Por defecto,
el <literal>SQLExceptionConverter</literal> est&#x00e1; definido para el dialecto configurado; sin embargo,
es tambi&#x00e9;n posible enchufar una implementaci&#x00f3;n personalizada (ver los javadocs de la clase
<literal>SQLExceptionConverterFactory</literal> para los detalles). Los subtipos est&#x00e1;ndar de
<literal>JDBCException</literal> son:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
<literal>JDBCConnectionException</literal> - indica un error con la comunicaci&#x00f3;n JDBC subyacente.
</para>
</listitem>
<listitem>
<para>
<literal>SQLGrammarException</literal> - indica un problema de gram&#x00e1;tica o sint&#x00e1;xis con el
SQL publicado.
</para>
</listitem>
<listitem>
<para>
<literal>ConstraintViolationException</literal> - indica alguna forma de violaci&#x00f3;n de restricci&#x00f3;n
de integridad.
</para>
</listitem>
<listitem>
<para>
<literal>LockAcquisitionException</literal> - indica un error adquiriendo un nivel de bloqueo
necesario para realizar una operaci&#x00f3;n solicitada.
</para>
</listitem>
<listitem>
<para>
<literal>GenericJDBCException</literal> - una excepci&#x00f3;n gen&#x00e9;rica que no cay&#x00f3; en ninguna de las
otras categor&#x00ed;as.
</para>
</listitem>
</itemizedlist>
</sect2>
</sect1>
<sect1 id="transactions-optimistic">
<title>Control optimista de concurrencia</title>
<para>
El &#x00fa;nico enfoque que es consistente con alta concurrencia y alta escalabilidad es el control
optimista de concurrencia con versionamiento. El chuequeo de versi&#x00f3;n usa n&#x00fa;meros de versi&#x00f3;n,
o timestamps, para detectar actualizaciones en conflicto (y para prevenir actualizaciones perdidas).
Hibernate provee para tres enfoques posibles de escribir c&#x00f3;digo de aplicaci&#x00f3;n que use concurrencia
optimista. Los casos de uso que hemos mostrado est&#x00e1;n en el contexto de transacciones de aplicaci&#x00f3;n
largas pero el chequeo de versiones tiene adem&#x00e1;s el beneficio de prevenir actualizaciones perdidas
en transacciones de base de datos solas.
</para>
<sect2 id="transactions-optimistic-manual">
<title>Chequeo de versiones de aplicaci&#x00f3;n</title>
<para>
En una implementaci&#x00f3;n sin mucha ayuda de Hibernate, cada interacci&#x00f3;n con la base de datos ocurre en una
nueva <literal>Session</literal> y el desarrollador es responsable de recargar todas las intancias
persistentes desde la base de datos antes de manipularlas. Este enfoque fuerza a la aplicaci&#x00f3;n a
realizar su propio chequeo de versiones para asegurar el aislamiento de transacciones de base de datos.
Es el enfoque m&#x00e1;s similar a los EJBs de entidad.
</para>
<programlisting><![CDATA[// 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();]]></programlisting>
<para>
La propiedad <literal>version</literal> se mapea usando <literal>&lt;version&gt;</literal>,
e Hibernate la incrementar&#x00e1; autom&#x00e1;ticamente durante la limpieza si la entidad est&#x00e1; sucia.
</para>
<para>
Por supuesto, si est&#x00e1;s operando un entorno de baja-concurrencia-de-datos y no requieres
chequeo de versiones, puedes usar este enfoque y simplemente saltar el chequeo de versiones.
En ese caso, <emphasis>el &#x00fa;ltimo compromiso (commit) gana</emphasis> ser&#x00e1; la estrategia por
defecto para tus transacciones de aplicaci&#x00f3;n largas. Ten en mente que esto podr&#x00ed;a confundir
a los usuarios de la aplicaci&#x00f3;n, pues podr&#x00ed;an experimentar actualizaciones perdidas sin
mensajes de error ni chance de fusionar los cambios conflictivos.
</para>
<para>
Claramente, el chequeo manual de versiones es factible solamente en circunstancias muy triviales,
y no es pr&#x00e1;ctico para la mayor&#x00ed;a de aplicaciones. Frecuentemente, no s&#x00f3;lo intancias solas, sino grafos
completos de objetos modificados tienen que ser chequeados. Hibernate ofrece chequeo de versiones
autom&#x00e1;tico con el paradigma de dise&#x00f1;o de <literal>Session</literal> larga o de instancias separadas.
</para>
</sect2>
<sect2 id="transactions-optimistic-longsession">
<title>Sesi&#x00f3;n larga y versionado autom&#x00e1;tico</title>
<para>
Una sola instancia de <literal>Session</literal> y sus instancias persistentes
son usadas para toda la transacci&#x00f3;n de aplicaci&#x00f3;n. Hibernate chequea las versiones
de instancia en el momento de limpieza (flush), lanzando una excepci&#x00f3;n si se detecta
una modificaci&#x00f3;n concurrente. Concierne al desarrollador capturar y manejar esta excepci&#x00f3;n
(las opciones comunes son la oportunidad del usuario de fusionar los cambios, o recomenzar el
proceso de negocio sin datos a&#x00f1;ejos).
</para>
<para>
La <literal>Session</literal> se desconecta de cualquier conexi&#x00f3;n JDBC subyacente
al esperar por una interacci&#x00f3;n del usuario. Este enfoque es el m&#x00e1;s eficiente en t&#x00e9;rminos
de acceso a base de datos. La aplicaci&#x00f3;n no necesita tratar por s&#x00ed; misma con el chequeo de
versiones, ni re-uniendo instancias separadas, ni tiene que recargar instancias en cada
transacci&#x00f3;n de base de datos.
</para>
<programlisting><![CDATA[// foo is an instance loaded earlier by the Session
session.reconnect(); // Obtain a new JDBC connection
Transaction t = session.beginTransaction();
foo.setProperty("bar");
t.commit(); // End database transaction, flushing the change and checking the version
session.disconnect(); // Return JDBC connection ]]></programlisting>
<para>
El objeto <literal>foo</literal> todav&#x00ed;a conoce en qu&#x00e9; <literal>Session</literal> fue cargado.
<literal>Session.reconnect()</literal> obtiene una nueva conexi&#x00f3;n (o puedes proveer una) y
reasume la sesi&#x00f3;n. El m&#x00e9;todo <literal>Session.disconnect()</literal> desconectar&#x00e1; la sesi&#x00f3;n
de la conexi&#x00f3;n JDBC y la devolver&#x00e1; la conexi&#x00f3;n al pool (a menos que hayas provisto la conexi&#x00f3;n).
Despu&#x00e9;s de la reconexi&#x00f3;n, para forzar un chequeo de versi&#x00f3;n en datos que no est&#x00e9;s actualizando,
puedes llamar a <literal>Session.lock()</literal> con <literal>LockMode.READ</literal> sobre
cualquier objeto que pudiese haber sido actualizado por otra transacci&#x00f3;n. No necesitas bloquear
ning&#x00fa;n dato que <emphasis>s&#x00ed; est&#x00e9;s</emphasis> actualizando.
</para>
<para>
Si las llamadas expl&#x00ed;citas a <literal>disconnect()</literal> y <literal>reconnect()</literal>
son muy onerosas, puedes usar en cambio <literal>hibernate.connection.release_mode</literal>.
</para>
<para>
Este patr&#x00f3;n es problem&#x00e1;tico si la <literal>Session</literal> es demasiado grande para ser almacenada
durante el tiempo de pensar del usuario, por ejemplo, una <literal>HttpSession</literal> debe
mantenerse tan peque&#x00f1;a como sea posible. Ya que la <literal>Session</literal> es tambi&#x00e9;n el cach&#x00e9;
(obligatorio) de primer nivel y contiene todos los objetos cargados, podemos probablemente cargar
esta estrategia s&#x00f3;lo para unos pocos ciclos petici&#x00f3;n/respuesta. Esto est&#x00e1; de hecho recomendado, ya que
la <literal>Session</literal> tendr&#x00e1; pronto tambi&#x00e9;n datos a&#x00f1;ejos.
</para>
<para>
Nota tambi&#x00e9;n que debes mantener la <literal>Session</literal> desconectada pr&#x00f3;xima a la capa
de persistencia. En otras palabras, usa una sesi&#x00f3;n de EJB con estado para tener la
<literal>Session</literal> y no transferirla a la capa web para almacenarla en la
<literal>HttpSession</literal> (ni incluso serializarla a una capa separada).
</para>
</sect2>
<sect2 id="transactions-optimistic-detached">
<title>Objetos separados y versionado autom&#x00e1;tico</title>
<para>
Cada interacci&#x00f3;n con el almac&#x00e9;n persistente ocurre en una nueva <literal>Session</literal>.
Sin embargo, las mismas instancias persistentes son reusadas para cada interacci&#x00f3;n con la base de
datos. La aplicaci&#x00f3;n manipula el estado de las instancias separadas originalmente cargadas en otra
<literal>Session</literal> y luego las readjunta usando <literal>Session.update()</literal>,
<literal>Session.saveOrUpdate()</literal>, o <literal>Session.merge()</literal>.
</para>
<programlisting><![CDATA[// foo is an instance loaded by a previous Session
foo.setProperty("bar");
session = factory.openSession();
Transaction t = session.beginTransaction();
session.saveOrUpdate(foo); // Use merge() if "foo" might have been loaded already
t.commit();
session.close();]]></programlisting>
<para>
De nuevo, Hibernate chequear&#x00e1; las versiones de instancia durante la limpieza (flush),
lanzando una excepci&#x00f3;n si ocurrieron actualizaciones en conflicto.
</para>
<para>
Puedes tambi&#x00e9;n llamar a <literal>lock()</literal> en vez de <literal>update()</literal>
y usar <literal>LockMode.READ</literal> (realizando un chequeo de versi&#x00f3;n, puenteando
todos los cach&#x00e9;s) si est&#x00e1;s seguro que el objeto no ha sido modificado.
</para>
</sect2>
<sect2 id="transactions-optimistic-customizing">
<title>Personalizando el versionado autom&#x00e1;tico</title>
<para>
Puedes deshabilitar el incremento de versi&#x00f3;n autom&#x00e1;tico de Hibernate para propiedades en particular
y colecciones estableciendo el atributo de mapeo <literal>optimistic-lock</literal> a
<literal>false</literal>. Hibernate entonces no incrementar&#x00e1; ya m&#x00e1;s las versiones si la propiedad est&#x00e1;
sucia.
</para>
<para>
Los esquemas de base de datos heredados son frecuentemente est&#x00e1;ticos y no pueden ser modificados.
U otras aplicaciones podr&#x00ed;an tambi&#x00e9;n acceder la misma base de datos y no saber c&#x00f3;mo manejar los n&#x00fa;meros
de versi&#x00f3;n ni incluso timestamps. 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&#x00f3;n o
timestamp, con una comparaci&#x00f3;n del estado de todos los campos en una fila, activa
<literal>optimistic-lock="all"</literal> en el mapeo de <literal>&lt;class&gt;</literal>.
Nota que esto conceptualmente funciona solamente si Hibernate puede comparar el estado viejo y nuevo,
es decir, si usas una sola <literal>Session</literal> larga y no
sesi&#x00f3;n-por-petici&#x00f3;n-con-instancias-separadas.
</para>
<para>
A veces las modificaciones concurrentes pueden permitirse, en cuanto los cambios que hayan sido
hechos no se traslapen. Si estableces <literal>optimistic-lock="dirty"</literal> al mapear la
<literal>&lt;class&gt;</literal>, Hibernate s&#x00f3;lo comparar&#x00e1; los campos sucios durante la limpieza.
</para>
<para>
En ambos casos, con columnas de versi&#x00f3;n/timestamp dedicadas o con comparaci&#x00f3;n de campos
completa/sucios, Hibernate usa una sola sentencia <literal>UPDATE</literal>
(con una cl&#x00e1;usula <literal>WHERE</literal> apropiada) por entidad para ejecutar el chequeo
de versiones y actualizar la informaci&#x00f3;n. Si usas persistencia transitiva para la re-uni&#x00f3;n
en cascada de entidades asociadas, Hibernate podr&#x00ed;a ejecutar actualizaciones innecesarias.
Esto usualmente no es un problema, pero podr&#x00ed;an ejecutarse disparadores (triggers)
<emphasis>on update</emphasis> en la base de datos incluso cuando no se haya hecho ning&#x00fa;n cambio
a las instancias separadas. Puedes personalizar este comportamiento estableciendo
<literal>select-before-update="true"</literal> en el mapeo de <literal>&lt;class&gt;</literal>,
forzando a Hibernate a <literal>SELECT</literal> la instancia para asegurar que las actualizaciones
realmente ocurran, antes de actualizar la fila.
</para>
</sect2>
</sect1>
<sect1 id="transactions-locking">
<title>Bloqueo pesimista</title>
<para>
No se pretende que los usuarios gasten mucho tiempo preocup&#x00e1;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 pueden
a veces obtener bloqueos exclusivos pesimistas, o reobtener bloqueos al comienzo de una nueva
transacci&#x00f3;n.
</para>
<para>
&#x00a1;Hibernate siempre usar&#x00e1; el mecanismo de bloqueo de la base de datos, nunca bloqueo
de objetos en memoria!
</para>
<para>
La clase <literal>LockMode</literal> define los diferentes niveles de bloqueo que pueden ser adquiridos
por Hibernate. Un bloqueo se obtiene por los siguientes mecanismos:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
<literal>LockMode.WRITE</literal> se adquiere autom&#x00e1;ticamente cuando Hibernate actualiza o
inserta una fila.
</para>
</listitem>
<listitem>
<para>
<literal>LockMode.UPGRADE</literal> puede ser adquirido bajo petici&#x00f3;n expl&#x00ed;cita del usuario
usando <literal>SELECT ... FOR UPDATE</literal> en base de datos que soporten esa sint&#x00e1;xis.
</para>
</listitem>
<listitem>
<para>
<literal>LockMode.UPGRADE_NOWAIT</literal> puede ser adquirido bajo petici&#x00f3;n expl&#x00ed;cita del usuario
usando un <literal>SELECT ... FOR UPDATE NOWAIT</literal> bajo Oracle.
</para>
</listitem>
<listitem>
<para>
<literal>LockMode.READ</literal> es adquirido autom&#x00e1;ticamente cuando Hibernate lee datos
bajo los niveles de aislamiento Repeatable Read o Serializable. Puede ser readquirido por
pedido expl&#x00ed;cito del usuario.
</para>
</listitem>
<listitem>
<para>
<literal>LockMode.NONE</literal> representa la ausencia de un bloqueo. Todos los objetos se pasan
a este modo de bloqueo al final de una <literal>Transaction</literal>. Los objetos asociados con una
sesi&#x00f3;n v&#x00ed;a una llamada a <literal>update()</literal> o <literal>saveOrUpdate()</literal> tambi&#x00e9;n
comienzan en este modo de bloqueo.
</para>
</listitem>
</itemizedlist>
<para>
La "petici&#x00f3;n expl&#x00ed;cita del usuario" se expresa en una de las siguientes formas:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
Una llamada a <literal>Session.load()</literal>, especificando un <literal>LockMode</literal>.
</para>
</listitem>
<listitem>
<para>
Una llamada a <literal>Session.lock()</literal>.
</para>
</listitem>
<listitem>
<para>
Una llamada a <literal>Query.setLockMode()</literal>.
</para>
</listitem>
</itemizedlist>
<para>
Si se llama a <literal>Session.load()</literal> con <literal>UPGRADE</literal> o
<literal>UPGRADE_NOWAIT</literal>, y el objeto pedido no ha sido a&#x00fa;n cargado por la sesi&#x00f3;n, el objeto es
cargado usando <literal>SELECT ... FOR UPDATE</literal>. Si se llama a <literal>load()</literal> para
un objeto que ya est&#x00e9; cargado con un bloqueo menos restrictivo que el pedido, Hibernate llama a
<literal>lock()</literal> para ese objeto.
</para>
<para>
<literal>Session.lock()</literal> realiza un chequeo de n&#x00fa;mero de versi&#x00f3;n si el modo de bloqueo especificado
es <literal>READ</literal>, <literal>UPGRADE</literal> o <literal>UPGRADE_NOWAIT</literal>. (En el caso de
<literal>UPGRADE</literal> o <literal>UPGRADE_NOWAIT</literal>, se usa
<literal>SELECT ... FOR UPDATE</literal>.)
</para>
<para>
Si la base de datos no soporta el modo de bloqueo solicitado, Hibernate usar&#x00e1; un modo alternativo
apropiado (en vez de lanzar una excepci&#x00f3;n). Esto asegura que las aplicaciones ser&#x00e1;n portables.
</para>
</sect1>
</chapter>