-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 
Author Message
 Post subject: Multithread issues in using Hibernate SessionFactory
PostPosted: Sat Feb 12, 2011 1:46 am 
Newbie

Joined: Sat Feb 12, 2011 1:43 am
Posts: 3
Updated the question

1) Have a table 'temp' ..
Code:
CREATE TABLE `temp` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `student_id` bigint(20) unsigned NOT NULL,
  `current` tinyint(1) NOT NULL DEFAULT '1',
  `closed_at` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `unique_index` (`student_id`,`current`,`closed_at`),
  KEY `studentIndex` (`student_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

The corresponding Java pojo is http://pastebin.com/JHZwubWd . This table has a unique constraint such that only one record for each student can be active.

2) I have a test code which does try to continually add records for a student ( each time making the older active one as inactive and adding a new active record) and also in a different thread accessing some random ( non-related ) table.
Code:
public static void main(String[] args) throws Exception {
        final SessionFactory sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        int runs = 0;
        while(true) {
            Temp testPojo = new Temp();
            testPojo.setStudentId(1L);
            testPojo.setCurrent(true);
            testPojo.setClosedAt(new Date(0));
            add(testPojo, sessionFactory);
            Thread.sleep(1500);

            executorService.submit(new Callable<Object>() {
                @Override
                public Object call() throws Exception {
                    Session session = sessionFactory.openSession();
                    // Some dummy code to print number of users in the system.
                    // Idea is to "touch" the DB/session in this background
                    // thread.
                    System.out.println("No of users: " + session.createCriteria(User.class).list().size());
                    session.close();
                    return null;
                }
            });
            if(runs++ > 100) {
                break;
            }
        }

        executorService.shutdown();
        executorService.awaitTermination(1, TimeUnit.MINUTES);
    }

private static void add(final Temp testPojo, final SessionFactory sessionFactory) throws Exception {
        Session dbSession = null;
        Transaction transaction = null;
        try {
            dbSession = sessionFactory.openSession();
            transaction = dbSession.beginTransaction();

            // Set all previous state of the student as not current.
            List<Temp> oldActivePojos = (List<Temp>) dbSession.createCriteria(Temp.class)
                    .add(Restrictions.eq("studentId", testPojo.getStudentId())).add(Restrictions.eq("current", true))
                    .list();
            for(final Temp oldActivePojo : oldActivePojos) {
                oldActivePojo.setCurrent(false);
                oldActivePojo.setClosedAt(new Date());

                dbSession.update(oldActivePojo);
                LOG.debug(String.format("  Updated old state as inactive:%s", oldActivePojo));
            }
            if(!oldActivePojos.isEmpty()) {
                dbSession.flush();
            }

            LOG.debug(String.format("  saving state:%s", testPojo));
            dbSession.save(testPojo);
            LOG.debug(String.format("  new state saved:%s", testPojo));

            transaction.commit();

        }catch(Exception exception) {
            LOG.fatal(String.format("Exception in adding state: %s", testPojo), exception);
            transaction.rollback();
        }finally {
            dbSession.close();
        }
    }


Upon running the code, after a few runs, I am getting an index constraint exception. It happens because for some strange reason, it does not find the latest active record but instead some older stale active record and tries marking it as inactive before saving ( though the DB actually has a new active record already present).

Notice that both the code share the same sessionfactory and the both code works on a totally different tables. My guess is that some internal cache state gets dirty. If I use 2 different sessionfactory for the foreground and background thread, it works fine.

Could some one help figure out this issue?

Thanks!


Last edited by rahulj on Sat Feb 12, 2011 5:21 am, edited 2 times in total.

Top
 Profile  
 
 Post subject: Re: Multithread issues in using Hibernate SessionFactory
PostPosted: Sat Feb 12, 2011 5:20 am 
Newbie

Joined: Sat Feb 12, 2011 1:43 am
Posts: 3
Update: Disabling the background thread, obviously, makes the code obviously work fine. Wondering if there is an issue using a single sessionfactory across threads?


Top
 Profile  
 
 Post subject: Re: Multithread issues in using Hibernate SessionFactory
PostPosted: Sat Feb 12, 2011 7:38 am 
Newbie

Joined: Sat Feb 12, 2011 1:43 am
Posts: 3
Another weird thing is that in the background thread ( where I print the no of users), if I wrap it in a transaction ( even though it is only a read operation), the code works fine! Sp looks like I need to wrap all DB operations ( irrespective of read / write ) in a transaction for it to work in a multithreaded environment.

Could some one point out what is the correct way to solve this?


Top
 Profile  
 
 Post subject: Re: Multithread issues in using Hibernate SessionFactory
PostPosted: Sat Feb 12, 2011 9:31 am 
Newbie

Joined: Sun May 09, 2010 4:48 am
Posts: 11
Why do you reattach an already attached object with dbSession.update(oldActivePojo)? This should not be necessary.
Calling the setter on the attached pojos already performs the update.
What is the if(!oldActivePojos.isEmpty()) dbSession.flush() good for? You are comitting the transaction two lines later anyways?
I would recommend to always call session.beginTransaction() to set autocommit mode to false and avoid many short transactions.
For example for the call session.createCriteria(User.class).list().size().
See http://community.jboss.org/wiki/Non-tra ... commitmode for that matter.
And SessionFactory should be thread-safe from what I can recall.


Top
 Profile  
 
 Post subject: Re: Multithread issues in using Hibernate SessionFactory
PostPosted: Mon Feb 14, 2011 3:55 pm 
Hibernate Team
Hibernate Team

Joined: Fri Oct 05, 2007 4:47 pm
Posts: 2536
Location: Third rock from the Sun
mueven is right, 1)SessionFactory has no known threading issues 2)you must use transactions 3)you must make sure to close sessions properly both in case of success and failure of the transaction

_________________
Sanne
http://in.relation.to/


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.