Page 2 of 3 FirstFirst 123 LastLast
Results 11 to 20 of 21

Thread: fastest way to batch insert w/ hibernate

  1. #11
    Join Date
    Jan 2007
    Posts
    139

    Default

    Quote Originally Posted by dejanp View Post
    Nope, that will not work. Hibernate doesn't care about instances of your service class, but instances of hibernate persistent classes referenced in multiple hibernate sessions.
    I don't completely understand what you mean here . . . there should be a way to allow multiple Spring Remoting clients to access a bean exposed through the Spring org.springframework.remoting.httpinvoker.HttpInvok erProxyFactoryBean.
    If in fact the scope=prototype can be applied to the proxied object, then there would be two separate beans, hence, two distinct collection objects, each tying to a distinct Hibernate Session, correct?

    Here is the method in the Service Bean exposed through the proxy and called from the Rich Client app through HTTP Invoker:

    Code:
    public class ImportXTFServiceImpl implements ImportXTFService{
        
    /** Creates a new instance of ImportXTFServiceImpl */
    public ImportXTFServiceImpl() {
    
    private static int saveCount = 0;
    private SessionFactory sessionFactory = null;
    private Session session = null;
    private Transaction transaction = null;
       
    public void setSessionFactory(SessionFactory sessionFactory) {
       this.sessionFactory = sessionFactory;
    }
    
    public void importBatch(Collection objs) {       
           
           session = this.sessionFactory.openSession();
           session.setCacheMode(CacheMode.IGNORE);
           session.getTransaction().begin();
                  
           for(Object o : objs) {           
               session.save(o);
               if(++saveCount % objs.size() == 0)
                {                       
                   session.getTransaction().commit();
                   session.clear();
                   saveCount = 0;
                }
           }       
           
           session.close();
        }
    Is there a property on the org.springframework.remoting.httpinvoker.HttpInvok erProxyFactoryBean bean that needs to be added which controls the scope of the bean exposed by the proxy, similar to how I have the scope=prototype setting on the bean def in the app context running on the server?

  2. #12
    Join Date
    Jan 2007
    Posts
    139

    Default

    ok, this is interesting . . .

    I just ran another test, only this time, the test ran over a local app context started in the same JVM as the unit test, as opposed to a separate JVM for the unit test and the web app containing the Spring Container. Two concurrent tests run at the same time did NOT result in the Illegal attempt to associate a collection with two open sessions exception when run locally in the same JVM, even when the scope on the ImportXTFServiceImpl bean was left at the default(singleton).

    So, it sounds like this is the result of separate JVMs and perhaps an equality issue of some sort? The exception here is a bit misleading I think.

    Why would running this in a separate JVM cause this problem? Is there a way to accomodate for this through the proxied object somehow?

  3. #13
    Join Date
    Jan 2007
    Posts
    139

    Default Problem "SolvEDD"

    Looks like I have a solution. Using TransactionTemplate did the trick, but I am still not certain exactly why. It may have something to do with way the following from the below snippet of code behaves in the Callback versus the Plain Old Hibernate approach:
    Code:
    public void setSessionFactory(SessionFactory sessionFactory) {
            this.sessionFactory = sessionFactory;
        }
        
        public SessionFactory getSessionFactory() {
            return this.sessionFactory;
        }
    
     . . .
    
    getSessionFactory().getCurrentSession().save(object);
    
     . . .
    Here is the refactored importBatch method:

    Code:
    public void importBatch(final Collection objs) {       
          this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {
             public void doInTransactionWithoutResult(TransactionStatus status) { 
               for(Object object : objs) {             
                 getSessionFactory().getCurrentSession().save(object);
               }
             }
          });
        }

  4. #14
    Join Date
    Sep 2004
    Posts
    1,086

    Default

    It's not the collection that you pass as a parameter the hibernate complains about (hibernate sees single instances in that collection, doesn't care at all if you pack them into a collection at some point or not). The error comes from the fact that some of the objects in that collection internally share a reference to some (other) collection.

  5. #15
    Join Date
    Jan 2007
    Posts
    139

    Default

    However there is still something going on based on the relationship between Hibernate and Spring that has caused this new behaviour. The use of TransactionTemplate, for one, has allowed concurrent requests to work where the "plain old Hibernate" approach and managing the session myself did not work. I understand what you are saying, and that is a pure Hibernate issue. So, what has resulted in this successfully working now? The way TransactionTemplate manages getCurrentSession()?

    Again, the root problem as I see it is an object in the collection(or one of it's objects which may be contained in a collection) was already associated to an open session. Using the plain hibernate approach, wiring up the bean w/an AOP interceptor and a txadvice w/propagation=NEVER and calling getCurrentSession() resulted in the error, where as the use of TransactionTemplate and getCurrentSession() works.

  6. #16
    Join Date
    Sep 2004
    Posts
    1,086

    Default

    Quote Originally Posted by mark_in_gr View Post
    Code:
    public class ImportXTFServiceImpl implements ImportXTFService{
        
    /** Creates a new instance of ImportXTFServiceImpl */
    public ImportXTFServiceImpl() {
    
    private static int saveCount = 0;
    private SessionFactory sessionFactory = null;
    private Session session = null;
    private Transaction transaction = null;
       
    public void setSessionFactory(SessionFactory sessionFactory) {
       this.sessionFactory = sessionFactory;
    }
    
    public void importBatch(Collection objs) {       
           
           session = this.sessionFactory.openSession();
           session.setCacheMode(CacheMode.IGNORE);
           session.getTransaction().begin();
                  
           for(Object o : objs) {           
               session.save(o);
               if(++saveCount % objs.size() == 0)
                {                       
                   session.getTransaction().commit();
                   session.clear();
                   saveCount = 0;
                }
           }       
           
           session.close();
        }
    The code above is most certainly not threadsafe. SessionFactory can safely be used as an instance variable, but not the Session. The scope "prototype" means a new instance is created, but per injection, not per request.

  7. #17
    Join Date
    Jan 2007
    Posts
    139

    Default

    Quote Originally Posted by dejanp View Post
    The code above is most certainly not threadsafe. SessionFactory can safely be used as an instance variable, but not the Session. The scope "prototype" means a new instance is created, but per injection, not per request.
    It wasn't my intention to make it not thread safe, in fact, I wanted to somehow mimic the behaviour derived from the use of getCurrentSession() when CurrentSessionContext was based on the ThreadLocalSessionContext implementation. But the way Spring overrides getCurrentSession() based on it's org.springframework.orm.hibernate3.LocalSessionFac toryBean implementation, the normal Hibernate getCurrentSession() behaviour is not observed.

  8. #18
    Join Date
    Jan 2007
    Posts
    139

    Default

    Quote Originally Posted by dejanp View Post
    The scope "prototype" means a new instance is created, but per injection, not per request.
    I don't believe that is entirely accurate based on the Spring Docs, which states that allong with being injected into another bean, any getBean() calls on the container will result in a new instance.

  9. #19
    Join Date
    Sep 2004
    Posts
    1,086

    Default

    Quote Originally Posted by mark_in_gr View Post
    I don't believe that is entirely accurate based on the Spring Docs, which states that allong with being injected into another bean, any getBean() calls on the container will result in a new instance.
    Yes, but invoker will not execute getBean() per request.

  10. #20
    Join Date
    Sep 2004
    Posts
    1,086

    Default

    Quote Originally Posted by mark_in_gr View Post
    It wasn't my intention to make it not thread safe, in fact, I wanted to somehow mimic the behaviour derived from the use of getCurrentSession() when CurrentSessionContext was based on the ThreadLocalSessionContext implementation. But the way Spring overrides getCurrentSession() based on it's org.springframework.orm.hibernate3.LocalSessionFac toryBean implementation, the normal Hibernate getCurrentSession() behaviour is not observed.
    To mimic ThreadLocalSessionContext you need to use ThreadLocal and not simple instance variable.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •