I am quite new with Hibernate and I have this newbie question in my head the clear answer to which I couldn't find anywhere online.
In my multithreaded application I want to use Hibernate for data persistence. The app by nature is event based, means that new event will spawn new thread to process incoming data. From one of online Getting Started tutorials I implemented Hibernate Session Factory which will create single session object and will return it upon HibernateUtil.getSessionFactory().getCurrentSession(). From same tutorial I use this factory as follows:
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
/*
do smth here
*/
session.getTransaction().commit();
What I want to clarify is since there is only one session object shared across different threads will the concurrency work with this code?
For example I am not clear how it will handle situations when 1st thread started transaction, then 2nd thread started transaction, then 1st thread tries to commit transaction, which transaction will it commit? Since I am apllying HQL or Criteroins to Session object, would it know which transaction it should be applied to?
No need to worry about a session being shared across multiple threads. By default the SessionFactory will bind a Session per thread in each of the 3 possible configurations. Here it is an extract from Hibernate 4 docs:
However, as of version 3.1, the processing behind SessionFactory.getCurrentSession() is now pluggable. To that end, a new extension interface, org.hibernate.context.spi.CurrentSessionContext, and a new configuration parameter, hibernate.current_session_context_class, have been added to allow pluggability of the scope and context of defining current sessions.
...
Out-of-the-box, Hibernate comes with three implementations of this interface:
org.hibernate.context.internal.JTASessionContext: current sessions are tracked and scoped by a JTA transaction. The processing here is exactly the same as in the older JTA-only approach. See the Javadocs for details.
org.hibernate.context.internal.ThreadLocalSessionContext:current sessions are tracked by thread of execution. See the Javadocs for details.
org.hibernate.context.internal.ManagedSessionContext: current sessions are tracked by thread of execution. However, you are responsible to bind and unbind a Session instance with static methods on this class: it does not open, flush, or close a Session.
SessionFactory - It shared single object to whole application.It is threadsafe.
Session - It will be created for every application request. It does share with any other object and It is not threadsafe.
Related
Problem
There's a Quartz job, let me refer to it as Job-A, that runs at a fix interval. On each interval, the job runs in a thread that performs some database updates. However, the next immediate run does not see the updates made on the prior run. The flow goes like this:
Job-A runs. Thread: thread-1.
Pick up entities to process.
Commit Connection in the current session to make all changes made since the previous commit/rollback permanent and releases any database locks currently held in it.
Mark an entity, herein referred to as Entity-A, as processed. The goal is to process this entity only once.
Update and commit the transaction.
Refresh the entity so the changes is visible in the succeeding operations.
Job-A runs again. Thread: thread-2.
Pick up entities to process.
Expected: Thread-2 sees Entity-A as processed and skip it over.
Actual : Thread-2 does NOT see the processed status of Entity-A and picks it up again. Why?
The rest of the steps are the same.
Technology Stack
Hibernate 3.6.6
session context is ThreadLocal
MySQL 5.7.3
Quartz 2.3.2
Job-A is annotated with #DisallowConcurrentExecution, so multiple instances of Job-A will never run at the same time.
Job-A runs at a fix interval in seconds.
Questions
I suspect a race condition but is race condition a relevant issue in this problem? If so, how do I narrow down the culprit?
What are other possible causes and how do I track them down?
The default isolation level for MySQL is snapshot isolation. It might be that this is the cause. Try setting the isolation to read committed through the JDBC driver configuration.
Since you're using SessionFactory.getCurrentSession() with ThreadLocal SessionContext, you will have multiple sessions, and each session will keep persisted objects in memory.
Quartz uses a thread pool, so it will reuse threads for different jobs. If you have more than 1 thread in the pool, you will have more than one session.
So this can happen:
Run 1 on thread 1: loads entity A but does not change it
Run 2 on thread 2: loads entity A and updates it
Run 3 on thread 1: reuses session from run 1 and reuses unchanged entity A from session memory
Hibernate has this legacy of being able to run without transaction manager, but in cases like this, it leads to unexpected results.
It's better to have each transaction run in a separate session.
try(Session session = SessionFactory.openSession()){
// start transaction
//...
// commit transaction
}//auto-closes session
If you use a JTA transaction manager, you can also use
hibernate.current_session_context_class = jta
This way, you can keep using SessionFactory.getCurrentSession(), but it will be opened/closed per transaction, which is the cleanest solution.
Unfortunately, I have no clear example how to configure JTA for your stack.
See also: https://docs.jboss.org/hibernate/orm/5.3/userguide/html_single/Hibernate_User_Guide.html#architecture-current-session
If you're worried about performance, you'll better configure a second level cache. A second level cache is shared between sessions, and is also transaction aware, so it will contain only the latest committed version of an object.
See also:
https://docs.jboss.org/hibernate/orm/5.3/userguide/html_single/Hibernate_User_Guide.html#caching-config
I understood the basic concept of Thread in standalone application. But, got confused in below areas.
1). In Java webapplication (servlet and Spring based)?
I hope, each request is handled by a different thread. Is this correct? Is there any other definition available?
2). what is a thread in Hibernate with Spring MVC?
Session factory is thread safe.. where as session object is not. What is mean by thread here?
Please help me to understand this.
1) The application server has a thread pool, when a request comes in it gets assigned a thread from the pool.The same thread calls the dispatcher servlet, which calls a controller, which calls a service, etc., and finally creates an HttpResponse and sends it to the client.
2) A usual pattern with Hibernate (if you're not using Seam conversations) is session-per-request:
2.4.2. Session-per-request pattern
This is the most common transaction pattern. The term request here
relates to the concept of a system that reacts to a series of requests
from a client/user. Web applications are a prime example of this type
of system, though certainly not the only one. At the beginning of
handling such a request, the application opens a Hibernate Session,
starts a transaction, performs all data related work, ends the
transaction and closes the Session. The crux of the pattern is the
one-to-one relationship between the transaction and the Session.
The transaction is stored by Spring in a threadlocal variable. So the thread has a Hibernate session (which is confined to that thread), and it is associated with a transaction (or a stack of transactions, since they can be nested).
My question is basically the same as is here, but I'm not satisfied with the answer so I'm writing this question.
In Spring Framework manual it is stated that for a PROPAGATION_REQUIRES_NEW the current transaction will be suspended. How is this actually implemented? I know that most databases don't support nested transactions and can have only one transaction running in one connection. This means that you can't just "not use" original transaction and start a new one - before starting new one you must commit or rollback original transaction.
Example:
START TRANSACTION
SELECT ...
UPDATE ...
-- Now we run method with PROPAGATION_REQUIRES_NEW
-- How do we "suspend" transaction so we can start new one?
START TRANSACTION
UPDATE ...
COMMIT
-- We returned from the method, result was commited
-- Now we'd like to "unsuspend" the original transaction so it can be commited/rollbacked, but how?
Or is this possibly implemented using another connection (Session object)? So that we stop using the original connection and create a new one where we can start new transaction?
I am missing here something so obvious that nobody cares to explain it (at least not in Spring docs, Spring in Action, Spring persistence with Hibernate).
Thanks a lot!
The point of suspending a transaction is to change the current transaction for a thread to a new one. This would NOT line up with the semantics of nested transactions because the new and suspended transactions are completely independent of each other. There is no connection-level API to support suspending transactions so this has to be done by using a different connection. If you are using JTA with Spring, this is done by the JTA transaction manager. If you are using DataSourceTransactionManager, you can look in the code and see that it will be saving off the current connection as a "suspended resource" and grabbing a new connection from the data source for the new transaction.
I am using approach from the "Accessing scoped proxy beans within Threads of" answer. However I am seeing rare deadlocks involving RequestAttributes object. The main reason of the deadlock is between the synchronized (this.sessionAttributesToUpdate) statement in the object and servlet session hash-map. Normally the instances of the object are created for each request, so they don't clash, but if I pass the object to another thread to use the session beans, the same object is used and it causes deadlock sometimes.
The deadlock happens if current http request is not completed while the another thread starts using a session bean passed with RequestContextHolder.setRequestAttributes.
I think this guy mentions the same problem, but his question is unanswered: Session scoped bean encountering deadlock.
So, any ideas how to avoid the deadlock?
Here's an answer that provides alternative solution considering the objective is to calculate something in background while user is navigating pages.
Possibility 1:
Create a service bean with processing method that is annotated with #Async (http://static.springsource.org/spring/docs/3.0.x/reference/scheduling.html) that returns a result of computation in a Future object. Store the Future object in the session. Access the result in subsequent requests through Future object if task is completed. Cancel the the task via Future.cancel if session is to be destroyed before task is completed.
Possibility 2:
Take a look if new features of Spring 3.2 and Servlet 3.0 async processing can help you:
http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-ann-async
Possibility 3:
Emulate a task queue. Create a singleton service that can put objects to structure like ConcurrentMap where key would be some job id (you can than store key into session) and value the result of background processing. Background thread would then store objects there (this is perhaps not much better than accessing session directly, but you can make sure it's thread safe).
I have a web application that consists of a web service with two operations: createA and createB. An handler is registered for the endpoint. This handler opens a Session and start a transaction when the request is received. Then the code of the requested operation is executed. Before the response is sent back, the transaction is committed and the session is closed.
The code of createA consists of creating an entity of type A and persisting it using Session.save() method. In DEBUG mode, after Session.save() is called, I can see that there is one insertion in the ActionQueue of the session.
The code of createB consists of :
retrieving the previously created entity of type A
creating an Entity B that references the instance of A (B has a property that represents an associated A)
updating A to reference the new instance of B
call Session.save() for the new instance of B
call Session.update() for the new modified instance of A
However, in DEBUG mode, after calling Session.save() and Session.update(), the ActionQueue of the corresponding Session is empty. But, after the transaction commits, I can see the created entity in the database.
Operation createA and createB are invoked in this order without DEBUG. An error appears during the execution of the create B when it tries to retrieve the instance of A previously created using a criteria and the Session.list() method. The problem is that the instance of A is not found.
However, if I repeat the same sequence of operations in DEBUG or using Thread.sleep(15s) between invocations of the two operations, the instance of A can be found.
Thanks
EDIT: I forgot to precise that it works on certain machines but not on others. And I don't see any differences between these machines.
If you use the same Hibernate session for both createA and createB, then it'll work. You can store the Hibernate session in the Http session for achieving this (pay attention to synchronize the access to the session object, as requests from the same browser session can com in different threads).
Your problem is, Hibernate opens a new database connection for every session. Now your database seems not to synchronize the statements. It can happen in the database the select arrives before the insert is finished. Then it just depends of the speed of the involved computers if this condition happens or not. With the debug mode or the sleep() you make one computer slower so you don't have the problem any more.
If you want to continue with two different sessions for these two procedures, you can
look for the transaction mode of your database. Some databases have a dirty read where no correct locking or synchronization is done. Check if you accidentally used such a mode.
Check the JDBC parameters (they can be used in the hibernate connection.url) if there are parameters for your database which change timing and synchronization.
Check your connection pool (for the case you're using one).
The problem is that Hibernate does not save the entity to the database when you call Session.save(). It simply prepares the statement for execution later. This happens when you the transaction ends or when the you flush the session.
Your call to B is probably happening sometimes before the transaction ends for the A request. That is why it works if you wait a little while.
Try adding session.flush() after the save call. This will force Hibernate to persist the changes to the DB.