Feature - Hibernate 6 support #4322

pull/4364/head
Nikita Koksharov 3 years ago
parent bf3849f6ca
commit 1e38128276

@ -18,6 +18,7 @@
<module>redisson-hibernate-5</module>
<module>redisson-hibernate-52</module>
<module>redisson-hibernate-53</module>
<module>redisson-hibernate-6</module>
</modules>

@ -0,0 +1,98 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.redisson</groupId>
<artifactId>redisson-hibernate</artifactId>
<version>3.17.3-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
<artifactId>redisson-hibernate-6</artifactId>
<packaging>jar</packaging>
<name>Redisson/Hibernate-6.x+</name>
<dependencies>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.0.2.Final</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-testing</artifactId>
<version>6.0.2.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.7.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<archive>
<manifestEntries>
<Build-Time>${maven.build.timestamp}</Build-Time>
<Automatic-Module-Name>redisson.hibernate53</Automatic-Module-Name>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>com.mycila</groupId>
<artifactId>license-maven-plugin</artifactId>
<version>3.0</version>
<configuration>
<basedir>${basedir}</basedir>
<header>${basedir}/../../header.txt</header>
<quiet>false</quiet>
<failIfMissing>true</failIfMissing>
<aggregate>false</aggregate>
<includes>
<include>src/main/java/org/redisson/</include>
</includes>
<excludes>
<exclude>target/**</exclude>
</excludes>
<useDefaultExcludes>true</useDefaultExcludes>
<mapping>
<java>JAVADOC_STYLE</java>
</mapping>
<strictCheck>true</strictCheck>
<useDefaultMapping>true</useDefaultMapping>
<encoding>UTF-8</encoding>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,58 @@
/**
* Copyright (c) 2013-2021 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 org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.cache.CacheException;
import org.hibernate.engine.jndi.JndiException;
import org.hibernate.engine.jndi.spi.JndiService;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.redisson.api.RedissonClient;
import java.util.Map;
/**
* Hibernate Cache region factory based on Redisson.
* Uses Redisson instance located in JNDI.
*
* @author Nikita Koksharov
*
*/
public class JndiRedissonRegionFactory extends RedissonRegionFactory {
private static final long serialVersionUID = -4814502675083325567L;
public static final String JNDI_NAME = CONFIG_PREFIX + "jndi_name";
@Override
protected RedissonClient createRedissonClient(StandardServiceRegistry registry, Map properties) {
String jndiName = ConfigurationHelper.getString(JNDI_NAME, properties);
if (jndiName == null) {
throw new CacheException(JNDI_NAME + " property not set");
}
try {
return (RedissonClient) registry.getService(JndiService.class).locate(jndiName);
} catch (JndiException e) {
throw new CacheException(e);
}
}
@Override
protected void releaseFromUse() {
}
}

