DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • How Spring and Hibernate Simplify Web and Database Management
  • Enhanced Query Caching Mechanism in Hibernate 6.3.0
  • Choosing the Right Caching Strategy
  • Implement Hibernate Second-Level Cache With NCache

Trending

  • The Smart Way to Talk to Your Database: Why Hybrid API + NL2SQL Wins
  • How To Build Resilient Microservices Using Circuit Breakers and Retries: A Developer’s Guide To Surviving
  • Vibe Coding With GitHub Copilot: Optimizing API Performance in Fintech Microservices
  • Integration Isn’t a Task — It’s an Architectural Discipline
  1. DZone
  2. Data Engineering
  3. Data
  4. Setting Up Distributed Infinispan Cache with Hibernate and Spring

Setting Up Distributed Infinispan Cache with Hibernate and Spring

A pretty typical setup–a Spring and Hibernate application that requires a distributed cache. But it turns out to be not so trivial to setup.

By 
Bozhidar Bozhanov user avatar
Bozhidar Bozhanov
·
May. 26, 16 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
16.9K Views

Join the DZone community and get the full member experience.

Join For Free

You obviously need cache. There are options to do that with EhCache, Hazelcast, Infinispan, memcached, Redis, AWS’s ElastiCache and some others. However, EhCache supports only replicated and not distributed cache, and Hazelcast does not yet work with the latest version of Hibernate. Infinispan and Hazelcast support consistent hashing, so the entries live only on specific instance(s), rather than having a full copy of all the cache on the heap of each instance. ElastiCache is AWS-specific, so Infinispan seems the most balanced option with the Spring / Hibernate setup.

So, let’s first setup the Hibernate 2nd-level cache. The official documentation for Infinispan is not the top Google result—it is usually either very old documentation, or just 2 versions old documentation. You’d better open the latest one from the homepage.

Some of the options below are rather “hidden,” and I couldn’t find them easily in the documentation or in existing “how-to”s.

First, add the relevant dependencies to your dependency manager configuraton. You’ll need infinispan-core, infinispan-spring and hibernate-infinispan. Then in your configuration file (whichever it is – in my case it is jpa.xml, a spring file that defines the JPA properties) configure the following:

<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.infinispan.InfinispanRegionFactory</prop>
<prop key="hibernate.cache.inifinispan.statistics">true</prop>
<prop key="hibernate.cache.infinispan.cfg">infinispan.xml</prop>
<prop key="hibernate.cache.infinispan.query.cfg">distributed-query</prop>

These settings enable 2nd-level cache and query cache, using the default region factory (we’ll see why that may need to be changed to a custom one later), enable statistics, point to an infinispan.xml configuraton file and change the default name for the query cache in order to be able to use a distributed one (by default it’s “local-cache”). Of course, you can externalize all these to a .properties file.

Then, at the root of your classpath (src/main/resources) create infinispan.xml:

<?xml version="1.0" encoding="UTF-8"?>
<infinispan xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="urn:infinispan:config:8.1 http://www.infinispan.org/schemas/infinispan-config-8.1.xsd
 urn:infinispan:config:store:jdbc:8.0 http://www.infinispan.org/schemas/infinispan-cachestore-jpa-config-8.0.xsd"
 xmlns="urn:infinispan:config:8.1">
 <jgroups>
 <stack-file name="external-file" path="${jgroups.config.path}" /> 
 </jgroups>
 <cache-container default-cache="default" statistics="true">
 <transport stack="external-file" />
 <distributed-cache-configuration name="entity" statistics="true" />
 <distributed-cache-configuration name="distributed-query" statistics="true" />
 </cache-container>
</infinispan>

This expects -Djgroups.config.path to be passed to the JVM to point to a jgroups configuration. Depending on whether you use your own setup or AWS, there are multiple options. Here you can find config files for EC2, Google cloud, and basic UDP and TCP mechanism. These should be placed outside the project itself, because locally you most likely don’t want to use S3_PING (S3 based mechanism for node detection), and values may vary between environments.

If you need statistics (and it’s good to have them) you have to enable them both at cache-container level and at cache-level. I actually have no idea what the statistics option in the hibernate properties is doing – it didn’t change anything for me.

Then you define each of your caches. Your entities should be annotated with something like

@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "user")
public class User { .. }

