If we go with programmatic transaction, we write
Session session=sessiongFactory.openSession();
Transaction tx=session.buildTransaction();
And for a session we can build as many transaction we want.
So, We have first session object than we get Transaction Object.
While in Declarative Transaction,If we declarative #Transaction annotation at service level.
"When this Service Method will be called,Transaction will be Open" so here there is not any inforamtion about Session.
Then in Dao we write
Session session=sessiongFactory.getCurrentSession();
Here we have first Transation then Session,
Can any one please help me in understanding ,How spring manages this Declarative Transaction.
According to documentation method sessiongFactory.getCurrentSession() obtains the current session, and"current session" means controlled by the CurrentSessionContext impl configured for use.
Documentation also provides this explanation for backwards compatibility: if a CurrentSessionContext is not configured but a JTA TransactionManagerLookup is configured, this will default to the JTASessionContext impl.
JTASessionContext implementation will generate Sessions as needed provided a JTA transaction is in effect. If a session is not already associated with the current JTA transaction at the time currentSession() is called, a new session will be opened and it will be associated with that JTA transaction.
With Spring declarative transaction management you can apply #Transactional at both method & class level.
It is enabled via AOP proxies. The combination of AOP with transactional metadata yields an AOP proxy that uses a TransactionInterceptor in conjunction with an appropriate PlatformTransactionManager implementation to drive transactions around method invocations.
Conceptually, calling a method on a transactional proxy looks like this…
When using proxies, you should apply the #Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the #Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings.
All transactions are associated with the session. Transactions are initiated on the service layer but they have to be associated with a session to be committed. First transaction completes then session is closed. A session can also span several transactions. If you are using hibernate, spring uses hibernate managed transaction manager which is responsible for associating transactions with hibernate session.
Spring transaction management abstract the transaction handling and decouples the transaction demarcation logic (e.g. #Transactional) from the actual transaction manager (e.g. RESOURCE_LOCAL, JTA).
The problem with programmatic transaction is that you tie your application code to the transaction management logic. On the other hand, Spring allows you to switch from JpaTransactionManager to JtaTransactionManager with just some configuration (no need to change the application code).
Spring only creates a transaction context that's used by the inner TransactionInterceptor to execute the actual transaction management hooks.
For RESOURCE_LOCAL, transactions are handled through the JDBC Connection commit() and rollback() methods.
For JTA, transactions are handled through the JTA UserTransaction commit() and rollback() methods.
Everything is explained in the docs. You may also want to take a look at Spring integration with ORM.
Basically, Spring creates a proxy which intercepts the transactional methods invocation and starts the transaction before delegating the call to the target method, and ends the transaction when the target method returns.
Related
I have an java applciation that uses both Hibernate and Spring, and also Hibernate session's programmatical transaction control and Spring's #Transactional automatic commit
I need to handle onFlush hibernate event, so with hibernate session it works, but when Spring makes #Transactional annotated methods automatic commit it fails.
I register hibernate event listeners:
EventListenerRegistry registry = sessionFactoryImpl.getServiceRegistry().getService(EventListenerRegistry.class);
registry.appendListeners(EventType.SAVE, this);
registry.appendListeners(EventType.SAVE_UPDATE, this);
registry.appendListeners(EventType.MERGE, this);
and the I do something like
session.save(entity), event listeners work as expected;
when I call Spring repository.save(), it also works
But when Spring makes #Transactional annotated methods automatic commit without calling to repository.save(), I don't enter hibernate event listener.
Can anyone pls help how to deal with it?
For example I have #Stateless java bean:
#Stateless(mappedName = "test")
public class Test implements ITest
{
#Override
public void updateActivity
(SomeObj activity)
throws Exception
{
em.persist(activity);
}
}
Because it's a container-managed bean, then tell me, when does the container decide to synchronize the context with a DB? In this case I immediately see the results in the DB, but sometimes they do not seem to immediately appear there, right?
Please explain me How the synchronization works with the context and the DB at Container-Managed mode? When does the container decide to synchronize the context with a DB?
This will be driven by the transaction propagation configuration as your EJB bean might be one of many managed beans participating in a single transaction. This gets more complex if there are multiple transaction sources in flight e.g. XA 2PC. Generally, the changes will be flushed into the database on transaction commit however this further depends on the transaction isolation level or the presence of savepoints when nested transactions are used.
Check the #TransactionAttribute annotation docs or look for a tutorial that explains transaction propagation.
Assume that we go inside a method and start a transaction in main thread. In this method, there are some async methods so we created 2 more threads inside this method;
Thread 1 --> SubMethod1 --> Saving (t=1)
^
|
MainThread --> MainMethod --> Saving (t=3)
|
v
Thread 2 --> SubMethod2 --> Exception while saving (t=2).
Since thread 2 gets an exception, I want to rollback all transactions done by other threads. However, although transactions owned by main thread an Thread 2 can be roll-backed, I cannot roll-back thread 1 work.
I am using Spring/Hibernate, so do you have any idea to manage this and how to apply?
Thanks
From Professional Java for Web Applications by Nicholas S. Williams
The transaction scope in Spring is limited to the thread the transaction begins in. The transaction manager then links the transaction to managed resources used in the same thread during the life of the transaction. When using the Java Persistence API, the resource you work with is the EntityManager. It is the functional equivalent of Hibernate ORM’s Session and JDBC’s Connection. Normally, you would obtain an EntityManager from the EntityManagerFactory before beginning a transaction and performing JPA actions. However, this does not work with the Spring Framework model of managing transactions on your behalf. The solution to this problem is the org.springframework.orm.jpa.support.SharedEntityManagerBean. When you configure JPA in Spring Framework, it creates a SharedEntityManagerBean that proxies the EntityManager interface. This proxy is then injected into your JPA repositories. When an EntityManager method is invoked on this proxy instance, the following happens in the background:
➤➤ If the current thread already has a real EntityManager with an active transaction, it delegates the call to the method on that EntityManager.
➤➤ Otherwise, Spring Framework obtains a new EntityManager from the EntityManagerFactory, starts a transaction, and binds both to the current thread. It then delegates the call to the method on that EntityManager. When the transaction is either committed or rolled back, Spring unbinds the transaction and the EntityManager from the thread and then closes the EntityManager. Future #Transactional actions on the same thread (even within the same request) start the process over again, obtaining a new EntityManager from the factory and beginning a new transaction. This way, no two threads use an EntityManager at the same time, and a given thread has only one transaction and one EntityManager active at any given time.
If you were not to use Spring MVC then you would have gotten the session using SessionFactory in Hibernate. Hibernate Sessions represent the life of a transaction from start to finish. Depending on how your application is architected, that might be less than a second or several minutes; and in a web application, it could be one of several transactions in a request, a transaction lasting an entire request, or a transaction spanning multiple requests. A Session, which is not thread-safe and must be used only in one thread at a time, is responsible for managing the state of entities.
This post https://dzone.com/articles/spring-transaction-management-over-multiple-thread-1 appears to cover the issue quite well and mentions a project that is doing something similar.
I am trying to understand the transactions management and try to use its power in my already existing application developed in Struts 2, EJB 3 and hibernate 5.2.
Now I have ejb in my business layer like below
#Stateless
#TransactionManagement(TransactionManagementType.CONTAINER)
public class MyEJb implements ejbxyz {
#Override
public void method(){
Dao dao=new Dao() //Dao class is simple java class
dao.fooMethod(); //this method updates some record
dao.barMethod(); // this method updates some other record
}
}
public class Dao{
fooMethid(){
Session session=sessFactory.openSession();
session.beginTransaction();
session.update(x);
}
barMethod(){
try{
Session session=sessFactory.getCurrentSession();
session.getNamedQuery("xyz").executeUpdate();
}catch(HibernateException ex){
session.getTransaction.rollback();
}
}
}
I understand that Transaction management should be done at service layer(at ejb in my case). But how can I achieve this over there. ?
Now the dependency is if barMethod() fails to update the record then I need to rollback the changes made in fooMethod. So basically I need both the methods to be done in one transaction.
When I execute the application it throws the below exception
Exception while barMethod getNamedQuery is not valid without active transaction
Its because I am not beginning any transaction in barMethod. But then I really dont want to start a new transaction and want to continue with the older transaction started in fooMethod.
Container managed transactions are indeed suported out of the box for EJB beans. However, your Dao class is not a managed bean - it is a regular pojo that you instantiate manualy - therefore it does not participate in any transaction started by your other ejb.
So move your Dao to separate file, annotate it with #Stateless and then inject it into your service using #EJB private Dao dao;
There is more to transactions in Ejb container though. You can control the transaction support on method level via #TransactionAttribute annotation, that specifies how should the container invoke your method with regard to transaction. That way you can control, whether your method requires its own transaction, or if it shall participate in a transaction initiated by the caller(e.g. when invoked from ejb bean). For more info have a look at official Java EE tutorial
I had a class that looked like this:
#Singleton
#ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
#Lock(LockType.WRITE)
#AccessTimeout(value = 20, unit = TimeUnit.MINUTES)
public class MyClass{
public void someLongRunningQuery(){
}
}
However, this would fail with:
org.jboss.resteasy.spi.UnhandledException: javax.ejb.EJBTransactionRolledbackException: Transaction rolled back
I was able to resolve this by adding this annotation.
#TransactionTimeout(value = 20, unit = TimeUnit.MINUTES)
My question is - why is jboss treating this method as a transaction? My understanding is the concurrency management and transaction management are different things.
Transaction timeout handling is not defined in EJB spec and JBoss uses a separate transaction management module to do that. Your example points at a REST access to your singleton - is the Singleton also a REST endpoint or is it invoked from some REST endpoint bean? Just a couple of ideas:
If you have a REST endpoint that injects your singleton and invokes the long running op, isn't it by any chance a #Stateless bean? EJB beans are transaction enabled by default, therefore if a client(REST endpoint) initiates a transaction(it does by default - #TransactionAttribute(REQUIRED)), your singleton will also participate in the same transaction, but because its invocation takes longer than the default transaction timeout value(300s) it causes transaction rollback. If you do not need a transaction on the rest layer, try disabling it with #TransactionAttribute(NOT_SUPPORTED)
If you invoke this long running op from REST layer, wouldn't it be better to use #Asynchronous and return some kind of request/job handle to the client, so that it can query the status of the operation instead of waiting and blocking while it finishes? Keep in mind that EJBs are pooled resources, therefore invoking this long op multiple times can drain your pool and cause acquisition timeouts.