Fixed - Cache isn't applied to hibernate collection, that is joined by non primary key field #2708

pull/2776/head
Nikita Koksharov 5 years ago
parent e197c43107
commit c7036d9339

@ -1,8 +0,0 @@
{
"singleServerConfig":{
"address": "redis://127.0.0.1:6379"
},
"codec":{
"class":"org.redisson.codec.FstCodec"
}
}

@ -1,8 +0,0 @@
{
"singleServerConfig":{
"address": "redis://127.0.0.1:6379"
},
"codec":{
"class":"org.redisson.codec.FstCodec"
}
}

@ -0,0 +1,2 @@
singleServerConfig:
address: "redis://127.0.0.1:6379"

@ -0,0 +1,63 @@
/**
* Copyright (c) 2013-2020 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.hibernate;
import io.netty.buffer.ByteBuf;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.cache.internal.DefaultCacheKeysFactory;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.persister.collection.CollectionPersister;
import org.redisson.client.codec.Codec;
import java.io.IOException;
import java.lang.reflect.Field;
/**
*
* @author Nikita Koksharov
*
*/
public class RedissonCacheKeysFactory extends DefaultCacheKeysFactory {
private final Codec codec;
public RedissonCacheKeysFactory(Codec codec) {
this.codec = codec;
}
@Override
public Object createCollectionKey(Object id, CollectionPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
try {
String[] parts = persister.getRole().split("\\.");
Field f = ReflectHelper.findField(id.getClass(), parts[parts.length - 1]);
Object prev = f.get(id);
f.set(id, null);
ByteBuf state = codec.getMapKeyEncoder().encode(id);
Object newId = codec.getMapKeyDecoder().decode(state, null);
state.release();
f.set(id, prev);
return super.createCollectionKey(newId, persister, factory, tenantIdentifier);
} catch (PropertyNotFoundException e) {
return super.createCollectionKey(id, persister, factory, tenantIdentifier);
} catch (IllegalAccessException | IOException e) {
throw new IllegalStateException(e);
}
}
}

