Re: Using Hibernate Second Level Cache as a Distributed Cache From One Configuration

1,344 views
Skip to first unread message

Barry Lagerweij

unread,
Jan 16, 2013, 3:46:05 AM1/16/13
to haze...@googlegroups.com
When you use Hibernate SessionFactory, instead of specifying the class-name of your provider, you can also pass a reference like this:

    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" depends-on="cacheManager" autowire="constructor">
        <property name="cacheRegionFactory" ref="regionFactory"/>
    </bean>

When you use JPA, this is not (yet) available. As a workaround, you can extend the JPA Vendor Adapter:

        <property name="jpaVendorAdapter">
            <bean class="my.example.hibernate.HibernateJpaVendorAdapter">
                <property name="regionFactory" ref="regionFactory"/>
            </bean>
        </property>

And use the following extended adapter:

public class HibernateJpaVendorAdapter extends org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter {

    private static final ThreadLocal<RegionFactory> configTimeCacheRegionFactoryHolder = new ThreadLocal<RegionFactory>();

    private RegionFactory regionFactory;

    @Override
    public Map<String, Object> getJpaPropertyMap() {
        Map<String,Object> propertyMap = super.getJpaPropertyMap();
        if (regionFactory != null) {
            propertyMap.put(Environment.CACHE_REGION_FACTORY,RegionFactoryProxy.class.getName());
            configTimeCacheRegionFactoryHolder.set(regionFactory);
        }
        return propertyMap;
    }

    public void setRegionFactory(RegionFactory regionFactory) {
        this.regionFactory = regionFactory;
    }

    @Override
    public void postProcessEntityManagerFactory(EntityManagerFactory emf) {
        configTimeCacheRegionFactoryHolder.remove();
        super.postProcessEntityManagerFactory(emf);
    }

    public static RegionFactory getConfigTimeCacheRegionFactoryHolder() {
        return configTimeCacheRegionFactoryHolder.get();
    }
}

BTW, the code above was simply copied from the Hibernate AnnotationSessionFactoryBean.

Barry


On Wed, Jan 16, 2013 at 7:46 AM, <cgsw...@gmail.com> wrote:
Hello, 

My application is using Hazelcast as a second level through it's JPA/Spring configuration file:

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
       
<property name="dataSource" ref="dataSource" />
       
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
       
<property name="persistenceUnitName" value="MyApp" />
       
<property name="jpaProperties">
           
<props>
               
<prop key="hibernate.use_sql_comments">${jpa.vendor.sqlcomments}</prop>
               
<prop key="hibernate.generate_statistics">${jpa.vendor.generate.statistics}</prop>
               
<prop key="hibernate.archive.autodetection">class</prop>
               
<prop key="hibernate.cache.use_second_level_cache">${cache.use}</prop>
               
<prop key="hibernate.cache.provider_class">${cache.provider}</prop>
               
<prop key="hibernate.cache.use_query_cache">${cache.use.query}</prop>
               
<prop key="hibernate.cache.use_minimal_puts">${cache.use.minimal.puts}</prop>
               
<prop key="hibernate.cache.hazelcast.configuration_file_path">hazelcast-${cache.group.name}.xml</prop>
           
</props>
       
</property>
   
</bean>

I also configure my distributed cache in my Spring config like so:

<hz:hazelcast id="instance">....</hz:hazelcast>
<hz:hibernate-cache-provider id="cacheProvider" instance-ref="instance"/>
<hz:hibernate-region-factory id="regionFactory" instance-ref="instance"/>

This creates two cache groups (one for the hibernate and one for the id="instance").  Is there a way to consolidate this into one?  I noticed others requesting this capability and I wasn't sure how to check to see if it was ever implemented....thanks!

--
You received this message because you are subscribed to the Google Groups "Hazelcast" group.
To post to this group, send email to haze...@googlegroups.com.
To unsubscribe from this group, send email to hazelcast+...@googlegroups.com.
Visit this group at http://groups.google.com/group/hazelcast?hl=en-US.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Barry Lagerweij

unread,
Jan 16, 2013, 2:20:10 PM1/16/13
to haze...@googlegroups.com
Yes, indeed.

Hibernate expects a property with a classname, so that Hibernate can instantiate that class. However, the 'Spring' way would be to autowire beans.

I forgot to include the wrapper class:

public class RegionFactoryProxy implements RegionFactory {

    RegionFactory proxy;

    public RegionFactoryProxy() {
        proxy = HibernateJpaVendorAdapter.getConfigTimeCacheRegionFactoryHolder();
    }

    @Override
    public void start(Settings settings, Properties properties) throws CacheException {
        proxy.start(settings,properties);
    }

    @Override
    public void stop() {
        proxy.stop();
    }

    @Override
    public boolean isMinimalPutsEnabledByDefault() {
        return proxy.isMinimalPutsEnabledByDefault();
    }

