Announcement Announcement Module
Collapse
No announcement yet.
cleaning hibernate cache between dao tests Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • cleaning hibernate cache between dao tests

    Hi everybody.
    How do I force hibernate to ignore the cache when running several AbstractTransactionalDataSourceSpringContextTests after each other?
    Since I'm populating the DB with fresh data for each test I need hibernate to ignore all cache data.
    I tried with session.clear() but it is not enough
    Suggestions?

    Keywords:
    AbstractTransactionalDataSourceSpringContextTests
    Dbunit
    Spring 1.2
    Hibernate 2

  • #2
    So I think you are referring to the second-level cache? In that case you could use an alternative configuration with second-level cache disabled for your tests. Then you do not have to bother clearing it.

    Regards,
    Andreas

    Comment


    • #3
      That is an idea. How do I disable the cache? is there a hibernate property for that?
      Still wondering, is this the right way to go? I may be intrested in checking the effect of the cache in my tests. What I want is that tests do not (obvious) depend by each other but I don't want to run tests in an environment which is too different from the one I'm going to run in production.
      Currently I found a shortcut, which is to call sessionFactory.evict(Class) for each of the potential classes...but I don't really like it...

      Comment


      • #4
        Originally posted by robcos View Post
        That is an idea. How do I disable the cache? is there a hibernate property for that?
        Yes. See here

        However, as far as I know 2nd level cache is disabled by default.

        Originally posted by robcos View Post
        Still wondering, is this the right way to go? I may be intrested in checking the effect of the cache in my tests.
        Well, then you have to separate the cases where you want to include caching, and where you don't.

        Regards,
        Andreas

        Comment


        • #5
          Originally posted by Andreas Senft View Post
          Yes. See here
          This does not say much on how to disable it
          Originally posted by Andreas Senft View Post
          However, as far as I know 2nd level cache is disabled by default.
          Sure, but I want it to be enabled! I just need to purge it before my test runs.
          My test case has to be run with a clean, enabled but clean cache! Only in this way I can predict the test result

          Comment


          • #6
            Originally posted by robcos View Post
            This does not say much on how to disable it
            If you specify nothing, it is disabled. So if your configuration does not mention anything about caching, then you do not have any.

            Originally posted by robcos View Post
            My test case has to be run with a clean, enabled but clean cache! Only in this way I can predict the test result
            I wonder why? An application might _benefit_ from caching but it should never rely on it on a functional basis.

            Regards,
            Andreas

            Comment


            • #7
              Originally posted by Andreas Senft View Post
              If you specify nothing, it is disabled. So if your configuration does not mention anything about caching, then you do not have any.
              Then there is no way to disable it :-D Never mind, for this is not the case


              Originally posted by Andreas Senft View Post
              I wonder why? An application might _benefit_ from caching but it should never rely on it on a functional basis.
              That is exactly the point. Caching must not affect the functionality: How do I verify that the functionality is not affected by caching if I run the tests with disabled caching? It's like testing without caching and hope that it will work when the caching is on...

              Comment


              • #8
                Originally posted by Andreas Senft View Post
                I wonder why? An application might _benefit_ from caching but it should never rely on it on a functional basis.
                Because you might misapply caching. I had added caching to a recent DAO and my tests broke. My case was a little different because I was using Spring Modules against a JDBC class. But nonetheless adding caching did break my tests.

                Comment


                • #9
                  Originally posted by wpoitras View Post
                  Because you might misapply caching. I had added caching to a recent DAO and my tests broke. My case was a little different because I was using Spring Modules against a JDBC class. But nonetheless adding caching did break my tests.
                  Exactly, caching should not interfere with your functionality and you can only verify it if you run with your caching on (when you need it, of course)

                  Comment


                  • #10
                    Hey robcos,

                    I'm just throwing this out as a possibility. Here is an abstract class that requires a HibernateTemplate be injected and disables the second-level cache (by default):

                    Code:
                    package org.spring.forum;
                    
                    import org.hibernate.CacheMode;
                    import org.hibernate.HibernateException;
                    import org.hibernate.Session;
                    import org.springframework.orm.hibernate3.HibernateCallback;
                    import org.springframework.orm.hibernate3.HibernateTemplate;
                    import org.springframework.test.AbstractTransactionalSpringContextTests;
                    
                    public abstract class AbstractHibernateCacheIgnorableTest extends AbstractTransactionalSpringContextTests {
                    
                        /** Hibernate template to use for disabling second level cache. */
                        private HibernateTemplate hibernateTemplate;
                        
                        /** Whether to disable second level cache; defaults to true. */
                        private boolean disableSecondLevelCache = true;
                        
                        @Override
                        protected void onSetUpInTransaction() throws Exception {
                            if (this.disableSecondLevelCache) {
                                disableSecondLevelCache();
                            }
                        }
                        
                        /**
                         * Performs the actual disabling of the Hibernate second-level cache.
                         */
                        protected void disableSecondLevelCache() {
                            this.hibernateTemplate.execute(new HibernateCallback() {
                                public Object doInHibernate(Session session) throws HibernateException {
                                    session.setCacheMode(CacheMode.IGNORE);
                                    return null;
                                }
                            });     
                        }
                    
                        /**
                         * Sets the HibernateTemplate for disabling the Hibernate second-level
                         * cache.
                         * 
                         * @param hibernateTemplate the HibernateTemplate for disabling the
                         *            Hibernate second-level cache.
                         */
                        public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
                            this.hibernateTemplate = hibernateTemplate;
                        }
                    
                        /**
                         * Sets whether to disable the Hibernate second-level cache. Defaults to
                         * true.
                         * 
                         * @param disableSecondLevelCache whether to disable the Hibernate
                         *            second-level cache.
                         */
                        public void setDisableSecondLevelCache(boolean disableSecondLevelCache) {
                            this.disableSecondLevelCache = disableSecondLevelCache;
                        }
                    
                    }
                    Here is a sample applicationContext file that should work (note that there is a "hibernateTemplate" bean configured):

                    Code:
                    <?xml version="1.0" encoding="UTF-8"?>
                    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
                    
                    <beans>
                        
                        <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
                            <property name="location" value="jdbc.properties" />
                        </bean>
                        
                        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
                            <property name="driverClassName" value="${jdbc.driverClassName}" />
                            <property name="url" value="${jdbc.url}" />
                            <property name="username" value="${jdbc.username}" />
                            <property name="password" value="${jdbc.password}" />
                        </bean>
                        
                        <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
                            <property name="dataSource" ref="dataSource" />
                            <property name="mappingResources">
                                <list>
                                    <value>org/spring/forum/Team.hbm.xml</value>
                                    <value>org/spring/forum/Player.hbm.xml</value>
                                </list>
                            </property>
                            <property name="hibernateProperties">
                                <props>
                                    <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                                    <prop key="hibernate.show_sql">true</prop>
                                    <prop key="hibernate.generate_statistics">true</prop>
                                    <!--<prop key="hibernate.hbm2ddl.auto">create</prop>-->
                                </props>
                            </property>
                            <property name="eventListeners">
                                <map>
                                    <entry key="merge">
                                        <bean class="org.springframework.orm.hibernate3.support.IdTransferringMergeEventListener"/>
                                    </entry>
                                </map>
                            </property>
                        </bean>
                        
                        <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
                            <property name="sessionFactory" ref="sessionFactory"/>
                        </bean>
                        
                        <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
                            <property name="sessionFactory" ref="sessionFactory"/>
                        </bean>
                    
                    </beans>
                    The test class makes use of the Session.setCacheMode method and sets the cache mode to CacheMode.IGNORE.

                    I haven't tested this, but it might be a start...

                    -Arthur Loder
                    Last edited by Arthur Loder; Sep 29th, 2006, 10:23 AM.

                    Comment


                    • #11
                      Originally posted by Arthur Loder View Post
                      Hey robcos,

                      protected void disableSecondLevelCache() {
                      this.hibernateTemplate.execute(new HibernateCallback() {
                      public Object doInHibernate(Session session) throws HibernateException {
                      session.setCacheMode(CacheMode.IGNORE);
                      return null;
                      }
                      });
                      }
                      This disables the first level cache (you are running on the session object)
                      and again, I'm trying to clean the cache, not to disable it. The only solution seems to call sessionFactory.evict() for each of the classes that need are mapped using hibernate. A similar approach can be taken, although.
                      /Roberto

                      Comment


                      • #12
                        Clarification

                        Hi robcos,

                        Originally posted by robcos View Post
                        This disables the first level cache (you are running on the session object)
                        Actually, from the CacheMode API:
                        Controls how the session interacts with the second-level cache and query cache.
                        -Arthur Loder

                        Comment


                        • #13
                          Originally posted by Arthur Loder View Post
                          Hi robcos,



                          Actually, from the CacheMode API:
                          Controls how the session interacts with the second-level cache and query cache.
                          -Arthur Loder
                          Oh sorry, I missunderstood that. Thanks for pointing it out.

                          Comment


                          • #14
                            Updated code

                            Hi robcos,

                            Here is the original class with an updated disableSecondLevelCache() method so that it clears the cache rather than disabling the Session's use of the cache; it relies on casting the SessionFactory to SessionFactoryImpl:

                            Code:
                            package org.spring.forum;
                            
                            import java.util.Collection;
                            import java.util.Map;
                            
                            import org.hibernate.HibernateException;
                            import org.hibernate.Session;
                            import org.hibernate.cache.Cache;
                            import org.hibernate.impl.SessionFactoryImpl;
                            import org.springframework.orm.hibernate3.HibernateCallback;
                            import org.springframework.orm.hibernate3.HibernateTemplate;
                            import org.springframework.test.AbstractTransactionalSpringContextTests;
                            
                            public abstract class AbstractHibernateCacheClearableTest extends AbstractTransactionalSpringContextTests {
                            
                                /** Hibernate template to use for disabling second level cache. */
                                private HibernateTemplate hibernateTemplate;
                                
                                /** Whether to disable second level cache; defaults to true. */
                                private boolean disableSecondLevelCache = true;
                                
                                @Override
                                protected void onSetUpInTransaction() throws Exception {
                                    if (this.disableSecondLevelCache) {
                                        disableSecondLevelCache();
                                    }
                                }
                                
                                /**
                                 * Performs the actual disabling of the Hibernate second-level cache.
                                 */
                                protected void disableSecondLevelCache() {
                                     this.hibernateTemplate.execute(new HibernateCallback() {
                                        public Object doInHibernate(Session session) throws HibernateException {
                                            SessionFactoryImpl sessionFactoryImpl = (SessionFactoryImpl)session.getSessionFactory();
                                            Map cacheRegionsMap = sessionFactoryImpl.getAllSecondLevelCacheRegions();
                                            Collection<Cache> cacheRegions = cacheRegionsMap.values();
                                            for (Cache cache : cacheRegions) {
                                                cache.clear();
                                            }
                                            return null;
                                        }
                                    });     
                                }
                            
                                /**
                                 * Sets the HibernateTemplate for disabling the Hibernate second-level
                                 * cache.
                                 * 
                                 * @param hibernateTemplate the HibernateTemplate for disabling the
                                 *            Hibernate second-level cache.
                                 */
                                public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
                                    this.hibernateTemplate = hibernateTemplate;
                                }
                            
                                /**
                                 * Sets whether to disable the Hibernate second-level cache. Defaults to
                                 * true.
                                 * 
                                 * @param disableSecondLevelCache whether to disable the Hibernate
                                 *            second-level cache.
                                 */
                                public void setDisableSecondLevelCache(boolean disableSecondLevelCache) {
                                    this.disableSecondLevelCache = disableSecondLevelCache;
                                }
                            
                                /**
                                 * Checks whether disabling of Hibernate second-level cache should occur.
                                 * Can be overridden by subclasses to return false to allow second-level
                                 * cache usage (or the disableSecondLevelCache can be set to false, either
                                 * programmatically or through injection).
                                 * 
                                 * @return whether disabling of Hibernate second-level cache should occur.
                                 */
                                public boolean isDisableSecondLevelCache() {
                                    return this.disableSecondLevelCache;
                                }
                                
                            }
                            The above code calls Cache.clear for every cache region. An alternative method implementation that might work is defined below, which retrieves all mapped classes and calls Session.evictEntity for each:

                            Code:
                            protected void disableSecondLevelCache() {                                                                                                                         
                                this.hibernateTemplate.execute(new HibernateCallback() {                                  
                                    public Object doInHibernate(Session session) throws HibernateException {                 
                                        SessionFactoryImpl sessionFactoryImpl = (SessionFactoryImpl)session.getSessionFactory();
                                        // Every persistent class extends java.lang.Object, so the following call will return all persistent classes
                                        String[] persistentClasses = sessionFactoryImpl.getImplementors("java.lang.Object");    
                                        for (String persistentClass : persistentClasses) {                             
                                            sessionFactoryImpl.evictEntity(persistentClass);                              
                                        }                                                                              
                                        return null;                                                                            
                                    }                                                                                  
                                });                                                                                       
                            }
                            The relevant APIs are SessionFactoryImpl.getAllSecondLevelCacheRegions and SessionFactoryImpl.getImplementors.

                            I hope one of those implementations actually works!

                            -Arthur Loder
                            Last edited by Arthur Loder; Oct 4th, 2006, 11:28 AM.

                            Comment


                            • #15
                              Originally posted by Arthur Loder View Post
                              Hi robcos,
                              Here is the original class with an updated disableSecondLevelCache() method so that it clears the cache rather than disabling the Session's use of the cache; it relies on casting the SessionFactory to SessionFactoryImpl:
                              I like it, thank you!
                              Anyway, it is maybe safer to use the interface "SessionFactoryImplementor" instead of the class "SessionFactoryImpl"

                              /roberto

                              Comment

                              Working...
                              X