Feature - Hibernate 6 support #4322
parent
bf3849f6ca
commit
1e38128276
@ -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 @@
|
|||||||
|
org.redisson.hibernate.RedissonStrategyRegistrationProvider
|
@ -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…
Reference in New Issue