And then Infinispan creates caches automatically. They can all share some default settings, and these defaults are defined for the cache named “entity”. Took me a while to find that out, and I finally got an answer on StackOverflow. The last thing is the query cache (using the name we defined in the hibernate properties). Note the “distributed-cache-configuration” element—that way you explicitly say “this (or all) cache(s) must be distributed” (they will use the transport mechanism specified in the jgroups file. Unfortunately I didn’t figure a way to have a default fallback jgroups file, so the -Djgroups.config.path is mandatory.)

You can define entity-specific properties using <distributed-cache-configuration name="user" /> for example (check the autocomplete from the XSD to see what configuration options you have (and XML is a pretty convenient config DSL, isn’t it?).

So far, so good. Now our cache will work both locally and on AWS (EC2, S3), provided we configure the right access keys, and locally. Technically, it may be a good idea to have different infinispan.xml files for local and production, and to define by default <local-cache>, rather than a distributed one, because with the TCP or UDP settings, you may end up in a cluster with other teammates in the same network (though I’m not sure about that; it may present some unexpected issues).

Now, Spring. If you were to only setup Spring, you’d create a bean with a SpringEmbeddedCacheManagerFactoryBean, pass classpath:infinispan.xml as resource location, and it would work. And you can still do that, if you want completely separated cache managers. But Cache managers are tricky. I’ve given an outline of the problems with EhCache, and here we have to do some workarounds in order to have a cache manager shared between Hibernate and Spring. Is that a good idea? It depends. But even if you need separate cache managers, you may need a reference to the Hibernate underlying cache manager, so some of the steps below are still needed. A problem with using separate caches is the JMX name they get registered under, but that I guess can be configured as well.

So, if we want a shared cache manager, we have to create subclasses of the two factory classes:

/**
 * A region factory that exposes the created cache manager as a static variable, so that
 * it can be reused in other places (e.g. as spring cache)
 * 
 * @author bozho
 *
 */
public class SharedInfinispanRegionFactory extends InfinispanRegionFactory {

 private static final long serialVersionUID = 1126940233087656551 L;

 private static EmbeddedCacheManager cacheManager;

 public static EmbeddedCacheManager getSharedCacheManager() {
  return cacheManager;
 }

 @Override
 protected EmbeddedCacheManager createCacheManager(ConfigurationBuilderHolder holder) {
  EmbeddedCacheManager manager = super.createCacheManager(holder);
  cacheManager = manager;
  return manager;
 }

 @Override
 protected EmbeddedCacheManager createCacheManager(Properties properties, ServiceRegistry serviceRegistry)
 throws CacheException {
  EmbeddedCacheManager manager = super.createCacheManager(properties, serviceRegistry);
  cacheManager = manager;
  return manager;
 }
}

Yup, a static variable. Tricky, I know, so be careful.

Then we reuse that for Spring:

/**
 * A spring cache factory bean that reuses a previously instantiated infinispan embedded cache manager
 * @author bozho
 *
 */
public class SharedInfinispanCacheManagerFactoryBean extends SpringEmbeddedCacheManagerFactoryBean {
 private static final Logger logger = ...;
 @Override
 protected EmbeddedCacheManager createBackingEmbeddedCacheManager() throws IOException {
  EmbeddedCacheManager sharedManager = SharedInfinispanRegionFactory.getSharedCacheManager();
  if (sharedManager == null) {
   logger.warn("No shared EmbeddedCacheManager found. Make sure the hibernate 2nd level " + "cache provider is configured and instantiated.");
   return super.createBackingEmbeddedCacheManager();
  }

  return sharedManager;
 }
}

Then we change the hibernate.cache.region.factory_class property in the Hibernate configuration to our new custom class, and in our spring configuration file we do:

<bean id="cacheManager" class="com.yourcompany.util.SharedInfinispanCacheManagerFactoryBean" />
<cache:annotation-driven />

The spring cache is used with a method-level @Cacheable annotation that allows us to cache method calls, and we can also access the CacheManager via simple injection.

Then the “last” part is to check if it works. Even if your application starts ok and looks to be working fine, you should run your integration or selenium test suite and check the statistics via JMX. You may even have tests that use the MBeans to fetch certain stats data about the caches to make sure they are being used.

Cache (computing) Spring Framework Hibernate Infinispan

Published at DZone with permission of Bozhidar Bozhanov, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • How Spring and Hibernate Simplify Web and Database Management
  • Enhanced Query Caching Mechanism in Hibernate 6.3.0
  • Choosing the Right Caching Strategy
  • Implement Hibernate Second-Level Cache With NCache

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!