Transacciones en hibernate
#Transacciones en spring framework
Checked exception: No causa rollback.
Runtime exception (o subclases): Lanza rollback
Estas reglas se definieron así pensando que en las checked exceptions el usuario tiene posibilidad de hacer algo (el rollback debe ser manual).
Se pueden cambiar los comportamientos descritos anteriormente cambiando el comportamiento default y utilizando en algunos casos aspectj.
Las transacciones locales involucran un mismo recurso (p.e. la misma base de datos), las transacciones globales o distribuidas involucran varios recursos (p.e. múltiples bases de datos de distintos proveedores).
Las transacciones también agrupan operaciones de acceso a datos, de hecho cualquier sentencia SQL, sean queries o DML, debe ser ejecutada dentro de una transacción de base de datos. No puede haber comunicación con la base de datos si no existe una transacción. Por lo tanto 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.
Un método para solventar esto (para no estar creando tx por cada operación), es el modo autocommit true
donde cualquier sentencia simple es envuelta en una transacción muy corta. Este modo nunca es apropiado para una aplicación, solo se utiliza para la ejecución de comandos en una consola. Hibernate deshabilita o espera que el entorno (J2EE/JEE) deshabilite el modo autocommit.
El método correcto para las aplicaciones es marcar autocommit=false
y definir explicitamente dónde empieza y dónde hacer commit/rollback de la transacción (ya sea con anotaciones o programáticamente).
Ninguna sentencia sql puede ser lanzada hacia la base de datos si no existe una transacción de base de datos. El término acceso de datos no transaccional (notransactional data access) significa que no hay una transacción definida a nivel de sistema por lo tanto se utiliza el modo autocommit=true
. Esto no significa que estén involucradas transacciones de base de datos en el acceso de datos no transaccional.
La recomendación es no utilizar el modo autocommit=true
en una aplicación, y aplicar transacciones read-only solo cuando exista un beneficio obvio en performance o cuando se desee indicar explicitamente que el método no escribe hacia la base de datos. Siempre prefiera transacciones regulares para agrupar las operaciones de acceso a datos sin importar si lee o escribe datos.
Considere el siguiente código:
Session session = getSessionFactory().openSession();
session.get(Item.class, 123l);
session.close();
Lo que sucede es lo siguiente:
setAutoCommit(false)
. Esto efectivamente comienza una transacción JDBC.conn.close()
). Pero qué pasa con la transacción uncommited?La respuesta a la pregunta es ¡depende!. La especificación JDBC no dice nada sobre qué hacer con las transacciones pendientes cuando se invoca close() sobre una conexión. Lo que sucede depende del vendedor que implementa la especificación. El driver JDBC de oracle, por ejemplo hace commit de la transacción cuando se invoca a close(). Muchos otros vendedores JDBC toman el camino más sano y hacen rollback de cualquier transacción pendiente cuando la conexión es cerrada y el recurso regresado al pool. Obviamente, esto no es un problema para la sentencia SELECT pero veamos otra variación:
Session session = getSessionFactory().openSession();
Long generatedId = session.save(item);
session.close();
Este código resulta en una sentencia INSERT, ejecutado dentro de una transacción que nunca es commited o rollback. En oracle, esta pieza de código inserta los datos permanentemente, en otras bases de datos, podría no hacerlo!.
De hecho aún no hemos tocado el modo autocommit, solo se ha puesto de manifiesto un problema que puede aparecer wi intenta trabajar sin trabajar explicitamente con transacciones. Supongamos que usted aún piensa que trabajar sin transacciones es buena idea y desea trabajar con el modo autocommit. Primero, usted necesita decirle a hibernate que debe permitir conexiones con autocommit:
<property name="connection.autocommit">true</property>
Con este parámetro, Hibernate ya no apagará el modo autocommit cuando una conexión sea obtenida del pool. Los ejemplos previos ahora trabajarán de manera predecible, y el driver JDBC englobará las sentencias SQL en una transacción corta --con las implicaciones que se ha platicado anteriormente.
En resumen, se recomienda utilizar siempre transacciones regulares para realizar operaciones hacia la base de datos sin importar si son de lectura o escritura.
Haciendo la transacción readonly se deshabilita el dirty checking (y por lo tanto su costo en performance).
@Transactional(readOnly=true)
public void someBusinessMethod() {
....
}
Marca la transacción actual como rollback. Una transacción marcada como rollback será deshecha y nunca puede ser commited.
Cuando una transacción se marca como setRollbackOnly afecta a las transacciones externas que se unieron a la transacción y serán deshechas (rollback).
No es común llamar a setRollbackOnly()
manualmente, a menudo se activa por automático cuando se lanza una RuntimeException.
When Spring loads your bean definitions, and has been configured to look for @Transactional annotations, it will create these proxy objects around your actual bean. These proxy objects are instances of classes that are auto-generated at runtime. The default behaviour of these proxy objects when a method is invoked is just to invoke the same method on the "target" bean (i.e. your bean).
However, the proxies can also be supplied with interceptors, and when present these interceptors will be invoked by the proxy before it invokes your target bean's method. For target beans annotated with @Transactional, Spring will create a TransactionInterceptor, and pass it to the generated proxy object. So when you call the method from client code, you're calling the method on the proxy object, which first invokes the TransactionInterceptor (which begins a transaction), which in turn invokes the method on your target bean. When the invocation finishes, the TransactionInterceptor commits/rolls back the transaction. It's transparent to the client code.
As for the "external method" thing, if your bean invokes one of its own methods, then it will not be doing so via the proxy. Remember, Spring wraps your bean in the proxy, your bean has no knowledge of it. Only calls from "outside" your bean go through the proxy.
From http://stackoverflow.com/questions/1099025/spring-transactional-what-happens-in-background
It's a limitation with Spring AOP. (dynamic objects and CGLIB). If you configure Spring to use AspectJ to handle the transactions, your code will work.
The problem here is, that Spring's AOP proxies don't extend but rather wrap your service instance to intercept calls. This has the effect, that any call to "this" from within your service instance is directly invoked on that instance and cannot be intercepted by the wrapping proxy (the proxy is not even aware of any such call).
El comportamiento default de esos objetos proxy cuando un método es invocado es invocar el mismo método sobre el target bean, por tal motivo this
siempre será del tipo de la clase en la que se encuentra y nunca del proxy (aunque el método sea invocado a través del proxy).
Por tal motivo se omite @Transactional
en los métodos que se invocan de la misma clase (self-invocations) ya que se están invocando a través de un objeto que no es el proxy. Para llamar un método dentro de la misma clase y tome en cuenta @Transactional
hay que inyectar un objeto de la misma clase e invocar el método a través de ella.
https://developer.jboss.org/wiki/Non-transactionaldataaccessandtheauto-commitmode
http://www.codesenior.com/tutorial/Hibernate-Session-Commit-Rollback-Save-Concepts