@ -15,22 +15,12 @@
*/
package org.redisson.hibernate;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Properties;
import org.hibernate.boot.registry.selector.spi.StrategySelector;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.CollectionRegion;
import org.hibernate.cache.spi.EntityRegion;
import org.hibernate.cache.spi.NaturalIdRegion;
import org.hibernate.cache.spi.QueryResultsRegion;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.TimestampsRegion;
import org.hibernate.cache.spi.*;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cfg.Environment;
import org.hibernate.cfg.Settings;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.jboss.logging.Logger;
@ -40,11 +30,13 @@ import org.redisson.api.RScript;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.LongCodec;
import org.redisson.config.Config;
import org.redisson.hibernate.region.RedissonCollectionRegion;
import org.redisson.hibernate.region.RedissonEntityRegion;
import org.redisson.hibernate.region.RedissonNaturalIdRegion;
import org.redisson.hibernate.region.RedissonQueryRegion;
import org.redisson.hibernate.region.RedissonTimestampsRegion;
import org.redisson.hibernate.region.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Properties;
/**
* Hibernate Cache region factory based on Redisson.
@ -80,11 +72,17 @@ import org.redisson.hibernate.region.RedissonTimestampsRegion;
protected RedissonClient redisson;
private Settings settings;
private CacheKeysFactory cacheKeysFactory;
@Override
public void start(SessionFactoryOptions settings, Properties properties) throws CacheException {
this.redisson = createRedissonClient(properties);
this.settings = new Settings(settings);
StrategySelector selector = settings.getServiceRegistry().getService(StrategySelector.class);
cacheKeysFactory = selector.resolveDefaultableStrategy(CacheKeysFactory.class,
properties.get(Environment.CACHE_KEYS_FACTORY), new RedissonCacheKeysFactory(redisson.getConfig().getCodec()));
}
protected RedissonClient createRedissonClient(Properties properties) {
@ -175,7 +173,7 @@ import org.redisson.hibernate.region.RedissonTimestampsRegion;
log.debug("Building entity cache region: " + regionName);
RMapCache<Object, Object> mapCache = getCache(regionName, properties, ENTITY_DEF);
return new RedissonEntityRegion(mapCache, this, metadata, settings, properties, ENTITY_DEF);
return new RedissonEntityRegion(mapCache, this, metadata, settings, properties, ENTITY_DEF, cacheKeysFactory);
}
@Override
@ -184,7 +182,7 @@ import org.redisson.hibernate.region.RedissonTimestampsRegion;
log.debug("Building naturalId cache region: " + regionName);
RMapCache<Object, Object> mapCache = getCache(regionName, properties, NATURAL_ID_DEF);
return new RedissonNaturalIdRegion(mapCache, this, metadata, settings, properties, NATURAL_ID_DEF);
return new RedissonNaturalIdRegion(mapCache, this, metadata, settings, properties, NATURAL_ID_DEF, cacheKeysFactory);
}
@Override
@ -193,7 +191,7 @@ import org.redisson.hibernate.region.RedissonTimestampsRegion;
log.debug("Building collection cache region: " + regionName);
RMapCache<Object, Object> mapCache = getCache(regionName, properties, COLLECTION_DEF);
return new RedissonCollectionRegion(mapCache, this, metadata, settings, properties, COLLECTION_DEF);
return new RedissonCollectionRegion(mapCache, this, metadata, settings, properties, COLLECTION_DEF, cacheKeysFactory);
}
@Override

@ -19,6 +19,7 @@ import java.util.Properties;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.CacheKeysFactory;
import org.hibernate.cache.spi.CollectionRegion;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.access.AccessType;
@ -38,11 +39,17 @@ import org.redisson.hibernate.strategy.TransactionalCollectionRegionAccessStrate
public class RedissonCollectionRegion extends BaseRegion implements CollectionRegion {
private final Settings settings;
private final CacheKeysFactory cacheKeysFactory;
public RedissonCollectionRegion(RMapCache<Object, Object> mapCache, RegionFactory regionFactory,
CacheDataDescription metadata, Settings settings, Properties properties, String defaultKey) {
CacheDataDescription metadata, Settings settings, Properties properties, String defaultKey, CacheKeysFactory cacheKeysFactory) {
super(mapCache, regionFactory, metadata, properties, defaultKey);
this.settings = settings;
this.cacheKeysFactory = cacheKeysFactory;
}
public CacheKeysFactory getCacheKeysFactory() {
return cacheKeysFactory;
}
@Override

@ -19,6 +19,7 @@ import java.util.Properties;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.CacheKeysFactory;
import org.hibernate.cache.spi.EntityRegion;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.access.AccessType;
@ -38,11 +39,17 @@ import org.redisson.hibernate.strategy.TransactionalEntityRegionAccessStrategy;
public class RedissonEntityRegion extends BaseRegion implements EntityRegion {
private final Settings settings;
private final CacheKeysFactory cacheKeysFactory;
public RedissonEntityRegion(RMapCache<Object, Object> mapCache, RegionFactory regionFactory,
CacheDataDescription metadata, Settings settings, Properties properties, String defaultKey) {
CacheDataDescription metadata, Settings settings, Properties properties, String defaultKey, CacheKeysFactory cacheKeysFactory) {
super(mapCache, regionFactory, metadata, properties, defaultKey);
this.settings = settings;
this.cacheKeysFactory = cacheKeysFactory;
}
public CacheKeysFactory getCacheKeysFactory() {
return cacheKeysFactory;
}
@Override

@ -19,6 +19,7 @@ import java.util.Properties;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.CacheKeysFactory;
import org.hibernate.cache.spi.NaturalIdRegion;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.access.AccessType;
@ -38,11 +39,17 @@ import org.redisson.hibernate.strategy.TransactionalNaturalIdRegionAccessStrateg
public class RedissonNaturalIdRegion extends BaseRegion implements NaturalIdRegion {
private final Settings settings;
private final CacheKeysFactory cacheKeysFactory;
public RedissonNaturalIdRegion(RMapCache<Object, Object> mapCache, RegionFactory regionFactory,
CacheDataDescription metadata, Settings settings, Properties properties, String defaultKey) {
CacheDataDescription metadata, Settings settings, Properties properties, String defaultKey, CacheKeysFactory cacheKeysFactory) {
super(mapCache, regionFactory, metadata, properties, defaultKey);
this.settings = settings;
this.cacheKeysFactory = cacheKeysFactory;
}
public CacheKeysFactory getCacheKeysFactory() {
return cacheKeysFactory;
}
@Override

@ -25,6 +25,7 @@ import org.hibernate.cfg.Settings;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.persister.collection.CollectionPersister;
import org.redisson.hibernate.region.RedissonCollectionRegion;
/**
*
@ -76,12 +77,12 @@ public class NonStrictReadWriteCollectionRegionAccessStrategy extends BaseRegion
@Override
public Object generateCacheKey(Object id, CollectionPersister persister, SessionFactoryImplementor factory,
String tenantIdentifier) {
return DefaultCacheKeysFactory.staticCreateCollectionKey( id, persister, factory, tenantIdentifier );
return ((RedissonCollectionRegion)region).getCacheKeysFactory().createCollectionKey( id, persister, factory, tenantIdentifier );
}
@Override
public Object getCacheKeyId(Object cacheKey) {
return DefaultCacheKeysFactory.staticGetCollectionId( cacheKey );
return ((RedissonCollectionRegion)region).getCacheKeysFactory().getCollectionId( cacheKey );
}
}

@ -25,6 +25,7 @@ import org.hibernate.cfg.Settings;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.redisson.hibernate.region.RedissonEntityRegion;
/**
*
@ -99,12 +100,12 @@ public class NonStrictReadWriteEntityRegionAccessStrategy extends BaseRegionAcce
@Override
public Object generateCacheKey(Object id, EntityPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
return DefaultCacheKeysFactory.staticCreateEntityKey( id, persister, factory, tenantIdentifier );
return ((RedissonEntityRegion)region).getCacheKeysFactory().createEntityKey( id, persister, factory, tenantIdentifier );
}
@Override
public Object getCacheKeyId(Object cacheKey) {
return DefaultCacheKeysFactory.staticGetEntityId( cacheKey );
return ((RedissonEntityRegion)region).getCacheKeysFactory().getEntityId( cacheKey );
}
}

@ -24,6 +24,7 @@ import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.cfg.Settings;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.redisson.hibernate.region.RedissonNaturalIdRegion;
/**
*
@ -96,12 +97,12 @@ public class NonStrictReadWriteNaturalIdRegionAccessStrategy extends BaseRegionA
@Override
public Object generateCacheKey(Object[] naturalIdValues, EntityPersister persister, SessionImplementor session) {
return DefaultCacheKeysFactory.staticCreateNaturalIdKey(naturalIdValues, persister, session);
return ((RedissonNaturalIdRegion)region).getCacheKeysFactory().createNaturalIdKey(naturalIdValues, persister, session);
}
@Override
public Object[] getNaturalIdValues(Object cacheKey) {
return DefaultCacheKeysFactory.staticGetNaturalIdValues(cacheKey);
return ((RedissonNaturalIdRegion)region).getCacheKeysFactory().getNaturalIdValues(cacheKey);
}
}

@ -25,6 +25,7 @@ import org.hibernate.cfg.Settings;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.persister.collection.CollectionPersister;
import org.redisson.hibernate.region.RedissonCollectionRegion;
/**
*
@ -69,12 +70,12 @@ public class ReadOnlyCollectionRegionAccessStrategy extends BaseRegionAccessStra
@Override
public Object generateCacheKey(Object id, CollectionPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
return DefaultCacheKeysFactory.staticCreateCollectionKey( id, persister, factory, tenantIdentifier );
return ((RedissonCollectionRegion)region).getCacheKeysFactory().createCollectionKey(id, persister, factory, tenantIdentifier);
}
@Override
public Object getCacheKeyId(Object cacheKey) {
return DefaultCacheKeysFactory.staticGetCollectionId(cacheKey);
return ((RedissonCollectionRegion)region).getCacheKeysFactory().getCollectionId(cacheKey);
}
}

@ -25,6 +25,7 @@ import org.hibernate.cfg.Settings;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.redisson.hibernate.region.RedissonEntityRegion;
/**
*
@ -93,12 +94,12 @@ public class ReadOnlyEntityRegionAccessStrategy extends BaseRegionAccessStrategy
@Override
public Object generateCacheKey(Object id, EntityPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
return DefaultCacheKeysFactory.staticCreateEntityKey( id, persister, factory, tenantIdentifier );
return ((RedissonEntityRegion)region).getCacheKeysFactory().createEntityKey( id, persister, factory, tenantIdentifier );
}
@Override
public Object getCacheKeyId(Object cacheKey) {
return DefaultCacheKeysFactory.staticGetEntityId(cacheKey);
return ((RedissonEntityRegion)region).getCacheKeysFactory().getEntityId(cacheKey);
}
}

@ -24,6 +24,7 @@ import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.cfg.Settings;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.redisson.hibernate.region.RedissonNaturalIdRegion;
/**
*
@ -90,12 +91,12 @@ public class ReadOnlyNaturalIdRegionAccessStrategy extends BaseRegionAccessStrat
@Override
public Object generateCacheKey(Object[] naturalIdValues, EntityPersister persister, SessionImplementor session) {
return DefaultCacheKeysFactory.staticCreateNaturalIdKey(naturalIdValues, persister, session);
return ((RedissonNaturalIdRegion)region).getCacheKeysFactory().createNaturalIdKey(naturalIdValues, persister, session);
}
@Override
public Object[] getNaturalIdValues(Object cacheKey) {
return DefaultCacheKeysFactory.staticGetNaturalIdValues(cacheKey);
return ((RedissonNaturalIdRegion)region).getCacheKeysFactory().getNaturalIdValues(cacheKey);
}
}

@ -23,6 +23,7 @@ import org.hibernate.cfg.Settings;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.collection.CollectionPersister;
import org.redisson.api.RMapCache;
import org.redisson.hibernate.region.RedissonCollectionRegion;
/**
*
@ -43,12 +44,12 @@ public class ReadWriteCollectionRegionAccessStrategy extends AbstractReadWriteAc
@Override
public Object generateCacheKey(Object id, CollectionPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
return DefaultCacheKeysFactory.staticCreateCollectionKey( id, persister, factory, tenantIdentifier );
return ((RedissonCollectionRegion)region).getCacheKeysFactory().createCollectionKey( id, persister, factory, tenantIdentifier );
}
@Override
public Object getCacheKeyId(Object cacheKey) {
return DefaultCacheKeysFactory.staticGetCollectionId(cacheKey);
return ((RedissonCollectionRegion)region).getCacheKeysFactory().getCollectionId(cacheKey);
}
}

@ -26,6 +26,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.redisson.api.RMapCache;
import org.redisson.hibernate.region.RedissonEntityRegion;
/**
*
@ -69,12 +70,12 @@ public class ReadWriteEntityRegionAccessStrategy extends AbstractReadWriteAccess
@Override
public Object generateCacheKey(Object id, EntityPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
return DefaultCacheKeysFactory.staticCreateEntityKey(id, persister, factory, tenantIdentifier);
return ((RedissonEntityRegion)region).getCacheKeysFactory().createEntityKey(id, persister, factory, tenantIdentifier);
}
@Override
public Object getCacheKeyId(Object cacheKey) {
return DefaultCacheKeysFactory.staticGetEntityId(cacheKey);
return ((RedissonEntityRegion)region).getCacheKeysFactory().getEntityId(cacheKey);
}
}

@ -25,6 +25,7 @@ import org.hibernate.cfg.Settings;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.redisson.api.RMapCache;
import org.redisson.hibernate.region.RedissonNaturalIdRegion;
/**
*
@ -67,12 +68,12 @@ public class ReadWriteNaturalIdRegionAccessStrategy extends AbstractReadWriteAcc
@Override
public Object generateCacheKey(Object[] naturalIdValues, EntityPersister persister, SessionImplementor session) {
return DefaultCacheKeysFactory.staticCreateNaturalIdKey(naturalIdValues, persister, session);
return ((RedissonNaturalIdRegion)region).getCacheKeysFactory().createNaturalIdKey(naturalIdValues, persister, session);
}
@Override
public Object[] getNaturalIdValues(Object cacheKey) {
return DefaultCacheKeysFactory.staticGetNaturalIdValues(cacheKey);
return ((RedissonNaturalIdRegion)region).getCacheKeysFactory().getNaturalIdValues(cacheKey);
}
}

@ -25,6 +25,7 @@ import org.hibernate.cfg.Settings;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.persister.collection.CollectionPersister;
import org.redisson.hibernate.region.RedissonCollectionRegion;
/**
*
@ -74,12 +75,12 @@ public class TransactionalCollectionRegionAccessStrategy extends BaseRegionAcces
@Override
public Object generateCacheKey(Object id, CollectionPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
return DefaultCacheKeysFactory.staticCreateCollectionKey( id, persister, factory, tenantIdentifier );
return ((RedissonCollectionRegion)region).getCacheKeysFactory().createCollectionKey( id, persister, factory, tenantIdentifier );
}
@Override
public Object getCacheKeyId(Object cacheKey) {
return DefaultCacheKeysFactory.staticGetCollectionId(cacheKey);
return ((RedissonCollectionRegion)region).getCacheKeysFactory().getCollectionId(cacheKey);
}
}

@ -25,6 +25,7 @@ import org.hibernate.cfg.Settings;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.redisson.hibernate.region.RedissonEntityRegion;
/**
*
@ -97,12 +98,12 @@ public class TransactionalEntityRegionAccessStrategy extends BaseRegionAccessStr
@Override
public Object generateCacheKey(Object id, EntityPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
return DefaultCacheKeysFactory.staticCreateEntityKey(id, persister, factory, tenantIdentifier);
return ((RedissonEntityRegion)region).getCacheKeysFactory().createEntityKey(id, persister, factory, tenantIdentifier);
}
@Override
public Object getCacheKeyId(Object cacheKey) {
return DefaultCacheKeysFactory.staticGetEntityId(cacheKey);
return ((RedissonEntityRegion)region).getCacheKeysFactory().getEntityId(cacheKey);
}
}

@ -24,6 +24,7 @@ import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.cfg.Settings;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.redisson.hibernate.region.RedissonNaturalIdRegion;
/**
*
@ -94,12 +95,12 @@ public class TransactionalNaturalIdRegionAccessStrategy extends BaseRegionAccess
@Override
public Object generateCacheKey(Object[] naturalIdValues, EntityPersister persister, SessionImplementor session) {
return DefaultCacheKeysFactory.staticCreateNaturalIdKey(naturalIdValues, persister, session);
return ((RedissonNaturalIdRegion)region).getCacheKeysFactory().createNaturalIdKey(naturalIdValues, persister, session);
}
@Override
public Object[] getNaturalIdValues(Object cacheKey) {
return DefaultCacheKeysFactory.staticGetNaturalIdValues(cacheKey);
return ((RedissonNaturalIdRegion)region).getCacheKeysFactory().getNaturalIdValues(cacheKey);
}
}

@ -0,0 +1,123 @@
package org.redisson.hibernate;
import org.hibernate.Session;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.stat.Statistics;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
/**
*
* @author Nikita Koksharov
*
*/
public class CollectionTest extends BaseCoreFunctionalTestCase {
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public static class A implements Serializable {
@Id
Long id;
@Column(unique = true) String uniqueField;
@ManyToMany
@JoinTable(
name = "a_b",
joinColumns = @JoinColumn(
name = "unique_field", referencedColumnName = "uniqueField"),
inverseJoinColumns = @JoinColumn(
name = "b_id", referencedColumnName = "id"))
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
private List<B> bs = new ArrayList<>();
}
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public static class B implements Serializable {
@Id Long id;
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { A.class, B.class };
}
@Override
protected void configure(Configuration cfg) {
super.configure(cfg);
cfg.setProperty(Environment.DRIVER, org.h2.Driver.class.getName());
cfg.setProperty(Environment.URL, "jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;");
cfg.setProperty(Environment.USER, "sa");
cfg.setProperty(Environment.PASS, "");
cfg.setProperty(Environment.CACHE_REGION_PREFIX, "");
cfg.setProperty(Environment.GENERATE_STATISTICS, "true");
cfg.setProperty(Environment.SHOW_SQL, "true");
cfg.setProperty(Environment.USE_SECOND_LEVEL_CACHE, "true");
cfg.setProperty(Environment.USE_QUERY_CACHE, "true");
cfg.setProperty(Environment.CACHE_REGION_FACTORY, RedissonRegionFactory.class.getName());
}
@Before
public void before() {
sessionFactory().getCache().evictEntityRegions();
sessionFactory().getStatistics().clear();
}
@Test
public void testQuery() {
Statistics stats = sessionFactory().getStatistics();
Session s = openSession();
s.beginTransaction();
A a = new A();
a.id = 1L;
a.uniqueField = "1";
B b = new B();
b.id = 1L;
s.save(b);
a.bs.add(b);
s.save(a);
s.flush();
s.getTransaction().commit();
s = openSession();
s.beginTransaction();
A a1 = s.get(A.class, 1L);
System.out.println("here1");
assertThat(a1.bs).hasSize(1);
s.getTransaction().commit();
Assert.assertEquals(0, stats.getSecondLevelCacheStatistics("org.redisson.hibernate.CollectionTest$A.bs").getHitCount());
s = openSession();
s.beginTransaction();
A a2 = s.get(A.class, 1L);
B b2 = a2.bs.iterator().next();
assertThat(a2.bs.size()).isEqualTo(1);
s.getTransaction().commit();
s.close();
Assert.assertEquals(1, stats.getSecondLevelCacheStatistics("org.redisson.hibernate.CollectionTest$A.bs").getHitCount());
stats.logSummary();
}
}