@ -0,0 +1,63 @@
/**
* Copyright (c) 2013-2021 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);
}
}
}

@ -0,0 +1,241 @@
/**
* Copyright (c) 2013-2021 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 org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.selector.spi.StrategySelector;
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.spi.CacheKeysFactory;
import org.hibernate.cache.spi.DomainDataRegion;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.support.*;
import org.hibernate.cfg.Environment;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.redisson.Redisson;
import org.redisson.api.RMapCache;
import org.redisson.api.RScript;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.LongCodec;
import org.redisson.config.Config;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Map;
/**
* Hibernate Cache region factory based on Redisson.
* Creates own Redisson instance during region start.
*
* @author Nikita Koksharov
*
*/
public class RedissonRegionFactory extends RegionFactoryTemplate {
private static final long serialVersionUID = 3785315696581773811L;
public static final String QUERY_DEF = "query";
public static final String COLLECTION_DEF = "collection";
public static final String ENTITY_DEF = "entity";
public static final String NATURAL_ID_DEF = "naturalid";
public static final String TIMESTAMPS_DEF = "timestamps";
public static final String MAX_ENTRIES_SUFFIX = ".eviction.max_entries";
public static final String TTL_SUFFIX = ".expiration.time_to_live";
public static final String MAX_IDLE_SUFFIX = ".expiration.max_idle_time";
public static final String CONFIG_PREFIX = "hibernate.cache.redisson.";
public static final String REDISSON_CONFIG_PATH = CONFIG_PREFIX + "config";
public static final String FALLBACK = CONFIG_PREFIX + "fallback";
private RedissonClient redisson;
private CacheKeysFactory cacheKeysFactory;
protected boolean fallback;
@Override
protected CacheKeysFactory getImplicitCacheKeysFactory() {
return cacheKeysFactory;
}
@Override
protected void prepareForUse(SessionFactoryOptions settings, @SuppressWarnings("rawtypes") Map properties) throws CacheException {
this.redisson = createRedissonClient(settings.getServiceRegistry(), properties);
String fallbackValue = (String) properties.getOrDefault(FALLBACK, "false");
fallback = Boolean.valueOf(fallbackValue);
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(StandardServiceRegistry registry, Map properties) {
Config config = null;
if (!properties.containsKey(REDISSON_CONFIG_PATH)) {
config = loadConfig(RedissonRegionFactory.class.getClassLoader(), "redisson.json");
if (config == null) {
config = loadConfig(RedissonRegionFactory.class.getClassLoader(), "redisson.yaml");
}
} else {
String configPath = ConfigurationHelper.getString(REDISSON_CONFIG_PATH, properties);
config = loadConfig(RedissonRegionFactory.class.getClassLoader(), configPath);
if (config == null) {
config = loadConfig(configPath);
}
}
if (config == null) {
throw new CacheException("Unable to locate Redisson configuration");
}
return Redisson.create(config);
}
private Config loadConfig(String configPath) {
try {
return Config.fromYAML(new File(configPath));
} catch (IOException e) {
// trying next format
try {
return Config.fromJSON(new File(configPath));
} catch (IOException e1) {
throw new CacheException("Can't parse default yaml config", e1);
}
}
}
private Config loadConfig(ClassLoader classLoader, String fileName) {
InputStream is = classLoader.getResourceAsStream(fileName);
if (is != null) {
try {
return Config.fromYAML(is);
} catch (IOException e) {
try {
is = classLoader.getResourceAsStream(fileName);
return Config.fromJSON(is);
} catch (IOException e1) {
throw new CacheException("Can't parse yaml config", e1);
}
}
}
return null;
}
@Override
protected void releaseFromUse() {
redisson.shutdown();
}
@Override
public boolean isMinimalPutsEnabledByDefault() {
return true;
}
@Override
public AccessType getDefaultAccessType() {
return AccessType.TRANSACTIONAL;
}
@Override
public long nextTimestamp() {
long time = System.currentTimeMillis() << 12;
try {
return redisson.getScript(LongCodec.INSTANCE).eval(RScript.Mode.READ_WRITE,
"local currentTime = redis.call('get', KEYS[1]);"
+ "if currentTime == false then "
+ "redis.call('set', KEYS[1], ARGV[1]); "
+ "return ARGV[1]; "
+ "end;"
+ "local nextValue = math.max(tonumber(ARGV[1]), tonumber(currentTime) + 1); "
+ "redis.call('set', KEYS[1], nextValue); "
+ "return nextValue;",
RScript.ReturnType.INTEGER, Arrays.<Object>asList("redisson-hibernate-timestamp"), time);
} catch (Exception e) {
if (fallback) {
return super.nextTimestamp();
}
throw e;
}
}
@Override
public DomainDataRegion buildDomainDataRegion(
DomainDataRegionConfig regionConfig,
DomainDataRegionBuildingContext buildingContext) {
verifyStarted();
return new DomainDataRegionImpl(
regionConfig,
this,
createDomainDataStorageAccess( regionConfig, buildingContext ),
getImplicitCacheKeysFactory(),
buildingContext
);
}
@Override
protected DomainDataStorageAccess createDomainDataStorageAccess(DomainDataRegionConfig regionConfig,
DomainDataRegionBuildingContext buildingContext) {
String defaultKey = null;
if (!regionConfig.getCollectionCaching().isEmpty()) {
defaultKey = COLLECTION_DEF;
} else if (!regionConfig.getEntityCaching().isEmpty()) {
defaultKey = ENTITY_DEF;
} else if (!regionConfig.getNaturalIdCaching().isEmpty()) {
defaultKey = NATURAL_ID_DEF;
} else {
throw new IllegalArgumentException("Unable to determine entity cache type!");
}
RMapCache<Object, Object> mapCache = getCache(qualifyName(regionConfig.getRegionName()), buildingContext.getSessionFactory().getProperties(), defaultKey);
return new RedissonStorage(mapCache, ((Redisson)redisson).getConnectionManager(), buildingContext.getSessionFactory().getProperties(), defaultKey);
}
private String qualifyName(String name) {
return RegionNameQualifier.INSTANCE.qualify(name, getOptions());
}
@Override
protected StorageAccess createQueryResultsRegionStorageAccess(String regionName,
SessionFactoryImplementor sessionFactory) {
RMapCache<Object, Object> mapCache = getCache(qualifyName(regionName), sessionFactory.getProperties(), QUERY_DEF);
return new RedissonStorage(mapCache, ((Redisson)redisson).getConnectionManager(), sessionFactory.getProperties(), QUERY_DEF);
}
@Override
protected StorageAccess createTimestampsRegionStorageAccess(String regionName,
SessionFactoryImplementor sessionFactory) {
RMapCache<Object, Object> mapCache = getCache(qualifyName(regionName), sessionFactory.getProperties(), TIMESTAMPS_DEF);
return new RedissonStorage(mapCache, ((Redisson)redisson).getConnectionManager(), sessionFactory.getProperties(), TIMESTAMPS_DEF);
}
protected RMapCache<Object, Object> getCache(String regionName, Map properties, String defaultKey) {
return redisson.getMapCache(regionName);
}
}

@ -0,0 +1,196 @@
/**
* Copyright (c) 2013-2021 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 org.hibernate.cache.CacheException;
import org.hibernate.cache.spi.support.DomainDataStorageAccess;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.redisson.api.RFuture;
import org.redisson.api.RMapCache;
import org.redisson.connection.ConnectionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
*
* @author Nikita Koksharov
*
*/
public class RedissonStorage implements DomainDataStorageAccess {
private static final Logger logger = LoggerFactory.getLogger(RedissonStorage.class);
private final RMapCache<Object, Object> mapCache;
private final ConnectionManager connectionManager;
int ttl;
int maxIdle;
int size;
boolean fallback;
volatile boolean fallbackMode;
public RedissonStorage(RMapCache<Object, Object> mapCache, ConnectionManager connectionManager, Map<String, Object> properties, String defaultKey) {
super();
this.mapCache = mapCache;
this.connectionManager = connectionManager;
String maxEntries = getProperty(properties, mapCache.getName(), defaultKey, RedissonRegionFactory.MAX_ENTRIES_SUFFIX);
if (maxEntries != null) {
size = Integer.valueOf(maxEntries);
mapCache.setMaxSize(size);
}
String timeToLive = getProperty(properties, mapCache.getName(), defaultKey, RedissonRegionFactory.TTL_SUFFIX);
if (timeToLive != null) {
ttl = Integer.valueOf(timeToLive);
}
String maxIdleTime = getProperty(properties, mapCache.getName(), defaultKey, RedissonRegionFactory.MAX_IDLE_SUFFIX);
if (maxIdleTime != null) {
maxIdle = Integer.valueOf(maxIdleTime);
}
String fallbackValue = (String) properties.getOrDefault(RedissonRegionFactory.FALLBACK, "false");
fallback = Boolean.valueOf(fallbackValue);
}
private String getProperty(Map<String, Object> properties, String name, String defaultKey, String suffix) {
String maxEntries = (String) properties.get(RedissonRegionFactory.CONFIG_PREFIX + name + suffix);
if (maxEntries != null) {
return maxEntries;
}
String defValue = (String) properties.get(RedissonRegionFactory.CONFIG_PREFIX + defaultKey + suffix);
if (defValue != null) {
return defValue;
}
return null;
}
private void ping() {
fallbackMode = true;
connectionManager.newTimeout(t -> {
RFuture<Boolean> future = mapCache.isExistsAsync();
future.whenComplete((r, ex) -> {
if (ex == null) {
fallbackMode = false;
} else {
ping();
}
});
}, 1, TimeUnit.SECONDS);
}
@Override
public Object getFromCache(Object key, SharedSessionContractImplementor session) {
if (fallbackMode) {
return null;
}
try {
if (maxIdle == 0 && size == 0) {
return mapCache.getWithTTLOnly(key);
}
return mapCache.get(key);
} catch (Exception e) {
if (fallback) {
ping();
logger.error(e.getMessage(), e);
return null;
}
throw new CacheException(e);
}
}
@Override
public void putIntoCache(Object key, Object value, SharedSessionContractImplementor session) {
if (fallbackMode) {
return;
}
try {
mapCache.fastPut(key, value, ttl, TimeUnit.MILLISECONDS, maxIdle, TimeUnit.MILLISECONDS);
} catch (Exception e) {
if (fallback) {
ping();
logger.error(e.getMessage(), e);
return;
}
throw new CacheException(e);
}
}
@Override
public boolean contains(Object key) {
if (fallbackMode) {
return false;
}
try {
return mapCache.containsKey(key);
} catch (Exception e) {
if (fallback) {
ping();
logger.error(e.getMessage(), e);
return false;
}
throw new CacheException(e);
}
}
@Override
public void evictData() {
if (fallbackMode) {
return;
}
try {
mapCache.clear();
} catch (Exception e) {
if (fallback) {
ping();
logger.error(e.getMessage(), e);
return;
}
throw new CacheException(e);
}
}
@Override
public void evictData(Object key) {
if (fallbackMode) {
return;
}
try {
mapCache.fastRemove(key);
} catch (Exception e) {
if (fallback) {
ping();
logger.error(e.getMessage(), e);
return;
}
throw new CacheException(e);
}
}
@Override
public void release() {
try {
mapCache.destroy();
} catch (Exception e) {
throw new CacheException(e);
}
}
}

@ -0,0 +1,42 @@
/**
* Copyright (c) 2013-2021 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 java.util.Collections;
import org.hibernate.boot.registry.selector.SimpleStrategyRegistrationImpl;
import org.hibernate.boot.registry.selector.StrategyRegistration;
import org.hibernate.boot.registry.selector.StrategyRegistrationProvider;
import org.hibernate.cache.spi.RegionFactory;
/**
*
* @author Nikita Koksharov
*
*/
public class RedissonStrategyRegistrationProvider implements StrategyRegistrationProvider {
@Override
public Iterable<StrategyRegistration> getStrategyRegistrations() {
return Collections.<StrategyRegistration>singleton(new SimpleStrategyRegistrationImpl(
RegionFactory.class,
RedissonRegionFactory.class,
"redisson",
RedissonRegionFactory.class.getName(),
RedissonRegionFactory.class.getSimpleName()));
}
}

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<blueprint default-activation="eager"
xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<bean id="strategyRegistrationProvider" class="org.redisson.hibernate.RedissonStrategyRegistrationProvider"/>
<service ref="strategyRegistrationProvider" interface="org.hibernate.boot.registry.selector.StrategyRegistrationProvider"/>
</blueprint>

@ -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 jakarta.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().evictEntityData();
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.getDomainDataRegionStatistics("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.getDomainDataRegionStatistics("org.redisson.hibernate.CollectionTest$A.bs").getHitCount());
stats.logSummary();
}
}