    @Override
    public AccessType getDefaultAccessType() {
        return proxy.getDefaultAccessType();
    }

    @Override
    public long nextTimestamp() {
        return proxy.nextTimestamp();
    }

    @Override
    public EntityRegion buildEntityRegion(String regionName, Properties properties, CacheDataDescription metadata) throws CacheException {
        return proxy.buildEntityRegion(regionName, properties, metadata);
    }

    @Override
    public NaturalIdRegion buildNaturalIdRegion(String regionName, Properties properties, CacheDataDescription metadata) throws CacheException {
        return proxy.buildNaturalIdRegion(regionName, properties, metadata);
    }

    @Override
    public CollectionRegion buildCollectionRegion(String regionName, Properties properties, CacheDataDescription metadata) throws CacheException {
        return proxy.buildCollectionRegion(regionName, properties, metadata);
    }

    @Override
    public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties) throws CacheException {
        return proxy.buildQueryResultsRegion(regionName, properties);
    }

    @Override
    public TimestampsRegion buildTimestampsRegion(String regionName, Properties properties) throws CacheException {
        return proxy.buildTimestampsRegion(regionName, properties);

Barry Lagerweij

unread,
Jan 17, 2013, 2:33:30 AM1/17/13
to haze...@googlegroups.com
We're using Spring 3.1.2 here, this might explain the difference in the constructor. Which Spring version are you using ?

Indeed, you do not need the 'hibernate.cache.region.factory_class' property, since the Java code will inject this property:

        if (regionFactory != null) {
            propertyMap.put(Environment.CACHE_REGION_FACTORY,RegionFactoryProxy.class.getName());
            configTimeCacheRegionFactoryHolder.set(regionFactory);
        }

The other hibernate properties in the jpaProperties are still used. In fact, you seem to missing this property:
                <prop key="javax.persistence.sharedCache.mode">ENABLE_SELECTIVE</prop>
You might also want to consider disabling the query cache, since that might have an adverse effect on your performance (google for 'Hibernate query cache considered harmful').

Since we were not happy with the 'distributed' 2nd level cache provided by Hazelcast, we decided to implement ours based upon EHCache, while we're still using Hazelcast for peer discovery and peer notification (we use a Hazelcast topic to send key invalidations to all nodes), which is why we needed a single Hazelcast instance.

Barry


On Thu, Jan 17, 2013 at 12:43 AM, <cgsw...@gmail.com> wrote:
Thanks, everything seems better, but I don't think the second level cache is working.  I believe I have something configured wrong.  I don't see any of my database entity classes in the startup log.  I use to see the below at startup for each object:

Jan 16, 2013 9:32:07 PM com.hazelcast.hibernate.provider.HazelcastCache
INFO
: Creating new HazelcastCache with region name: com.api.model.WirelessCarrier

I had to add the below constructor to the new RegionFactoryProxy class in order for the web app to start up   

 public RegionFactoryProxy(final Properties properties) {
       
this();
   
}

I removed the <property name="hibernate.cache.region.factory_class">com.hazelcast.hibernate.HazelcastCacheRegionFactory</property> from my entityManagerFactory bean's jpaProperties.  Was that correct?  Are any of the spring config's "hibernate..." jpaProperties even used with this JPA based implementation?   

Spring Config:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
       
<property name="dataSource" ref="dataSource" />
       
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />

       
<property name="persistenceUnitName" value="Spot" />
       
<property name="jpaProperties"> <!-- these needed? -->

           
<props>
               
<prop key="hibernate.use_sql_comments">${jpa.vendor.sqlcomments}</prop>
               
<prop key="hibernate.generate_statistics">${jpa.vendor.generate.statistics}</prop>
               
<prop key="hibernate.archive.autodetection">class</prop>

               
<prop key="hibernate.cache.use_second_level_cache">true</prop>
               
<prop key="hibernate.cache.use_query_cache">true</prop>
               
<prop key="hibernate.cache.use_minimal_puts">true</prop>                          
           
</props>
       
</property>
   
</bean>

<!-- my app's custom implemenetation of org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter-->
<bean id="jpaVendorAdapter" class="myapp.api.service.impl.HibernateJpaVendorAdapter">
       
<property name="regionFactory" ref="regionFactory"/>
       
<property name="showSql" value="${jpa.vendor.showsql}" />
       
<property name="generateDdl" value="${jpa.vendor.generate.ddl}" />
       
<property name="databasePlatform" value="${jpa.vendor.dialect}" />
   
</bean>


<hz:hazelcast id="instance">
       
<hz:config>...</hz:config>
</hz:hazelcast>



<hz:hibernate-region-factory id="regionFactory" instance-ref="instance"/>
Reply all
Reply to author
Forward
0 new messages