I'm using Spring Boot 1.4 and trying to understand how does Spring Transaction Management works.
Here is my question:
Lets say I have a service with a method A that is annotated with #Transactional(isolation = SERIALIZABLE) and another method B annotated with #Transactional(isolation = READ_COMMITED).
And then let's imagine that some service X calls these two methods A and B sequentially.
My colleague says that transaction level is set per connection in Spring. Which means that if the same connection from the pool is used for these two sequential calls, then isolation level for both transactions A and B = SERIALIZABLE.
However, to me it seems a bit strange. I would expect that these two transactions would have different level of isolations, because all sql databases allow to set isolation level for a given transaction explicitly.
I was trying to read documentation and couldn't find a place where it would be mentioned that transcation level is assigned to connection.
Can someone judge us on this issue?
If there is no transaction started when calling method A() or B() a new transaction is created when calling the method and gets closed when leaving the method. The used connection returns to pool or gets closed.
This thread explains what happens to the connection when transaction gets closed:
Does Spring close connection after committing transaction?
In case there exists a transaction that wraps both methods, there is only one connection used for both methods; and i guess the isolation level is the one defined by the bigger transaction.
Related
i am having a simple controller /hello which internally calls Repository layer for a transaction using the default propagation level i.e Propagation.REQUIRED.
#Controller
public void hello(data) -> doTransaction(data)
#Repository
#Transactional
public void doTransaction(data){
jdbcTemplateInsertA(data);
Thread.sleep(15*1000);
jdbcTemplateUpdateB(data);
}
as per online docs
REQUIRED is the default propagation. Spring checks if there is an active transaction, and if nothing exists, it creates a new one. Otherwise, the business logic appends to the currently active transaction:
now let's say i hit the API 2 times, with data1 and then data2, before request 1 was finished API was hit 2nd time.
would there be only a single transaction or two separate independent transactions?
Also, i want to know is using #Transactional with jdbcTemplate valid?
i am having doubt on how jdbcTemplate internally gets the same connection object that was used by #Transactional annotation considering pool size >=2.
UPDATE:
You will have a transaction per thread. -> any docs/resources where i can find this?
For 2nd part, let me rephrase my question.
Let's say using hikari pool with size as 5 as per my understanding #Transactional would pick some connection from pool, let's say it picked up connectionId1554 now, when i call jdbc.execute(), how spring ensure that it uses connectionId1554 and not any other available connection from the pool
You will have a transaction per thread. If you have two requests, then you will have two threads (from your web container, like Tomcat), and therefore you'll have two separate transactions.
Based on the code and (lack of) configuration it is hard to tell how you setup your JdbcTemplate. Most likely the connection used by the JdbcTemplate comes from a database connection pool. If you've configured two connections then you can only use two connections and the usage of those connections will block other requests to the database if they are in use. Now, that being said, database connection pools are generally programmed to be super smart and one database connection can be used between multiple database requests/transactions depending on configuration settings.
let's say i hit the API 2 times, with data1 and then data2, before request 1 was finished API was hit 2nd time.
would there be only a single transaction or two separate independent transactions?
Each incoming request to your api will result in an independent transaction being started. If you think this through this makes sense: if you have 2 users each making a request at the same time, you would want each of their requests to be handled independently, it would be odd if both of their requests joined and were handled as a single transaction.
i want to know is using #Transactional with jdbcTemplate valid? i am having doubt on how jdbcTemplate internally gets the same connection object that was used by #Transactional annotation
This depends on how you have configured a Datasource and a connection pool. Are you running this on an appserver like Webpshere, Weblogic or WildFly? They provide Datasource connection pooling integrated with a TransactionManager too.
Java web
Spring MVC framework
In my service level, I wrote a #Transactional method. In that method, I update some data in database and then read the data from the database later.
What surprises me is that when several requests come, inside the same thread, the data change is visible(which is supposed to be), but the data change is not visible in other thread.
Isn't different threads of one method build as one transaction?
And what should I do if I still want to use transaction and I want the same method called in different threads be in one transaction (the change of the data can be seen by each other)?
From Professional Java for Web Applications by Nicholas S. Williams
Using Threads for Transactions and Entity Managers
The transaction scope 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.
(I paraphrased it a little the above piece)
I think its self explanatory and answers your question. But I would like to add that, 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.
you may want to play with isolation parameter of#Transactional annotation. Spring by default uses.. hm.. DEFAULT which is set by database, so may be different. You can try to use: READ_UNCOMMITTED. More info: http://www.byteslounge.com/tutorials/spring-transaction-isolation-tutorial
I have two transactional methods A and B. A has isolation level of READ_COMMITTED and B has isolation level of SERIALIZABLE. if B was called inside A, what would be the default behavior here?
is spring going to create a new transaction for B or it will be run in the same transaction? would the isolation level of B be handled correctly?
in case of two threads are accessing A at the same time, what would happen when they reach B call?
In case of B's transaction was rolled back for some reason, would A's transaction be rolled back as well?
Note: let us assume that propagation level is the default one for both A and B.
Any ideas about what happens in such a situation?
I have read in the book that:
Typically, the container begins a transaction immediately before an enterprise bean method starts. It commits the transaction just before the method exits.
Suppose my stateful EJB has exposed 3 business methods and the client calls these 3 methods sequentially and want wants to run these 3 methods within a single transaction. These methods would be called across multiple requests.
Now the spec says that the transaction is committed just before the method exits. How will I be able to rollback the whole transaction, if my 1st method is successful and 2nd (or 3rd) method fails?
Please note that I don't want to use create my own transaction boundary.
Any help or the pointer in the right direction would be highly appreciated.
You are using a stateful session bean to act as a buffer. And you want a transaction around multiple calls.
From a design perspective, it's OK, if the transaction is started and committed/rollbacked from a single request (from within a single HttpServletRequest for example). In this case you can use a UserTransaction to span the transaction over multiple calls to an EJB. But a UserTransaction is bound to the current thread, so it might be difficult to pass it to the next request.
If you start and commit from different requests, you lose control over the duration of the transaction: Now a client controls the transaction. If that client crashes, the transaction won't be rolled back until the transaction timeout is reached. So the recommendation in this case is to buffer in a HttpSession for example. If all data has been collected, call a (stateless) EJB to persist it.
Create a method in the bean that calls all the other 3 methods. Then they'll be in the same transaction.
Is there any way to "replay" transaction?
I mean, sometimes I get RollbackException and rollback the transaction. Can I then "clone" the transaction and try again, or once rollback is called, transaction is lost?
I really need the changes, and really don't want to trace every change for rerunning later...
thanks,
udi
Why do you get the exception in the first place ? This seems to me to be the crux of the matter.
Are you relying on optimistic writing ? If so, then you'll have to wrap your database writes in some form of loop, incorporating (perhaps) a backoff and a number of retries. You can't do this automatically, unfortunately (unless you investigate some form of AOP solution wrapping your database writes with a retry strategy ?)
That depends where that transaction comes from. In Java/JDBC, a transaction is tied to a connection. You start one by setting setAutoCommit() to false (otherwise, every statement becomes its own little transaction).
There is nothing preventing you from reusing the connection after a transaction failed (i.e. you called rollback).
Things get more tricky when you use Spring. Spring wraps your methods in a transaction handler and this handler tries to guess what it should do with the current transaction from the exceptions that get thrown in the method. The next question is: Which wrapper created the current transaction? I just had a case where I would call a method foo() which would in turn call bar(), both #Transactional.
I wanted to catch errors from bar() in foo() and save them into the DB. That didn't work because the transaction was created for foo() (so I was still in a transaction which Spring thought broken by the exception in bar()) and it wouldn't let me save the error.
The solution was to create baz(), make it #Transactional(propagation=Propagation.REQUIRES_NEW) and call it from foo(). baz() would get a new, fresh transaction and would be able to write to the DB even though it was called from foo() which already had a (broken) transaction.
Another alternative is to use JDBC savepoints to partly roll back.