@ -0,0 +1,76 @@
package org.redisson.hibernate;
import java.util.ArrayList;
import java.util.List;
import jakarta.persistence.*;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.NaturalId;
import org.hibernate.annotations.NaturalIdCache;
/**
*
* @author Nikita Koksharov
*
*/
@Entity
@NamedQueries(@NamedQuery(name = "testQuery", query = "from ItemReadWrite where name = :name"))
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "item")
@NaturalIdCache
public class ItemReadWrite {
@Id
@GeneratedValue(generator = "increment")
@GenericGenerator(name = "increment", strategy = "increment")
private Long id;
private String name;
@NaturalId
private String nid;
@ElementCollection
@JoinTable(name = "Entries", joinColumns = @JoinColumn(name="Item_id"))
@Column(name = "entry", nullable = false)
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "item_entries")
private List<String> entries = new ArrayList<String>();
public ItemReadWrite() {
}
public ItemReadWrite(String name) {
this.name = name;
}
public String getNid() {
return nid;
}
public void setNid(String nid) {
this.nid = nid;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getEntries() {
return entries;
}
}

@ -0,0 +1,76 @@
package org.redisson.hibernate;
import java.util.ArrayList;
import java.util.List;
import jakarta.persistence.*;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.NaturalId;
import org.hibernate.annotations.NaturalIdCache;
/**
*
* @author Nikita Koksharov
*
*/
@Entity
@NamedQueries(@NamedQuery(name = "testQuery", query = "from ItemTransactional where name = :name"))
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL, region = "item")
@NaturalIdCache
public class ItemTransactional {
@Id
@GeneratedValue(generator = "increment")
@GenericGenerator(name = "increment", strategy = "increment")
private Long id;
private String name;
@NaturalId
private String nid;
@ElementCollection
@JoinTable(name = "Entries", joinColumns = @JoinColumn(name="Item_id"))
@Column(name = "entry", nullable = false)
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL, region = "item_entries")
private List<String> entries = new ArrayList<String>();
public ItemTransactional() {
}
public ItemTransactional(String name) {
this.name = name;
}
public String getNid() {
return nid;
}
public void setNid(String nid) {
this.nid = nid;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getEntries() {
return entries;
}
}

