Spring/Hibernate/Transaction synchronization issue - java

I have a service that returns a persisted Schedule object based on an input: date. I have multiple threads calling this method and I would like to ensure uniqueness. I don't want to catch a constraint violation and give any user an error. What I need is: if one thread calls the method and a Schedule object doesn't exist for that date, that thread creates it and the other threads wait. Before I have been using Spring, just plain Hibernate in my web applicaiton I have done it the following way:
I've used a ManagedSessionContext with hibernate. I was starting a transaction in the beginning of each web request and commiting it in the end. For the synchronized blocks I've commited the current transaction right away in a synchronized block and started a new one to finish the request. My isolation level was READ_COMMITED and it worked great.
public synchronized Schedule getSchedule(Date date) {
Schedule schedule = dao.getSchedule(date);
if (schedule == null) {
schedule = createSchedule(date);
dao.save(schedule);
HibernateUtils.getCurrentSession().getTransaction().commit();
HibernateUtils.getCurrentSession().getTransaction().begin();
}
return schedule;
}
Now I am using Spring with this project and I'm using aop for transaction management. I have removed the manual transaction code. I'm testing with multiple threads and I'm getting a deadlock
Caused by: java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction
My Transaction AOP:
<tx:advice id="txAdvice">
<tx:attributes >
<tx:method name="getSchedule" isolation="SERIALIZABLE" />
<tx:method name="*" />
</tx:attributes>
</tx:advice>

