Search code examples
javajparediseclipselinkredisson

Is there a way to make eclipselink/JPA use redis for saving and retrieving peristence-unit level (L2) cache?


I'm setting up a cluster environment and I want my JPA's second level cache to be replicated throughout the cluster's nodes. I'm using eclipselink as JPA provider and redis for cache management.


Solution

  • There is no official or community project that support it.

    However, you can implement your own CacheInterceptor.

    Eg.

    import org.eclipse.persistence.descriptors.ClassDescriptor;
    import org.eclipse.persistence.internal.identitymaps.CacheKey;
    import org.eclipse.persistence.internal.identitymaps.IdentityMap;
    import org.eclipse.persistence.internal.sessions.AbstractSession;
    import org.eclipse.persistence.sessions.interceptors.CacheInterceptor;
    import org.eclipse.persistence.sessions.interceptors.CacheKeyInterceptor;
    import java.util.Map;
    
    public class MyRedisCacheInterceptor extends CacheInterceptor {
    
        private final MyCacheProvider cacheSupport;
        private final String cacheName;
    
        public DefaultCacheInterceptor(IdentityMap targetIdentityMap, AbstractSession interceptedSession,
                                       String cacheName, DefaultCacheSupport cacheSupport) {
            super(targetIdentityMap, interceptedSession);
            this.cacheSupport = cacheSupport;
            this.cacheName = cacheName;
        }
    
        @Override
        public Object clone() {
            return null;
        }
    
        @Override
        protected CacheKeyInterceptor createCacheKeyInterceptor(CacheKey wrappedCacheKey) {
            final long longKey = (long) wrappedCacheKey.getKey();
    
            CacheKeyInterceptor newKey = new CacheKeyInterceptor(wrappedCacheKey) {
                @Override
                public Object getObject() {
                    return cacheSupport.getOrCreateCache(cacheName).get(longKey);
                }
    
                @Override
                public void setObject(Object object) {
                    cacheSupport.getOrCreateCache(cacheName).put(longKey, object);
                }
            };
    
            return newKey;
        }
    
        @Override
        public boolean containsKey(Object primaryKey) {
            return cacheSupport.getOrCreateCache(cacheName).containsKey(primaryKey);
        }
    
        @Override
        public Map<Object, Object> getAllFromIdentityMapWithEntityPK(Object[] pkList, ClassDescriptor descriptor, AbstractSession session) {
            return null;
        }
    
        @Override
        public Map<Object, CacheKey> getAllCacheKeysFromIdentityMapWithEntityPK(Object[] pkList, ClassDescriptor descriptor, AbstractSession session) {
            return null;
        }
    
        @Override
        public void release() {
        }
    }
    

    You can take a look to this project that uses Apache Ignite, replace it with Redis and try. sample project

    Cache Interceptor Impl: Sample cache interceptor impl

    Ignite Cache Interceptor: Sample Ignite cache interceptor project

    Other sample project with Hazelcast: Sample Hazelcast cache interceptor project

    In short, If you need to use Redis as a second level cache you need to implement your own customization or maybe Hibernate is your best choice. It has a redisson-hibernate cache provider that you can plug (redisson-hibernate).

    Regards,