@ -0,0 +1,94 @@
package org.redisson.hibernate;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Arrays;
import org.hibernate.query.Query;
import org.hibernate.Session;
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;
/**
*
* @author Nikita Koksharov
*
*/
public class ReadWriteTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { ItemReadWrite.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.USE_SECOND_LEVEL_CACHE, "true");
cfg.setProperty(Environment.USE_QUERY_CACHE, "true");
cfg.setProperty(Environment.CACHE_REGION_FACTORY, RedissonRegionFactory.class.getName());
cfg.setProperty("hibernate.cache.redisson.item.eviction.max_entries", "100");
cfg.setProperty("hibernate.cache.redisson.item.expiration.time_to_live", "1500");
cfg.setProperty("hibernate.cache.redisson.item.expiration.max_idle_time", "1000");
}
@Before
public void before() {
sessionFactory().getCache().evictAllRegions();
sessionFactory().getStatistics().clear();
}
@Test
public void testQuery() {
Statistics stats = sessionFactory().getStatistics();
Session s = openSession();
s.beginTransaction();
ItemReadWrite item = new ItemReadWrite("data");
item.getEntries().addAll(Arrays.asList("a", "b", "c"));
s.save(item);
s.flush();
s.getTransaction().commit();
s = openSession();
s.beginTransaction();
Query<ItemReadWrite> query = s.getNamedQuery("testQuery");
query.setCacheable(true);
query.setCacheRegion("myTestQuery");
query.setParameter("name", "data");
item = query.uniqueResult();
s.getTransaction().commit();
s.close();
Assert.assertEquals(1, stats.getDomainDataRegionStatistics("myTestQuery").getPutCount());
s = openSession();
s.beginTransaction();
Query<ItemReadWrite> query2 = s.getNamedQuery("testQuery");
query2.setCacheable(true);
query2.setCacheRegion("myTestQuery");
query2.setParameter("name", "data");
item = query2.uniqueResult();
s.delete(item);
s.getTransaction().commit();
s.close();
Assert.assertEquals(1, stats.getDomainDataRegionStatistics("myTestQuery").getHitCount());
stats.logSummary();
}
}

