Can you have multiple transactions within one Hibernate Session?
Asked Answered
B

6

29

Can you have multiple transactions within one Hibernate Session?

I'm unclear if this is an allowable desirable. In my code I have a long running thread and takes items from a Blocking Queue, depending on what is on the queue, it may need to create and save a hibernate object, or it may not need to do anything.

Each item is distinct so if item 1 is saved and item 2 fails to save whatever reason I don't want to that to prevent item 1 being added to the database.

So the simplest way to do this is for each item that needs to be created to create a new session, open transaction, save new object, commit transaction, close session

However, that means a new session is created for each item, which seems to go against Hibernates own recommendations to not do Session Per Request Pattern. So my alternative was to create one session in the thread, then just open and commit a new transaction as required when needed to create a new object. But I've seen no examples of this approach and I'm unsure if it actually works.

Blockade answered 17/9, 2014 at 14:34 Comment(1)
G
35

The session-per-request pattern uses one JDBC connection per session if you run local transactions. For JTA, the connections are aggressively released after each statement only to be reacquired for the next statement.

The Hibernate transaction API delegates the begin/commit/rollback to the JDBC Connection for local transactions and to the associated UserTransaction for JTA. Therefore, you can run multiple transactions on the same Hibernate Session, but there's a catch. Once an exception is thrown you can no longer reuse that Session.

My advice is to divide-and-conquer. Just split all items, construct a Command object for each of those and send them to an ExecutorService#invokeAll. Use the returned List to iterate and call Future#get() to make sure the original thread waits after all batch jobs to complete.

The ExecutorService will make sure you run all Commands concurrently and each Command should use a Service that uses its own @Transaction. Because transactions are thread-bound you will have all batch jobs run in isolation.

Gutta answered 24/9, 2014 at 20:59 Comment(7)
'Once an exception is thrown you can no longer reuse that Session.' - i think that is they key point that may have caused me a problem previously and breaks multiple txns within one Session for me, thanksBlockade
@VladMihalcea Could you please explain why we can't reuse the session if there is an exception? Is it because hibernate roll-back the transaction and close the session when an exception is thrown? or some other reason?Palacios
@VladMihalcea Thanks a lot for your answer. I can understand your answer, but I think I have a very limited knowledge in hibernate caching and synchronization.Palacios
@VladMihalcea After reading the sample chapters, I just ordered a copy of your book from ebay.Palacios
Could be some intermediaries. Leanpub is the original publisher for ebook and Amazon for the paperback. I suppose those two offer the best prices for it.Gutta
@VladMihalcea I have ordered it from wordery.com on ebay. wordery.com/…Palacios
Most likely they order it from Amazon tooGutta
O
14

Obviously, you can. A hibernate session is more or less a database connection and a cache for database objects. And you can have multiple successive transactions in a single database connection. More, when you use a connection pool, the connection is not closed but is recycled.

Whether you should or not is a matter of reusing objects from session. If there is a good chance but you can reuse objects that a preceding transaction has put in session, you should keep one single session for multiple transactions. But if once an object has been committed, it will never be re-used, it is certainly better to close the session and re-open a new one, or simply clear it.

How to do it :

If you have a Session object, you create transactions with :

Transaction transaction;
transaction = session.beginTransaction();
... (operations in the context of transaction)
transaction.commit();
... (other commands outside of any transaction)
transaction = session.beginTransaction();
... (and so on and so forth ...)
Oakes answered 24/9, 2014 at 20:58 Comment(0)
T
4

From hibernates documentation

"A Session is an inexpensive, non-threadsafe object that should be used once and then discarded for: a single request, a conversation or a single unit of work. A Session will not obtain a JDBC Connection, or a Datasource, unless it is needed. It will not consume any resources until used."

so if you are creating sessions again and again it will not burden the system much. If you are continuing a session for too long it may create problems as session is not thread safe .In my opinion you simplest solution is the best "So the simplest way to do this is for each item that needs to be created to create a new session, open transaction, save new object, commit transaction, close session"

By the way if you are creating single record of anything you dont need transaction too much. creating single record is inherently " all or none" thing for which we use transaction

Tridentine answered 17/9, 2014 at 14:57 Comment(2)
thread-safeness not an issue because as all code in this case run on one thread.Blockade
Another disadvantage of using single session for multiple transactions is that Objects will not automatically evict and session will keep on growing its size which may lead to out of memory error.Tridentine
A
0

Short answer is yes, you can use same session for transaction. Take a look at org.hibernate.Transaction., it has required method to manage transaction.

Asynchronism answered 24/9, 2014 at 20:12 Comment(0)
A
0
package hibernate;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
class Tester {
public static void main(String[] args) {
    SessionFactory sf = new org.hibernate.cfg.Configuration().configure().buildSessionFactory(new StandardServiceRegistryBuilder().configure().build());
    Session session = sf.openSession();
    session.beginTransaction();
        Student student = new Student();
    student.setName("Mr X");
    student.setRollNo(13090);
        session.save(student);
    session.getTransaction().commit();
    session.getTransaction().begin();
    session.load(Student.class,23);
    student.setName("New Name");
    student.setRollNo(123);
    session.update(student);
    session.getTransaction().commit();
    session.close();
  }
}
Acetylide answered 23/2, 2016 at 8:25 Comment(0)
S
0

docs.jboss.org Chapter 13. Transactions and Concurrency

Use a single database transaction to serve the clients request, starting and committing it when you open and close the Session. The relationship between the two is one-to-one and this model is a perfect fit for many applications.

It seemed we should always obey the "one-to-one relationship" rule.

But, although the sample below will trigger a exception in the line where the second "session.beginTransaction()" is called

Exception in thread "main" java.lang.IllegalStateException: Session/EntityManager is closed

    private static void saveEmployees(SessionFactory factory) {
    // crate session
    //Session session = factory.openSession();
    Session session = factory.getCurrentSession();


    {
        // start a transaction
        Transaction trans = session.beginTransaction();
        
        // create an employee
        Employee tempEmployee = new Employee("Steve","Rogers", "The Avengers");
        // save to database
        session.save(tempEmployee);

        // commit the transaction
        trans.commit();
    }
    {
        // start a transaction
        Transaction trans = session.beginTransaction();

        // create an employee
        Employee tempEmployee = new Employee("Tony","Stark", "The Avengers");
        // save to database
        session.save(tempEmployee);
        
        // commit the transaction
        trans.commit();
    }
    
    // close session
    session.close();
    
}

, another sample below will work properly. The only difference is that the second sample uses "factory.openSession()" to get a session, instead of "factory.getCurrentSession()".

    private static void saveEmployees(SessionFactory factory) {
    // crate session
    Session session = factory.openSession();
    //Session session = factory.getCurrentSession();


    {
        // start a transaction
        Transaction trans = session.beginTransaction();
        
        // create an employee
        Employee tempEmployee = new Employee("Steve","Rogers", "The Avengers");
        // save to database
        session.save(tempEmployee);

        // commit the transaction
        trans.commit();
    }
    {
        // start a transaction
        Transaction trans = session.beginTransaction();

        // create an employee
        Employee tempEmployee = new Employee("Tony","Stark", "The Avengers");
        // save to database
        session.save(tempEmployee);
        
        // commit the transaction
        trans.commit();
    }
    
    // close session
    session.close();
    
}

I am a starter, and I don't know why "factory.getCurrentSession()" works differently from "factory.openSession()", yet.

Spermicide answered 21/2, 2022 at 10:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.