Announcement Announcement Module
Collapse
No announcement yet.
Performance problem when switching from pure Hibernate Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Performance problem when switching from pure Hibernate

    Hi,

    I'm currently in the process of switching my persistence layer from pure Hibernate to Spring+Hibernate. That means instead of constructing the SessionFactory myself I let Spring do the configuration. My DAO implementation uses HibernateDaoSupport.

    Now the problem is: a single saveOrUpdate(object) that took about 500 ms now needs more than 9 seconds. The data are the same, the mapping is the same, Hibernate's configuration should be the same (copied and adapted from hibernate.cfg.xml to applicationContext.xml).

    I'm clueless what the reason for this performance problem is and where to look for a solution.

    Any hints are welcome,
    Oliver

  • #2
    Spring shouldn't affect Hibernate's performance. Can you isolate the code and post it.

    Comment


    • #3
      Oliver,

      This does sound quite bizarre. However, you might want to check whether you are using the same datasource configuration. You should also investigate the P6Spy tool to see whether both versions of the application are actually executing the same SQL.

      Post back with you findings.

      Rob

      Comment


      • #4
        Hmm, having show_sql set to true displays subtle differences. But the overall amount of SQL queries is nearly the same.

        The point is: without Spring the SQL log shows up very fast, with Spring I can nearly watch each query one after another.

        Regarding the DataSource:
        (note: this is currently a non-web standalone test)

        In hibernate.cfg.xml:
        Code:
              <session-factory>
                 <property name="hibernate.dialect"
                    >net.sf.hibernate.dialect.MySQLDialect</property>
                 <property name="hibernate.connection.driver_class"
                    >com.mysql.jdbc.Driver</property>
                 <property name="hibernate.connection.url"
                   >jdbc&#58;mysql&#58;//141.***&#58;3306/myDB?relaxAutoCommit=true</property>
                 <property name="hibernate.connection.username">xxx</property>
                 <property name="hibernate.connection.password">xxx</property>
        
                ...
        In applicationContext.xml:
        Code:
           <bean id="myDataSource" 
                 class="org.springframework.jdbc.datasource.DriverManagerDataSource">
              <property name="driverClassName">
                 <value>com.mysql.jdbc.Driver</value>
              </property>
              <property name="url">
                 <value>jdbc&#58;mysql&#58;//141.***&#58;3306/myDB?relaxAutoCommit=true</value>
              </property>
              <property name="username">
                 <value>xxx</value>
              </property>
              <property name="password">
                 <value>xxx</value>
              </property>
           </bean>
           
           <!-- Hibernate SessionFactory -->
           <bean id="mySessionFactory" 
              class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
              <property name="dataSource">
                 <ref local="myDataSource"/>
              </property>
              <property name="hibernateProperties">
                 <props>
                    <prop key="hibernate.dialect">net.sf.hibernate.dialect.MySQLDialect</prop>
                    <prop key="hibernate.connection.pool_size">1</prop>
                    <!-- etc other properties -->
        
                     ...
        
           <bean id="objectDAO" class="MyDAOImpl">
              <property name="sessionFactory">
                 <ref bean="mySessionFactory" />
              </property>
           </bean>
        
        ...
        Well, the Java code isn't any spectacular (both in main(String[] a))
        formerly:
        Code:
                Session s = MyHibernateHelper.currentSession&#40;&#41;;
                Transaction tx = s.beginTransaction&#40;&#41;;
                try &#123;
                    s.saveOrUpdate&#40;o&#41;;
                    tx.commit&#40;&#41;;
                &#125;
                catch &#40;HibernateException e&#41; &#123;
                    tx.rollback&#40;&#41;;
                &#125;
                MyHibernateHelper.closeSession&#40;&#41;;
        Now:
        Code:
                SessionFactory sessionFactory = 
                    &#40;SessionFactory&#41;MyDAOHelper.getBean&#40;"mySessionFactory"&#41;;
                Session s = SessionFactoryUtils.getSession&#40;sessionFactory, true&#41;;
                myDAO = &#40;MyDAO&#41;MyDAOHelper.getBean&#40;"objectDAO"&#41;;
                myDAO.save&#40;o&#41;;
                s.flush&#40;&#41;;
                SessionFactoryUtils.closeSessionIfNecessary&#40;s, sessionFactory&#41;;
        
             and in MyDAOImpl.save&#40;o&#41;&#58;
        
                getHibernateTemplate&#40;&#41;.saveOrUpdate&#40;o&#41;;
        I think this is all standard code.
        (Sorry for the length of this reply)

        I'm going to check P6Spy in any case, thanks.
        Oliver

        Comment


        • #5
          I would start by switching to a different data source - org.springframework.jdbc.datasource.DriverManagerD ataSource is more geared for testing and it does not do any connection pooling. Try using Commons DBCP or C3P0 pooled connection data source. If you want to run a quick test - use org.springframework.jdbc.datasource.SingleConnecti onDataSource - it keeps a connection open for the length of its life or until destroy() is called on it.

          Comment


          • #6
            Originally posted by trisberg
            Try using Commons DBCP or C3P0 pooled connection data source. If you want to run a quick test - use org.springframework.jdbc.datasource.SingleConnecti onDataSource - it keeps a connection open for the length of its life or until destroy() is called on it.
            Hi Thomas,
            unfortunately this doesn't change anything.
            org.springframework.jdbc.datasource.SingleConnecti onDataSource refused to work for me ("connection was closed"), and org.apache.commons.dbcp.BasicDataSource results in the same performance.

            Is there anything that happens between the single SQL statements in Spring? Can I trace this by some means?

            Oliver

            Comment


            • #7
              addendum:
              I got org.springframework.jdbc.datasource.SingleConnecti onDataSource working by adding
              <property name="suppressClose"><value>true</value></property>
              but again, no change in the performance.

              Comment


              • #8
                What is the lifetime of the appcontext this is all coming out of? You are not by any chance recreating the entire appcontext (including the expensive to create SessionFactory) on each usage of the DAO, are you?

                Comment


                • #9
                  No, the appcontext is created only once.

                  I guess it must have something to do with the way hibernate will be configured. I've transfered the entries of hibernate.cfg.xml to applicationContext.xml in the following way:

                  Code:
                     <bean id="mySessionFactory" 
                        class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
                        <property name="dataSource">
                           <ref local="myDataSource"/>
                        </property>
                        <property name="mappingResources">
                           <list> ... </list>
                        </property>
                        <property name="hibernateProperties">
                           <props>
                              <prop key="hibernate.dialect">net.sf.hibernate.dialect.MySQLDialect</prop>
                              <prop key="hibernate.connection.pool_size">1</prop>
                              <prop key="hibernate.show_sql">true</prop>
                              <prop key="hibernate.query.substitutions"
                                 >true 1, false 0, yes 'Y', no 'N'</prop>
                           
                              <!-- JDBC -->
                              <prop key="hibernate.jdbc.batch_size">0</prop>
                              <prop key="hibernate.jdbc.batch_versioned_data">true</prop>
                              <prop key="hibernate.jdbc.use_streams_for_binary">true</prop>
                           
                              <prop key="hibernate.max_fetch_depth">1</prop>
                           
                              <!-- Second level cache -->
                              <prop key="hibernate.cache.region_prefix"
                                 >hibernate.test</prop>
                              <prop key="hibernate.cache.use_query_cache">true</prop>
                              <prop key="hibernate.cache.provider_class"
                                 >net.sf.hibernate.cache.EhCacheProvider</prop>
                           </props>
                        </property>
                     </bean>
                  However, setting Hibernate's logging to debug, I see these lines in my original configuration (using Hibernate only with hibernate.cfg.xml):

                  Code:
                  DEBUG  net.sf.hibernate.cfg.Configuration.addProperties&#40;Configuration.java&#58;853&#41; - hibernate.connection.pool_size=1
                  DEBUG  net.sf.hibernate.cfg.Configuration.addProperties&#40;Configuration.java&#58;853&#41; - hibernate.show_sql=true
                  DEBUG  net.sf.hibernate.cfg.Configuration.addProperties&#40;Configuration.java&#58;853&#41; - hibernate.query.substitutions=true 1, false 0, yes 'Y', no 'N'
                  DEBUG  net.sf.hibernate.cfg.Configuration.addProperties&#40;Configuration.java&#58;853&#41; - hibernate.jdbc.batch_size=0
                  DEBUG  net.sf.hibernate.cfg.Configuration.addProperties&#40;Configuration.java&#58;853&#41; - hibernate.jdbc.batch_versioned_data=true
                  DEBUG  net.sf.hibernate.cfg.Configuration.addProperties&#40;Configuration.java&#58;853&#41; - hibernate.jdbc.use_streams_for_binary=true
                  DEBUG  net.sf.hibernate.cfg.Configuration.addProperties&#40;Configuration.java&#58;853&#41; - hibernate.max_fetch_depth=1
                  DEBUG  net.sf.hibernate.cfg.Configuration.addProperties&#40;Configuration.java&#58;853&#41; - hibernate.cache.region_prefix=hibernate.test
                  DEBUG  net.sf.hibernate.cfg.Configuration.addProperties&#40;Configuration.java&#58;853&#41; - hibernate.cache.use_query_cache=true
                  DEBUG  net.sf.hibernate.cfg.Configuration.addProperties&#40;Configuration.java&#58;853&#41; - hibernate.cache.provider_class=net.sf.hibernate.cache.EhCacheProvider
                  These lines don't appear if I use Hibernate via Spring.

                  Is this probably a Hibernate issue and should be asked on the Hibernate forum?

                  Oliver
                  (obviously a newbie to all this stuff)

                  Comment


                  • #10
                    If your code is working fine outside of Spring then this is Spring specific to the extent that I think your actual config or environment is messed up in Spring somehow. Spring is just a wrapper over HIbernate, so the same setup should run with the same speed, but in your case something is different.

                    W/regards to the logging statements you mention, it's nothing to worry about. In the case of hibernate parsing the hibernate.cfg.xml file, it logs each property it reads from the file. In the case of feeding them in via Spring, Spring just calls Hibernate's Configuration object's addProperties method, which doesn't log all the properties. Even with Spring you could just point it (via the configLocation property) to a hibernate.cfg.xml file if you wanted, and you would see the logging...

                    I'm sort of stumped here as I haven't seen anything like this before, and as I said, all Spring is doing is wrapping... Are you sure btw than in the Spring case your entire app is not running slowly for some reason (i.e. you have some thread or something consuming most resources)?

                    Comment


                    • #11
                      Thanks for your support.

                      I used the configLocation property (to be sure), but still the same.
                      I started a thread on the Hibernate Forum (http://forum.hibernate.org/viewtopic.php?p=2217649), but no answer yet.

                      However, analyzing Hibernate's log output I noted the following.

                      If I use Hibernate directly, the log contains a message
                      Code:
                      INFO   net.sf.hibernate.connection.DriverManagerConnectionProvider.configure&#40;DriverManagerConnectionProvider.java&#58;42&#41; - Using Hibernate built-in connection pool &#40;not for production use!&#41;
                      (and additionallly some pooling configuration logs)

                      Using spring, I can find only one entry instead
                      Code:
                      INFO   net.sf.hibernate.connection.ConnectionProviderFactory.newConnectionProvider&#40;ConnectionProviderFactory.java&#58;53&#41; - Initializing connection provider&#58; org.springframework.orm.hibernate.LocalDataSourceConnectionProvider
                      Could this be the source of my performance problem? Is the LocalDataSourceConnectionProvider possibly not correctly configured in my case?

                      Oliver

                      Comment


                      • #12
                        No, LocalDataSourceConnectinProvider is just a Hibernate standard ConnectionProvider implementation which allows Hibernate to get a new Connection when it needs one, in this case from the DataSource that Spring is managing...

                        In your Hibernate-only scenario, Hibernate itself is simply using its own ConnectionProvider implementation that gets a connection via the non-pooling DriveManager approach.

                        Comment


                        • #13
                          Did you get the solution ?

                          Hi.. I am also facing some issues. I am facing severe performance degradation when I use spring+hibernate compared to Hinernate alone. Would really appreciate if you can throw some insight.

                          Thanks in advance

                          Comment


                          • #14
                            The performance problems I've seen in the past have typically been related to configuration rather than anything to do with Spring. I'd start with a simple example and go from there. Is it possible you can post what you are trying to do?

                            Comment

                            Working...
                            X