#Autowire
private HibernateTransactionManager txManager;
Wrap the method body with:
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("transactionName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionStatus ts = txManager.getTransaction(def);
//Body Code
txManager.commit(ts);

Related

Transactional in both service and dao layers

I have a question regarding #Transactional annotation.
Nothing special defined, so as I understand is PROPAGATION_REQUIRED
Let’s say I have a transactional annotation which on both service and dao layer.
Service
#Transactional
public long createStudentInDB(Student student) {
final long id = addStudentToDB (student);
addStudentToCourses (id, student.getCourseIds());
return id;
}
private long addStudentToDB (Student student) {
StudentEntity entity = new StudentEntity ();
convertToEntity(student, entity);
try {
final id = dao.create(entity);
} catch (Exception e){
//
}
return id;
}
private void addStudentToCourses (long studentId, List<String> coursesIds){
//add user to group
if(coursesIds!= null){
List<StudentCourseEntity> studentCourses = new ArrayList<>();
for(String coursesId: coursesIds){
StudentCourseEntity entity = new StudentCourseEntity ();
entity.setCourseId(coursesId);
entity.setStudentId(userId);
studentCourses.add(studentId);
}
anotherDao.saveAll(studentCourses);
}
}
DAO
#Transactional
public UUID create(StudentEntity entity) {
if ( entity == null ) { throw new Exception(//…); }
getCurrentSession().save(entity);
return entity.getId();
}
ANOTHER DAO:
#Transactional
public void saveAll(Collection< StudentCourseEntity > studentCourses) {
List< StudentCourseEntity > result = new ArrayList<>();
if(studentCourses!= null) {
for (StudentCourseEntity studentCourse : studentCourses) {
if (studentCourse!= null) {
save(studentCourse);
}
}
}
}
Despite the fact that’s not optimal, it seems it causing deadlocks.
Let’s say I have max 2 connections to the database.
And I am using 3 different threads to run the same code.
Thread-1 and thread-2 receive a connection, thread-3 is not getting any connection.
More than that, it seems that thread-1 become stuck when trying to get a connection in dao level, same for thread-2. Causing a deadlock.
I was sure that by using propagation_required this would not happen.
Am I missing something?
What’s the recommendation for something like that? Is there a way I can have #transactional on both layers? If not which is preferred?
Thanks
Fabrizio
As the dao.doSomeStuff is expected to be invoked from within other transactions I would suggest you to configure this method as:
#Transaction(propagation=REQUIRES_NEW)
Thanks to that the transaction which is invoking this method will halted until the one with REQUIRES_NEW will be finished.
Not sure if this is the fix for your particular deadlock case but your example fits this particular set-up.
You are right, Propagation.REQUIRED is the default. But that also means that the second (nested) invocation on dao joins / reuses the transaction created on service level. So there is no need to create another transaction for the nested call.
In general Spring (on high level usage) should manage resource handling by forwarding it to the underlying ORM layer:
The preferred approach is to use Spring's highest level template based
persistence integration APIs or to use native ORM APIs with
transaction- aware factory beans or proxies for managing the native
resource factories. These transaction-aware solutions internally
handle resource creation and reuse, cleanup, optional transaction
synchronization of the resources, and exception mapping. Thus user
data access code does not have to address these tasks, but can be
focused purely on non-boilerplate persistence logic.
Even if you handle it on your own (on low level API usage) the connections should be reused:
When you want the application code to deal directly with the resource
types of the native persistence APIs, you use these classes to ensure
that proper Spring Framework-managed instances are obtained,
transactions are (optionally) synchronized, and exceptions that occur
in the process are properly mapped to a consistent API.
...
If an existing transaction already has a connection synchronized
(linked) to it, that instance is returned. Otherwise, the method call
triggers the creation of a new connection, which is (optionally)
synchronized to any existing transaction, and made available for
subsequent reuse in that same transaction.
Source
Maybe you have to find out what is happening down there.
Each Session / Unit of Work will be bound to a thread and released (together with the assigned connection) after the transaction has ended. Of course when your thread gets stuck it won't release the connection.
Are you sure that this 'deadlock' is caused by this nesting? Maybe that has another reason. Do you have some test code for this example? Or a thread dump or something?
#Transactional works by keeping ThreadLocal state, which can be accessed by the (Spring managed) Proxy EntityManager. If you are using Propagation.REQUIRED (the default), and you have a non-transactional method which calls two different DAOs (or two Transactional methods on the same DAO), you will get two transactions, and two calls to acquire a pooled connection. You may get the same connection twice or two different connections, but you should only use one connection at the time.
If you call two DAOs from a #Transactional method, there will only be one transaction, as the DAO will find and join the existing transaction found in the ThreadLocal state, again you only need one connection from the pool.
If you get a deadlock then something is very wrong, and you may want to debug when your connections and transaction are created. A transaction is started by calling Connection.setAutoCommit(false), in Hibernate this happens in org.hibernate.resource.jdbc.internal.AbstractLogicalConnectionImplementor#begin(). Connections are managed by a class extending org.hibernate.resource.jdbc.internal.AbstractLogicalConnectionImplementor so these are some good places to put a break point, and trace the call-stack back to your code to see which lines created connections.

Spring Transactional without rollback

#Transactional
public void setSomething(String name) { ... }
Sorry to ask this very basic question, Spring transactional annotation is so powerful but yet super hard to understand. Based on the code above, I don't have rollbackFor control, meaning, if there is exception, this transactional context will not be rollback. But based on my experience in old way to covering transaction block, if there is no rollback for exception, commit will be skipped and cause the (Oracle) database's table being locked (suspend, other user can't commit their SQL). Will Spring have the same issue without using rollbackFor?
The default /#Transactional settings are:
Propagation setting is REQUIRED.
Isolation level is DEFAULT.
Transaction is read/write.
Transaction timeout defaults to the default timeout of the underlying
transaction system, or none if timeouts are not supported.
Any RuntimeException triggers rollback, and any checked Exception
does not.
So in your case this will be rollbacked if you will have a RuntimeException.
But usually it is not sufficient to tell you simply to annotate your classes with the #Transactional annotation, add #EnableTransactionManagement to your configuration.
You can configure exactly which Exception types mark a transaction for rollback, including checked exceptions. The following XML snippet demonstrates how you configure rollback for a checked, application-specific Exception type.
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/>
<tx:method name="*"/>
</tx:attributes>
Or with annotations
#Transactional(rollbackFor=NoProductInStockException.class)
Detailed documentation you can find here:
http://docs.spring.io/autorepo/docs/spring/4.2.x/spring-framework-reference/html/transaction.html
Hope that helps.
The rollbackFor and related parameters are for fine-tuning. If you omit them, the default behaviour is to rollback for RuntimeExceptions. Other exceptions don't perform a rollback and any changes made into the database will be committed.
The transaction is either committed or rolled back, there's no way a #Transactional method will result in an unterminated transaction (at least in normal operation).

Spring HibernateDaoSupport creates multiple sessions

I am using Struts2.3 + Spring 3.2.6 + Hibernate 3.X for my web application.
I am using annotations to manage the transaction.
My DAO class is as below.
#Transactional(readOnly = true, rollbackFor={Exception.class})
public class CustomerDAOImpl extends HibernateDaoSupport implements CustomerDAO{
//adds the customer
#Transactional(propagation=Propagation.REQUIRED, rollbackFor = {Exception.class})
public void addCustomer(Customer customer){
Session session = getSession();
System.out.println("M1() Hash Code: --->"+session.hashCode()+ " Thread id: "+Thread.currentThread().getId());
//session.save(customer);
}
//return all the customers in list
// #Transactional(propagation=Propagation.REQUIRED, rollbackFor = {Exception.class})
#Transactional(readOnly = true)
public List<Customer> listCustomer(){
Session session = getSession();
System.out.println("M2() Hash Code: --->"+session.hashCode()+ " Thread id: "+Thread.currentThread().getId());
return null; //logic to get the list based on condition
}
These methods will be called from service layer and it is like below;
customerDAO.addCustomer(customer);
customerDAO.listCustomer();
I am getting different sessions for same thread when the above code is executed.
Output:
M1() Hash Code: --->5026724 Thread id: 21
M2() Hash Code: --->8899550 Thread id: 21
Due to this, if any exception comes in method2() the data which is persisted using method1() is not rollback.
Please let me know, how to get the single session based on thread (if i go for my own HibernateFactory i lose transaction management by Spring) with out loosing spring transaction management.
If you are using Hibernate make your DAO class to extend HibernateDaoSupport. Now you can get session from getHibernateTemplate() method. That way you can have the session managed by spring-hibernate
You can try this -
appContext.xml
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="submit*" propagation="REQUIRED" read-only="false" rollback-for="Exception"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="appControllerTransactionPointCuts"
expression="execution(* com.test.customer.bo.Custom.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="appControllerTransactionPointCuts" />
</aop:config>
Remove -
<tx:annotation-driven transaction-manager="transactionManager" />
from appcontext.xml
Now AOP should manage transactions.
How Transactions work -
Suppose you are persisting objects in multiple DAO methods and you want them all to happen in one transaction, then you have to apply transaction at the service layer method where the DAO methods are getting called. For e., in my case the service layer is com.test.customer.bo.Custom.*
If you are not doing so, then each of your DAO method will be executed in a separate transaction and will get persisted to database if no exception occurs. If exception occur in method2() of DAO layer it will not rollback method1() as it is already committed.

Confusion with DBCP connection pooling and Threads

I am trying to get Multi-Threading working in my Java web application and it seems like no matter what I try I run into some sort of issues with connection pooling.
My current process is that I have am looping through all my departments and processing them, which finally generates a display. This takes time, so I want to spawn a thread for each department and have them process concurrently.
After alot of time of first figuring out how to get my hibernate session to stay open in a thread to prevent the lazy initializion loading errors, I finally had the solution of creating a Spring bean of my Thread class, and creating a new instance of that bean for each thread. I have tried 2 different versions
1) I directly inject the DAO classes into the Bean. FAILED - After loading the page a few times I would get "Cannot get a connection, pool error Timeout waiting for idle object" for each thread and the app would crash.
2) Ok so then I tried injecting the spring SessionFactory into my bean, then create new instances of my DAO and set it with the SessionFactory. My DAO objects all extend HibernateDaoSupport. FAILED - After loading the page a few times I would get "too many connections" error messages.
Now what I am confused about is that my SessionFactory bean is a singleton which I understand to mean that it is a single Object that is shared throughout the Spring container. If that is true then why does it appear like each Thread is creating a new connection when it should just be sharing that single instance? It appears that all my connection pool is getting filled up and I don't understand why. Once the threads are done, all the connections that were created should be released but there not. I even tried running a close() operation on the injected SessionFactory but that has no effect. I tried limiting how many threads run concurrently at 5, hoping that would cause not so many connections to be created at one time but no luck.
I am obviously doing something wrong but I am not sure what. Am I taking the wrong approach entirely in trying to get my hibernate Session into my Threads? Am I somehow not managing my connection pool properly? any ideas would be greatly appreciated!
More info I thought of: My process creates about 25 threads total, which are run 5 at a time. I am able to refresh my page about 3 times before I start getting the errors. So apparently each refresh creates and holds on to a bunch of connections.
Here is my spring config file:
<bean id="mainDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
....
<!-- Purposely put big values in here trying to figure this issue out
since my standard smaller values didn't help. These big values Fail just the same -->
<property name="maxActive"><value>500</value></property>
<property name="maxIdle"><value>500</value></property>
<property name="minIdle"><value>500</value></property>
<property name="maxWait"><value>5000</value></property>
<property name="removeAbandoned"><value>true</value></property>
<property name="validationQuery"><value>select 0</value></property>
</bean>
<!--Failed attempt #1 -->
<bean id="threadBean" class="controller.manager.ManageApprovedFunds$TestThread" scope="prototype">
<property name="dao"><ref bean="dao" /></property>
</bean>
<!--Failed attempt #2 -->
<bean id="threadBean" class="manager.ManageApprovedFunds$TestThread" scope="prototype">
<property name="sessionFactory"><ref bean="sessionFactory" /></property>
</bean>
Java code:
ExecutorService taskExecutor = Executors.newFixedThreadPool(5);
for(Department dept : getDepartments()) {
TestThread t = (TestThread)springContext.getBean("threadBean");
t.init(dept, form, getSelectedYear());
taskExecutor.submit(t);
}
taskExecutor.shutdown();
public static class TestThread extends HibernateDaoSupport implements Runnable, ApplicationContextAware {
private ApplicationContext appContext;
#Override
public void setApplicationContext(ApplicationContext arg0)
throws BeansException {
this.appContext = arg0;
}
#Override
public void run() {
try {
MyDAO dao = new MyDAO();
dao.setSessionFactory(getSessionFactory());
//SOME READ OPERATIONS
getSessionFactory().close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Basically you shouldn't try to share a single hibernate Session between threads. Hibernate sessions, nor the entity objects, are threadsafe and that can lead to some suprising problems. Here and [here]3[] is a small but nice read there is also some value in the comments. Basically don't try to share a Session between threads.
There is also another problem as the whole transaction management is based around ThreadLocal objects, the same goes for obtaining the current session and underlying JDBC connection. Now trying to spawn threads will lead to suprising problems, one of them would be connection pool starvation. (Note: don't open too many connections, more information here).
Next to not to opening to much connections you should be aware of starting to many threads. A Thread is bound to a cpu (or core if you have multiple cores). Adding to many threads might lead to heavy sharing of a single cpu/core between to many threads. This can kill your performance instead of increasing it.
In short IMHO your approach is wrong, each thread should simply read the entity it cares about, do its thing and commit the transaction. I would suggest using something like Spring Batch for this instead of inventing your own mechanism. (Although if it is simple enough I would probably go for it).
A database connection is not associated with the SessionFactory but with the Session. To get your handling correct you have to take care of the following:
Only create one instance of SessionFactory (per persistence context) - but you are doing this already
Don't close() the SessionFactory - it's lifetime should end when the application dies - that is at undeployment or server-shutdown.
Make sure to always close() your Session - you use one database connection per open Session and if these don't get closed you are leaking connections.

using programmatic transaction control in CMT bean

I need to commit transactions from CMT bean by hand. There is a loop which processes multiple records and each record should be processed in its own transaction. I wanted to mark method transaction support as NOT_SUPPORTED and then control transaction from method. However I could not retrieve a UserTransaction instance neither from SessionContext neither injecting it as a JNDI resource java:/module/UserTransaction.
Are there any chance to process multiple records in CMT bean in their own transactions without introducing new BMT bean for such processing?
You should not mess around transactions yourself if you use CMT.
I recommend you create a method for the operation needs to be in transaction, mark it as REQUIRES_NEW, then call it from the loop.
Everytime the method is called, the current transaction (if any) will be suspended and a new transaction will be started for the operation.
Something like this:
#EJB
SomeEJBLocal anotherme;
public void loop() {
for(/* something */) {
anotherme.single();
}
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void single() {
// do stuff
}
You will have to inject another instance of the EJB and call single in order for the container to process the transaction aspects.

Categories

Resources