@ -31,7 +31,7 @@ public class ReadWriteTest extends BaseCoreFunctionalTestCase {
protected void configure(Configuration cfg) {
super.configure(cfg);
cfg.setProperty(Environment.DRIVER, org.h2.Driver.class.getName());
cfg.setProperty(Environment.URL, "jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE");
cfg.setProperty(Environment.URL, "jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;");
cfg.setProperty(Environment.USER, "sa");
cfg.setProperty(Environment.PASS, "");
cfg.setProperty(Environment.CACHE_REGION_PREFIX, "");

@ -31,7 +31,7 @@ public class TransactionalTest extends BaseCoreFunctionalTestCase {
protected void configure(Configuration cfg) {
super.configure(cfg);
cfg.setProperty(Environment.DRIVER, org.h2.Driver.class.getName());
cfg.setProperty(Environment.URL, "jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE");
cfg.setProperty(Environment.URL, "jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;");
cfg.setProperty(Environment.USER, "sa");
cfg.setProperty(Environment.PASS, "");
cfg.setProperty(Environment.CACHE_REGION_PREFIX, "");

@ -1,8 +0,0 @@
{
"singleServerConfig":{
"address": "redis://127.0.0.1:6379"
},
"codec":{
"class":"org.redisson.codec.FstCodec"
}
}

@ -1,8 +0,0 @@
{
"singleServerConfig":{
"address": "redis://127.0.0.1:6379"
},
"codec":{
"class":"org.redisson.codec.FstCodec"
}
}

@ -0,0 +1,63 @@
/**
* Copyright (c) 2013-2020 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.hibernate;
import io.netty.buffer.ByteBuf;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.cache.internal.DefaultCacheKeysFactory;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.persister.collection.CollectionPersister;
import org.redisson.client.codec.Codec;
import java.io.IOException;
import java.lang.reflect.Field;
/**
*
* @author Nikita Koksharov
*
*/
public class RedissonCacheKeysFactory extends DefaultCacheKeysFactory {
private final Codec codec;
public RedissonCacheKeysFactory(Codec codec) {
this.codec = codec;
}
@Override
public Object createCollectionKey(Object id, CollectionPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
try {
String[] parts = persister.getRole().split("\\.");
Field f = ReflectHelper.findField(id.getClass(), parts[parts.length - 1]);
Object prev = f.get(id);
f.set(id, null);
ByteBuf state = codec.getMapKeyEncoder().encode(id);
Object newId = codec.getMapKeyDecoder().decode(state, null);
state.release();
f.set(id, prev);
return super.createCollectionKey(newId, persister, factory, tenantIdentifier);
} catch (PropertyNotFoundException e) {
return super.createCollectionKey(id, persister, factory, tenantIdentifier);
} catch (IllegalAccessException | IOException e) {
throw new IllegalStateException(e);
}
}
}

@ -15,24 +15,10 @@
*/
package org.redisson.hibernate;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Properties;
import org.hibernate.boot.registry.selector.spi.StrategySelector;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.internal.DefaultCacheKeysFactory;
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.CacheKeysFactory;
import org.hibernate.cache.spi.CollectionRegion;
import org.hibernate.cache.spi.EntityRegion;
import org.hibernate.cache.spi.NaturalIdRegion;
import org.hibernate.cache.spi.QueryResultsRegion;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.TimestampsRegion;
import org.hibernate.cache.spi.*;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cfg.Environment;
import org.hibernate.cfg.Settings;
@ -44,11 +30,13 @@ import org.redisson.api.RScript;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.LongCodec;
import org.redisson.config.Config;
import org.redisson.hibernate.region.RedissonCollectionRegion;
import org.redisson.hibernate.region.RedissonEntityRegion;
import org.redisson.hibernate.region.RedissonNaturalIdRegion;
import org.redisson.hibernate.region.RedissonQueryRegion;
import org.redisson.hibernate.region.RedissonTimestampsRegion;
import org.redisson.hibernate.region.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Properties;
/**
* Hibernate Cache region factory based on Redisson.
@ -94,7 +82,7 @@ public class RedissonRegionFactory implements RegionFactory {
StrategySelector selector = settings.getServiceRegistry().getService(StrategySelector.class);
cacheKeysFactory = selector.resolveDefaultableStrategy(CacheKeysFactory.class,
properties.get(Environment.CACHE_KEYS_FACTORY), new DefaultCacheKeysFactory());
properties.get(Environment.CACHE_KEYS_FACTORY), new RedissonCacheKeysFactory(redisson.getConfig().getCodec()));
}
protected RedissonClient createRedissonClient(Properties properties) {

@ -0,0 +1,123 @@
package org.redisson.hibernate;
import org.hibernate.Session;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.stat.Statistics;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
/**
*
* @author Nikita Koksharov
*
*/
public class CollectionTest extends BaseCoreFunctionalTestCase {
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public static class A implements Serializable {
@Id
Long id;
@Column(unique = true) String uniqueField;
@ManyToMany
@JoinTable(
name = "a_b",
joinColumns = @JoinColumn(
name = "unique_field", referencedColumnName = "uniqueField"),
inverseJoinColumns = @JoinColumn(
name = "b_id", referencedColumnName = "id"))
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
private List<B> bs = new ArrayList<>();
}
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public static class B implements Serializable {
@Id Long id;
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { A.class, B.class };
}
@Override
protected void configure(Configuration cfg) {
super.configure(cfg);
cfg.setProperty(Environment.DRIVER, org.h2.Driver.class.getName());
cfg.setProperty(Environment.URL, "jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;");
cfg.setProperty(Environment.USER, "sa");
cfg.setProperty(Environment.PASS, "");
cfg.setProperty(Environment.CACHE_REGION_PREFIX, "");
cfg.setProperty(Environment.GENERATE_STATISTICS, "true");
cfg.setProperty(Environment.SHOW_SQL, "true");
cfg.setProperty(Environment.USE_SECOND_LEVEL_CACHE, "true");
cfg.setProperty(Environment.USE_QUERY_CACHE, "true");
cfg.setProperty(Environment.CACHE_REGION_FACTORY, RedissonRegionFactory.class.getName());
}
@Before
public void before() {
sessionFactory().getCache().evictEntityRegions();
sessionFactory().getStatistics().clear();
}
@Test
public void testQuery() {
Statistics stats = sessionFactory().getStatistics();
Session s = openSession();
s.beginTransaction();
A a = new A();
a.id = 1L;
a.uniqueField = "1";
B b = new B();
b.id = 1L;
s.save(b);
a.bs.add(b);
s.save(a);
s.flush();
s.getTransaction().commit();
s = openSession();
s.beginTransaction();
A a1 = s.get(A.class, 1L);
System.out.println("here1");
assertThat(a1.bs).hasSize(1);
s.getTransaction().commit();
Assert.assertEquals(0, stats.getSecondLevelCacheStatistics("org.redisson.hibernate.CollectionTest$A.bs").getHitCount());
s = openSession();
s.beginTransaction();
A a2 = s.get(A.class, 1L);
B b2 = a2.bs.iterator().next();
assertThat(a2.bs.size()).isEqualTo(1);
s.getTransaction().commit();
s.close();
Assert.assertEquals(1, stats.getSecondLevelCacheStatistics("org.redisson.hibernate.CollectionTest$A.bs").getHitCount());
stats.logSummary();
}
}

@ -31,7 +31,7 @@ public class ReadWriteTest extends BaseCoreFunctionalTestCase {
protected void configure(Configuration cfg) {
super.configure(cfg);
cfg.setProperty(Environment.DRIVER, org.h2.Driver.class.getName());
cfg.setProperty(Environment.URL, "jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE");
cfg.setProperty(Environment.URL, "jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;");
cfg.setProperty(Environment.USER, "sa");
cfg.setProperty(Environment.PASS, "");
cfg.setProperty(Environment.CACHE_REGION_PREFIX, "");

@ -41,7 +41,7 @@ public class TransactionalTest extends BaseCoreFunctionalTestCase {
protected void configure(Configuration cfg) {
super.configure(cfg);
cfg.setProperty(Environment.DRIVER, org.h2.Driver.class.getName());
cfg.setProperty(Environment.URL, "jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE");
cfg.setProperty(Environment.URL, "jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;");
cfg.setProperty(Environment.USER, "sa");
cfg.setProperty(Environment.PASS, "");
cfg.setProperty(Environment.CACHE_REGION_PREFIX, "");

@ -1,9 +0,0 @@
{
"singleServerConfig":{
"address": "redis://127.0.0.1:6379"
},
"codec":{
"class":"org.redisson.codec.FstCodec"
},
"registrationKey": "LJk6V4gqOgTOKZ53Cf2MywSb3/M+AW4igUgBG0pLbj77G2AMBpBkELbiuT9B3HSwqhGN5kxDNtPeUpLdTJLdNaoWSKLO6P9wozkdens3nfi7VE8VEwArQ1naIBLuGMQ5DZ3AHJ4cvYnj0RkAE/lx6sU/uWzBTB1e9jW9sLDSD8M="
}

@ -1,9 +0,0 @@
{
"singleServerConfig":{
"address": "redis://127.0.0.1:6379"
},
"codec":{
"class":"org.redisson.codec.FstCodec"
},
"registrationKey": "LJk6V4gqOgTOKZ53Cf2MywSb3/M+AW4igUgBG0pLbj77G2AMBpBkELbiuT9B3HSwqhGN5kxDNtPeUpLdTJLdNaoWSKLO6P9wozkdens3nfi7VE8VEwArQ1naIBLuGMQ5DZ3AHJ4cvYnj0RkAE/lx6sU/uWzBTB1e9jW9sLDSD8M="
}

@ -0,0 +1,63 @@
/**
* Copyright (c) 2013-2020 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.hibernate;
import io.netty.buffer.ByteBuf;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.cache.internal.DefaultCacheKeysFactory;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.persister.collection.CollectionPersister;
import org.redisson.client.codec.Codec;
import java.io.IOException;
import java.lang.reflect.Field;
/**
*
* @author Nikita Koksharov
*
*/
public class RedissonCacheKeysFactory extends DefaultCacheKeysFactory {
private final Codec codec;
public RedissonCacheKeysFactory(Codec codec) {
this.codec = codec;
}
@Override
public Object createCollectionKey(Object id, CollectionPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
try {
String[] parts = persister.getRole().split("\\.");
Field f = ReflectHelper.findField(id.getClass(), parts[parts.length - 1]);
Object prev = f.get(id);
f.set(id, null);
ByteBuf state = codec.getMapKeyEncoder().encode(id);
Object newId = codec.getMapKeyDecoder().decode(state, null);
state.release();
f.set(id, prev);
return super.createCollectionKey(newId, persister, factory, tenantIdentifier);
} catch (PropertyNotFoundException e) {
return super.createCollectionKey(id, persister, factory, tenantIdentifier);
} catch (IllegalAccessException | IOException e) {
throw new IllegalStateException(e);
}
}
}

@ -20,7 +20,6 @@ import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.cfg.spi.DomainDataRegionBuildingContext;
import org.hibernate.cache.cfg.spi.DomainDataRegionConfig;
import org.hibernate.cache.internal.DefaultCacheKeysFactory;
import org.hibernate.cache.spi.CacheKeysFactory;
import org.hibernate.cache.spi.DomainDataRegion;
import org.hibernate.cache.spi.access.AccessType;
@ -80,15 +79,19 @@ public class RedissonRegionFactory extends RegionFactoryTemplate {
private RedissonClient redisson;
private CacheKeysFactory cacheKeysFactory;
private boolean fallback;
@Override
protected CacheKeysFactory getImplicitCacheKeysFactory() {
return cacheKeysFactory;
}
@Override
protected void prepareForUse(SessionFactoryOptions settings, @SuppressWarnings("rawtypes") Map properties) throws CacheException {
this.redisson = createRedissonClient(properties);
StrategySelector selector = settings.getServiceRegistry().getService(StrategySelector.class);
cacheKeysFactory = selector.resolveDefaultableStrategy(CacheKeysFactory.class,
properties.get(Environment.CACHE_KEYS_FACTORY), DefaultCacheKeysFactory.INSTANCE);
properties.get(Environment.CACHE_KEYS_FACTORY), new RedissonCacheKeysFactory(redisson.getConfig().getCodec()));
}
protected RedissonClient createRedissonClient(Map properties) {
@ -192,11 +195,11 @@ public class RedissonRegionFactory extends RegionFactoryTemplate {
regionConfig,
this,
createDomainDataStorageAccess( regionConfig, buildingContext ),
cacheKeysFactory,
getImplicitCacheKeysFactory(),
buildingContext
);
}
@Override
protected DomainDataStorageAccess createDomainDataStorageAccess(DomainDataRegionConfig regionConfig,
DomainDataRegionBuildingContext buildingContext) {

@ -0,0 +1,123 @@
package org.redisson.hibernate;
import org.hibernate.Session;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.stat.Statistics;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
/**
*
* @author Nikita Koksharov
*
*/
public class CollectionTest extends BaseCoreFunctionalTestCase {
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public static class A implements Serializable {
@Id
Long id;
@Column(unique = true) String uniqueField;
@ManyToMany
@JoinTable(
name = "a_b",
joinColumns = @JoinColumn(
name = "unique_field", referencedColumnName = "uniqueField"),
inverseJoinColumns = @JoinColumn(
name = "b_id", referencedColumnName = "id"))
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
private List<B> bs = new ArrayList<>();
}
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public static class B implements Serializable {
@Id Long id;
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { A.class, B.class };
}
@Override
protected void configure(Configuration cfg) {
super.configure(cfg);
cfg.setProperty(Environment.DRIVER, org.h2.Driver.class.getName());
cfg.setProperty(Environment.URL, "jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;");
cfg.setProperty(Environment.USER, "sa");
cfg.setProperty(Environment.PASS, "");
cfg.setProperty(Environment.CACHE_REGION_PREFIX, "");
cfg.setProperty(Environment.GENERATE_STATISTICS, "true");
cfg.setProperty(Environment.SHOW_SQL, "true");
cfg.setProperty(Environment.USE_SECOND_LEVEL_CACHE, "true");
cfg.setProperty(Environment.USE_QUERY_CACHE, "true");
cfg.setProperty(Environment.CACHE_REGION_FACTORY, RedissonRegionFactory.class.getName());
}
@Before
public void before() {
sessionFactory().getCache().evictEntityRegions();
sessionFactory().getStatistics().clear();
}
@Test
public void testQuery() {
Statistics stats = sessionFactory().getStatistics();
Session s = openSession();
s.beginTransaction();
A a = new A();
a.id = 1L;
a.uniqueField = "1";
B b = new B();
b.id = 1L;
s.save(b);
a.bs.add(b);
s.save(a);
s.flush();
s.getTransaction().commit();
s = openSession();
s.beginTransaction();
A a1 = s.get(A.class, 1L);
System.out.println("here1");
assertThat(a1.bs).hasSize(1);
s.getTransaction().commit();
Assert.assertEquals(0, stats.getSecondLevelCacheStatistics("org.redisson.hibernate.CollectionTest$A.bs").getHitCount());
s = openSession();
s.beginTransaction();
A a2 = s.get(A.class, 1L);
B b2 = a2.bs.iterator().next();
assertThat(a2.bs.size()).isEqualTo(1);
s.getTransaction().commit();
s.close();
Assert.assertEquals(1, stats.getSecondLevelCacheStatistics("org.redisson.hibernate.CollectionTest$A.bs").getHitCount());
stats.logSummary();
}
}

@ -31,7 +31,7 @@ public class ReadWriteTest extends BaseCoreFunctionalTestCase {
protected void configure(Configuration cfg) {
super.configure(cfg);
cfg.setProperty(Environment.DRIVER, org.h2.Driver.class.getName());
cfg.setProperty(Environment.URL, "jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE");
cfg.setProperty(Environment.URL, "jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;");
cfg.setProperty(Environment.USER, "sa");
cfg.setProperty(Environment.PASS, "");
cfg.setProperty(Environment.CACHE_REGION_PREFIX, "");

@ -31,7 +31,7 @@ public class TransactionalTest extends BaseCoreFunctionalTestCase {
protected void configure(Configuration cfg) {
super.configure(cfg);
cfg.setProperty(Environment.DRIVER, org.h2.Driver.class.getName());
cfg.setProperty(Environment.URL, "jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE");
cfg.setProperty(Environment.URL, "jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;");
cfg.setProperty(Environment.USER, "sa");
cfg.setProperty(Environment.PASS, "");
cfg.setProperty(Environment.CACHE_REGION_PREFIX, "");

Loading…
Cancel
Save