@ -0,0 +1,180 @@
package org.redisson.hibernate;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Arrays;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.query.Query;
import org.hibernate.stat.Statistics;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
*
* @author Nikita Koksharov
*
*/
public class TransactionalTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { ItemTransactional.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.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().evictAllRegions();
sessionFactory().getStatistics().clear();
}
@Test
public void testQuery() {
Statistics stats = sessionFactory().getStatistics();
Session s = openSession();
s.beginTransaction();
ItemTransactional item = new ItemTransactional("data");
item.getEntries().addAll(Arrays.asList("a", "b", "c"));
s.save(item);
s.flush();
s.getTransaction().commit();
s = openSession();
s.beginTransaction();
Query query = s.getNamedQuery("testQuery");
query.setCacheable(true);
query.setCacheRegion("myTestQuery");
query.setParameter("name", "data");
item = (ItemTransactional) query.uniqueResult();
s.getTransaction().commit();
s.close();
Assert.assertEquals(1, stats.getDomainDataRegionStatistics("myTestQuery").getPutCount());
s = openSession();
s.beginTransaction();
Query query2 = s.getNamedQuery("testQuery");
query2.setCacheable(true);
query2.setCacheRegion("myTestQuery");
query2.setParameter("name", "data");
item = (ItemTransactional) query2.uniqueResult();
s.delete(item);
s.getTransaction().commit();
s.close();
Assert.assertEquals(1, stats.getDomainDataRegionStatistics("myTestQuery").getHitCount());
stats.logSummary();
}
@Test
public void testCollection() {
Long id = null;
Statistics stats = sessionFactory().getStatistics();
Session s = openSession();
s.beginTransaction();
ItemTransactional item = new ItemTransactional("data");
item.getEntries().addAll(Arrays.asList("a", "b", "c"));
id = (Long) s.save(item);
s.flush();
s.getTransaction().commit();
s = openSession();
s.beginTransaction();
item = (ItemTransactional) s.get(ItemTransactional.class, id);
assertThat(item.getEntries()).containsExactly("a", "b", "c");
s.getTransaction().commit();
s.close();
Assert.assertEquals(1, stats.getDomainDataRegionStatistics("item_entries").getPutCount());
s = openSession();
s.beginTransaction();
item = (ItemTransactional) s.get(ItemTransactional.class, id);
assertThat(item.getEntries()).containsExactly("a", "b", "c");
s.delete(item);
s.getTransaction().commit();
s.close();
Assert.assertEquals(1, stats.getDomainDataRegionStatistics("item_entries").getHitCount());
}
@Test
public void testNaturalId() {
Statistics stats = sessionFactory().getStatistics();
Session s = openSession();
s.beginTransaction();
ItemTransactional item = new ItemTransactional("data");
item.setNid("123");
s.save(item);
s.flush();
s.getTransaction().commit();
Assert.assertEquals(1, stats.getDomainDataRegionStatistics("item").getPutCount());
Assert.assertEquals(1, stats.getNaturalIdStatistics(ItemTransactional.class.getName()).getCachePutCount());
s = openSession();
s.beginTransaction();
item = (ItemTransactional) s.bySimpleNaturalId(ItemTransactional.class).load("123");
assertThat(item).isNotNull();
s.delete(item);
s.getTransaction().commit();
s.close();
Assert.assertEquals(1, stats.getDomainDataRegionStatistics("item").getHitCount());
Assert.assertEquals(1, stats.getNaturalIdStatistics(ItemTransactional.class.getName()).getCacheHitCount());
sessionFactory().getStatistics().logSummary();
}
@Test
public void testUpdateWithRefreshThenRollback() {
Statistics stats = sessionFactory().getStatistics();
Long id = null;
Session s = openSession();
s.beginTransaction();
ItemTransactional item = new ItemTransactional( "data" );
id = (Long) s.save( item );
s.flush();
s.getTransaction().commit();
Assert.assertEquals(1, stats.getDomainDataRegionStatistics("item").getPutCount());
s = openSession();
s.beginTransaction();
item = (ItemTransactional) s.get(ItemTransactional.class, id);
item.setName("newdata");
s.update(item);
s.flush();
s.refresh(item);
s.getTransaction().rollback();
s.clear();
s.close();
Assert.assertEquals(1, stats.getDomainDataRegionStatistics("item").getHitCount());
}
}

@ -0,0 +1,4 @@
hibernate.connection.driver_class=org.h2.Driver
hibernate.connection.url=jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1
hibernate.connection.username=sa
hibernate.connection.password=

@ -0,0 +1,2 @@
singleServerConfig:
address: "redis://127.0.0.1:6379"
Loading…
Cancel
Save