├── .gitignore ├── README.md ├── pom.xml └── src ├── main └── java │ └── net │ └── daum │ └── clix │ └── hibernate │ └── redis │ ├── AbstractRedisRegionFactory.java │ ├── RedisCache.java │ ├── RedisRegionFactory.java │ ├── jedis │ └── JedisCacheImpl.java │ ├── region │ ├── RedisCollectionRegion.java │ ├── RedisEntityRegion.java │ ├── RedisQueryResultRegion.java │ ├── RedisRegion.java │ ├── RedisTimestampsRegion.java │ └── RedisTransactionalRegion.java │ └── strategy │ ├── AbstractReadWriteRedisAccessStrategy.java │ ├── AbstractRedisAccessStrategy.java │ ├── NonStrictReadWriteRedisCollectionRegionAccessStrategy.java │ ├── NonStrictReadWriteRedisEntityRegionAccessStrategy.java │ ├── ReadOnlyRedisCollectionRegionAccessStrategy.java │ ├── ReadOnlyRedisEntityRegionAccessStrategy.java │ ├── ReadWriteRedisCollectionRegionAcessStrategy.java │ ├── ReadWriteRedisEntityRegionAcessStrategy.java │ ├── RedisAccessStrategyFactory.java │ └── RedisAccessStrategyFactoryImpl.java └── test ├── java └── net │ └── daum │ └── clix │ ├── Campaign.java │ └── test │ └── CampaignTest.java └── resources ├── hibernate.cfg.xml └── log4j.properties /.gitignore: -------------------------------------------------------------------------------- 1 | /db 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hibernate-redis 2 | A library for using Redis as a second level distributed cache in Hibernate. 3 | This is an open source project. 4 | 5 | version : 1.0.0-SNAPSHOT 6 | 7 | ## Feature 8 | * Implement hibernate entity & query results cache using redis 9 | * Implement read-write strategy locking with [SETNX](http://redis.io/commands/setnx) 10 | 11 | ## System Requirements 12 | 13 | JDK 1.6 or above. 14 | 15 | ## Using Hibernate-redis 16 | 17 | * checkout this source 18 | 19 | git clone git@github.com:Jongtae/hibernate-redis.git 20 | 21 | * install locally using maven' install commend: 22 | 23 | mvn install -DskipTests 24 | 25 | * add dependency in your project pom.xml: 26 | 27 | 28 | net.daum.clix 29 | hibernate-redis 30 | 31 | 32 | * configurate hibernate properties like this : 33 | 34 | true 35 | true 36 | net.daum.clix.hibernate.redis.RedisRegionFactory 37 | 38 | "redis.host" 39 | 40 | * set your additional redis properites optionally 41 | 42 | "redis.port" 43 | "redis.timeout" 44 | "redis.password" 45 | 46 | ## License 47 | 48 | This software is licensed under the Apache 2 license, quoted below. 49 | 50 | Licensed under the Apache License, Version 2.0 (the "License"); you may not 51 | use this file except in compliance with the License. You may obtain a copy of 52 | the License at 53 | 54 | http://www.apache.org/licenses/LICENSE-2.0 55 | 56 | Unless required by applicable law or agreed to in writing, software 57 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 58 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 59 | License for the specific language governing permissions and limitations under 60 | the License. -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | net.daum.clix 6 | hibernate-redis 7 | 1.0 8 | jar 9 | 10 | hibernate-redis 11 | https://github.com/Jongtae/hibernate-redis 12 | A library for using Redis as a second level distributed cache in Hibernate. 13 | 14 | 15 | 16 | Jongtae Lee 17 | jt.jongtae@gmail.com 18 | 19 | 20 | Jongjin Han 21 | 84june@gmail.com 22 | 23 | 24 | 25 | 26 | scm:git:git@github.com:jongtae/hibernate-redis.git 27 | scm:git:git@github.com:jongtae/hibernate-redis.git 28 | scm:git:git@github.com:jongtae/hibernate-redis.git 29 | 30 | 31 | 32 | UTF-8 33 | 34 | 35 | 36 | 37 | jboss 38 | https://repository.jboss.org/nexus/content/groups/public/ 39 | 40 | 41 | 42 | 43 | 44 | redis.clients 45 | jedis 46 | 2.0.0 47 | jar 48 | compile 49 | 50 | 51 | org.hibernate 52 | hibernate-core 53 | 4.3.5.Final 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | org.aspectj 63 | aspectjrt 64 | 1.6.11 65 | 66 | 67 | org.aspectj 68 | aspectjtools 69 | 1.6.11 70 | 71 | 72 | org.aspectj 73 | aspectjweaver 74 | 1.6.11 75 | 76 | 77 | 78 | 79 | org.slf4j 80 | slf4j-api 81 | 1.5.6 82 | 83 | 84 | org.slf4j 85 | slf4j-log4j12 86 | 1.5.6 87 | test 88 | 89 | 90 | 91 | 92 | org.hibernate 93 | hibernate-annotations 94 | 3.5.6-Final 95 | test 96 | 97 | 98 | javassist 99 | javassist 100 | 3.12.1.GA 101 | test 102 | 103 | 104 | junit 105 | junit 106 | 4.4 107 | test 108 | 109 | 110 | org.hsqldb 111 | hsqldb 112 | 2.2.9 113 | test 114 | 115 | 116 | 117 | 118 | org.springframework 119 | spring-core 120 | 3.0.5.RELEASE 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | org.codehaus.mojo 129 | aspectj-maven-plugin 130 | 1.4 131 | 132 | 1.6 133 | 1.6 134 | utf-8 135 | 1.6 136 | 137 | 138 | 139 | 140 | compile 141 | test-compile 142 | 143 | 144 | 145 | 146 | 147 | org.apache.maven.plugins 148 | maven-compiler-plugin 149 | 2.3.1 150 | 151 | UTF-8 152 | 1.6 153 | 1.6 154 | 155 | 156 | 157 | org.apache.maven.plugins 158 | maven-surefire-plugin 159 | 2.12.2 160 | 161 | 162 | -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/1.6.11/aspectjweaver-1.6.11.jar" 163 | 164 | 165 | 166 | 167 | org.apache.maven.plugins 168 | maven-release-plugin 169 | 2.4 170 | 171 | 172 | org.kualigan.maven.plugins 173 | redis-maven-plugin 174 | 175 | 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /src/main/java/net/daum/clix/hibernate/redis/AbstractRedisRegionFactory.java: -------------------------------------------------------------------------------- 1 | package net.daum.clix.hibernate.redis; 2 | 3 | import net.daum.clix.hibernate.redis.jedis.JedisCacheImpl; 4 | import net.daum.clix.hibernate.redis.region.RedisCollectionRegion; 5 | import net.daum.clix.hibernate.redis.region.RedisEntityRegion; 6 | import net.daum.clix.hibernate.redis.region.RedisQueryResultRegion; 7 | import net.daum.clix.hibernate.redis.region.RedisTimestampsRegion; 8 | import net.daum.clix.hibernate.redis.strategy.RedisAccessStrategyFactory; 9 | import net.daum.clix.hibernate.redis.strategy.RedisAccessStrategyFactoryImpl; 10 | import org.hibernate.cache.CacheException; 11 | import org.hibernate.cache.spi.*; 12 | import org.hibernate.cache.spi.access.AccessType; 13 | import org.hibernate.cfg.Settings; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | import redis.clients.jedis.JedisPool; 17 | 18 | import java.util.Properties; 19 | 20 | /** 21 | * @author jtlee 22 | * @author 84june 23 | */ 24 | abstract class AbstractRedisRegionFactory implements RegionFactory { 25 | 26 | private final Logger logger = LoggerFactory.getLogger(getClass()); 27 | 28 | protected JedisPool pool; 29 | 30 | protected Properties properties; 31 | 32 | protected Settings settings; 33 | 34 | /** 35 | * {@link RedisAccessStrategyFactory} for creating various access strategies 36 | */ 37 | private final RedisAccessStrategyFactory accessStrategyFactory = new RedisAccessStrategyFactoryImpl(); 38 | 39 | @Override 40 | public boolean isMinimalPutsEnabledByDefault() { 41 | return true; 42 | } 43 | 44 | @Override 45 | public AccessType getDefaultAccessType() { 46 | return AccessType.READ_WRITE; 47 | } 48 | 49 | @Override 50 | public long nextTimestamp() { 51 | return System.currentTimeMillis() / 100; 52 | } 53 | 54 | @Override 55 | public EntityRegion buildEntityRegion(String regionName, Properties properties, CacheDataDescription metadata) throws CacheException { 56 | return new RedisEntityRegion(accessStrategyFactory, getRedisCache(regionName), properties, metadata, settings); 57 | } 58 | 59 | @Override 60 | public CollectionRegion buildCollectionRegion(String regionName, Properties properties, CacheDataDescription metadata) 61 | throws CacheException { 62 | return new RedisCollectionRegion(accessStrategyFactory, getRedisCache(regionName), properties, metadata, settings); 63 | } 64 | 65 | @Override 66 | public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties) throws CacheException { 67 | return new RedisQueryResultRegion(accessStrategyFactory, getRedisCache(regionName), properties); 68 | } 69 | 70 | @Override 71 | public TimestampsRegion buildTimestampsRegion(String regionName, Properties properties) throws CacheException { 72 | return new RedisTimestampsRegion(accessStrategyFactory, getRedisCache(regionName), properties); 73 | } 74 | 75 | private RedisCache getRedisCache(String regionName) { 76 | return new JedisCacheImpl(pool, regionName); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/net/daum/clix/hibernate/redis/RedisCache.java: -------------------------------------------------------------------------------- 1 | package net.daum.clix.hibernate.redis; 2 | 3 | import org.hibernate.cache.CacheException; 4 | 5 | /** 6 | * An interface for Redis. 7 | * 8 | * @author jtlee 9 | * @author 84june 10 | */ 11 | public interface RedisCache { 12 | 13 | String getRegionName(); 14 | 15 | boolean exists(String key); 16 | 17 | Object get(Object key) throws CacheException; 18 | 19 | void put(Object key, Object value) throws CacheException; 20 | 21 | void remove(Object key) throws CacheException; 22 | 23 | void destory(); 24 | 25 | boolean lock(Object key, Integer expireMsecs) throws InterruptedException; 26 | 27 | void unlock(Object key); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/net/daum/clix/hibernate/redis/RedisRegionFactory.java: -------------------------------------------------------------------------------- 1 | package net.daum.clix.hibernate.redis; 2 | 3 | import org.hibernate.cache.CacheException; 4 | import org.hibernate.cache.spi.CacheDataDescription; 5 | import org.hibernate.cache.spi.NaturalIdRegion; 6 | import org.hibernate.cfg.Settings; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import redis.clients.jedis.JedisPool; 10 | import redis.clients.jedis.JedisPoolConfig; 11 | import redis.clients.jedis.Protocol; 12 | import sun.reflect.generics.reflectiveObjects.NotImplementedException; 13 | 14 | import java.util.Properties; 15 | 16 | /** 17 | * @author jtlee 18 | * @author 84june 19 | */ 20 | public class RedisRegionFactory extends AbstractRedisRegionFactory { 21 | 22 | private final Logger logger = LoggerFactory.getLogger(getClass()); 23 | 24 | public RedisRegionFactory(Properties properties) { 25 | this.properties = properties; 26 | } 27 | 28 | @Override 29 | public void start(Settings settings, Properties properties) throws CacheException { 30 | this.settings = settings; 31 | this.properties = properties; 32 | logger.info("Initializing RedisClient(Jedis)..."); 33 | this.pool = new JedisPool(new JedisPoolConfig(), properties.getProperty("redis.host", "localhost"), 34 | Integer.valueOf(properties.getProperty("redis.port", String.valueOf(Protocol.DEFAULT_PORT))), 35 | Integer.valueOf(properties.getProperty("redis.timeout",String.valueOf(Protocol.DEFAULT_TIMEOUT))), 36 | properties.getProperty("redis.password",null)); 37 | } 38 | 39 | @Override 40 | public void stop() { 41 | this.pool.destroy(); 42 | } 43 | 44 | @Override 45 | public NaturalIdRegion buildNaturalIdRegion(String regionName, Properties properties, CacheDataDescription metadata) throws CacheException { 46 | throw new NotImplementedException(); //TODO still not implemented 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/net/daum/clix/hibernate/redis/jedis/JedisCacheImpl.java: -------------------------------------------------------------------------------- 1 | package net.daum.clix.hibernate.redis.jedis; 2 | 3 | import net.daum.clix.hibernate.redis.RedisCache; 4 | import org.hibernate.cache.CacheException; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.core.serializer.support.DeserializingConverter; 8 | import org.springframework.core.serializer.support.SerializingConverter; 9 | import redis.clients.jedis.Jedis; 10 | import redis.clients.jedis.JedisPool; 11 | import redis.clients.jedis.exceptions.JedisConnectionException; 12 | 13 | /** 14 | * @author jtlee 15 | * @author 84june 16 | */ 17 | public class JedisCacheImpl implements RedisCache { 18 | 19 | private JedisPool jedisPool; 20 | 21 | private Jedis jedis; 22 | 23 | private String regionName; 24 | 25 | private final Logger logger = LoggerFactory.getLogger(getClass()); 26 | 27 | public JedisCacheImpl(JedisPool jedisPool, String regionName) { 28 | this.jedisPool = jedisPool; 29 | this.regionName = regionName; 30 | this.jedis = jedisPool.getResource(); 31 | } 32 | 33 | @Override 34 | public Object get(Object key) throws CacheException { 35 | Object o = null; 36 | 37 | byte[] k = serializeObject(key.toString()); 38 | try { 39 | byte[] v = jedis.get(k); 40 | if (v != null && v.length > 0) { 41 | o = deserializeObject(v); 42 | } 43 | 44 | return o; 45 | } catch (JedisConnectionException e) { 46 | logger.error(key.toString(), e); 47 | } 48 | return null; 49 | } 50 | 51 | @Override 52 | public void put(Object key, Object value) throws CacheException { 53 | byte[] k = serializeObject(key.toString()); 54 | byte[] v = serializeObject(value); 55 | 56 | try { 57 | jedis.set(k, v); 58 | } catch (JedisConnectionException e) { 59 | logger.error(key.toString(), e); 60 | } 61 | } 62 | 63 | @Override 64 | public void remove(Object key) throws CacheException { 65 | try { 66 | jedis.del(serializeObject(key.toString())); 67 | } catch (JedisConnectionException e) { 68 | logger.error(key.toString(), e); 69 | } 70 | } 71 | 72 | @Override 73 | public boolean exists(String key) { 74 | try { 75 | return jedis.exists(serializeObject(key)); 76 | } catch (JedisConnectionException e) { 77 | logger.error(key, e); 78 | } 79 | return false; 80 | } 81 | 82 | @Override 83 | public String getRegionName() { 84 | return this.regionName; 85 | } 86 | 87 | @Override 88 | public void destory() { 89 | jedisPool.returnResource(jedis); 90 | } 91 | 92 | private byte[] serializeObject(Object obj) { 93 | SerializingConverter sc = new SerializingConverter(); 94 | return sc.convert(obj); 95 | } 96 | 97 | private Object deserializeObject(byte[] b) { 98 | DeserializingConverter dc = new DeserializingConverter(); 99 | return dc.convert(b); 100 | } 101 | 102 | public boolean lock(Object key, Integer expireMsecs) throws InterruptedException { 103 | 104 | String lockKey = generateLockKey(key); 105 | long expires = System.currentTimeMillis() + expireMsecs + 1; 106 | String expiresStr = String.valueOf(expires); 107 | long timeout = expireMsecs; 108 | 109 | while (timeout >= 0) { 110 | 111 | try { 112 | if (jedis.setnx(lockKey, expiresStr) == 1) { 113 | return true; 114 | } 115 | 116 | String currentValueStr = jedis.get(lockKey); 117 | if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) { 118 | // lock is expired 119 | 120 | String oldValueStr = jedis.getSet(lockKey, expiresStr); 121 | if (oldValueStr != null && oldValueStr.equals(currentValueStr)) { 122 | // lock acquired 123 | return true; 124 | } 125 | } 126 | } catch (JedisConnectionException e) { 127 | logger.error(key.toString(), e); 128 | return false; 129 | } 130 | logger.info("{} is now locking and waiting for unlock", key.toString()); 131 | timeout -= 100; 132 | Thread.sleep(100); 133 | } 134 | return false; 135 | } 136 | 137 | @Override 138 | public void unlock(Object key) { 139 | jedis.del(generateLockKey(key)); 140 | } 141 | 142 | private String generateLockKey(Object key) { 143 | 144 | if (null == key) { 145 | throw new IllegalArgumentException("key must not be null"); 146 | } 147 | 148 | return key.toString() + ".lock"; 149 | } 150 | 151 | 152 | } 153 | -------------------------------------------------------------------------------- /src/main/java/net/daum/clix/hibernate/redis/region/RedisCollectionRegion.java: -------------------------------------------------------------------------------- 1 | package net.daum.clix.hibernate.redis.region; 2 | 3 | import net.daum.clix.hibernate.redis.RedisCache; 4 | import net.daum.clix.hibernate.redis.strategy.RedisAccessStrategyFactory; 5 | import org.hibernate.cache.CacheException; 6 | import org.hibernate.cache.spi.CacheDataDescription; 7 | import org.hibernate.cache.spi.CollectionRegion; 8 | import org.hibernate.cache.spi.access.AccessType; 9 | import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy; 10 | import org.hibernate.cfg.Settings; 11 | 12 | import java.util.Properties; 13 | 14 | /** 15 | * @author jtlee 16 | * @author 84june 17 | */ 18 | public class RedisCollectionRegion extends RedisTransactionalRegion implements CollectionRegion { 19 | 20 | public RedisCollectionRegion(RedisAccessStrategyFactory accessStrategyFactory, RedisCache cache, Properties properties, CacheDataDescription metadata, Settings settings) { 21 | super(accessStrategyFactory, cache, properties, metadata, settings); 22 | } 23 | 24 | @Override 25 | public CollectionRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException { 26 | return accessStrategyFactory.createCollectionRegionAccessStrategy(this, accessType); 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /src/main/java/net/daum/clix/hibernate/redis/region/RedisEntityRegion.java: -------------------------------------------------------------------------------- 1 | package net.daum.clix.hibernate.redis.region; 2 | 3 | import net.daum.clix.hibernate.redis.RedisCache; 4 | import net.daum.clix.hibernate.redis.strategy.RedisAccessStrategyFactory; 5 | import org.hibernate.cache.CacheException; 6 | import org.hibernate.cache.spi.CacheDataDescription; 7 | import org.hibernate.cache.spi.EntityRegion; 8 | import org.hibernate.cache.spi.access.AccessType; 9 | import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; 10 | import org.hibernate.cfg.Settings; 11 | 12 | import java.util.Properties; 13 | 14 | /** 15 | * @author jtlee 16 | * @author 84june 17 | */ 18 | public class RedisEntityRegion extends RedisTransactionalRegion implements EntityRegion { 19 | 20 | public RedisEntityRegion(RedisAccessStrategyFactory accessStrategyFactory, RedisCache cache, Properties properties, CacheDataDescription metadata, Settings settings) { 21 | super(accessStrategyFactory, cache, properties, metadata, settings); 22 | } 23 | 24 | @Override 25 | public EntityRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException { 26 | return accessStrategyFactory.createEntityRegionAccessStrategy(this, accessType); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/net/daum/clix/hibernate/redis/region/RedisQueryResultRegion.java: -------------------------------------------------------------------------------- 1 | package net.daum.clix.hibernate.redis.region; 2 | 3 | import net.daum.clix.hibernate.redis.RedisCache; 4 | import net.daum.clix.hibernate.redis.strategy.RedisAccessStrategyFactory; 5 | import org.hibernate.cache.CacheException; 6 | import org.hibernate.cache.spi.QueryResultsRegion; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.Properties; 11 | 12 | /** 13 | * @author jtlee 14 | * @author 84june 15 | */ 16 | public class RedisQueryResultRegion extends RedisRegion implements QueryResultsRegion { 17 | 18 | private final Logger LOG = LoggerFactory.getLogger(getClass()); 19 | 20 | public RedisQueryResultRegion(RedisAccessStrategyFactory accessStrategyFactory, RedisCache cache, Properties properties) { 21 | super(accessStrategyFactory, cache, properties); 22 | } 23 | 24 | @Override 25 | public Object get(Object key) throws CacheException { 26 | return cache.get(key); 27 | } 28 | 29 | @Override 30 | public void put(Object key, Object value) throws CacheException { 31 | cache.put(key, value); 32 | } 33 | 34 | @Override 35 | public void evict(Object key) throws CacheException { 36 | cache.remove(key); 37 | } 38 | 39 | @Override 40 | public void evictAll() throws CacheException { 41 | throw new UnsupportedOperationException("RedisQueryResultRegion#evictAll has not implemented yet!!"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/net/daum/clix/hibernate/redis/region/RedisRegion.java: -------------------------------------------------------------------------------- 1 | package net.daum.clix.hibernate.redis.region; 2 | 3 | import net.daum.clix.hibernate.redis.RedisCache; 4 | import net.daum.clix.hibernate.redis.strategy.RedisAccessStrategyFactory; 5 | import org.hibernate.cache.CacheException; 6 | import org.hibernate.cache.spi.Region; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | import java.util.Properties; 11 | 12 | /** 13 | * @author jtlee 14 | * @author 84june 15 | */ 16 | public abstract class RedisRegion implements Region { 17 | 18 | private static final String CACHE_LOCK_TIMEOUT_PROPERTY = "net.daum.clix.hibernate.redis.cache_lock_timeout"; 19 | private static final int DEFAULT_CACHE_LOCK_TIMEOUT = 1000; 20 | 21 | /** 22 | * RedisCache instance backing this Hibernate data region. 23 | */ 24 | protected final RedisCache cache; 25 | 26 | /** 27 | * The {@link net.daum.clix.hibernate.redis.strategy.RedisAccessStrategyFactory} used for creating various access strategies 28 | */ 29 | protected final RedisAccessStrategyFactory accessStrategyFactory; 30 | 31 | private int cacheLockTimeout; 32 | 33 | /** 34 | * Create a Hibernate data region backed by the given Redis instance. 35 | */ 36 | RedisRegion(RedisAccessStrategyFactory accessStrategyFactory, RedisCache cache, Properties properties) { 37 | this.accessStrategyFactory = accessStrategyFactory; 38 | this.cache = cache; 39 | String timeoutProperty = properties.getProperty(CACHE_LOCK_TIMEOUT_PROPERTY); 40 | this.cacheLockTimeout = timeoutProperty == null ? DEFAULT_CACHE_LOCK_TIMEOUT : Integer.parseInt(timeoutProperty); 41 | } 42 | 43 | /** 44 | * {@inheritDoc} 45 | */ 46 | public String getName() { 47 | return cache.getRegionName(); 48 | } 49 | 50 | /** 51 | * {@inheritDoc} 52 | */ 53 | public void destroy() throws CacheException { 54 | cache.destory(); 55 | } 56 | 57 | /** 58 | * {@inheritDoc} 59 | */ 60 | public long getSizeInMemory() { 61 | return -1; 62 | } 63 | 64 | /** 65 | * {@inheritDoc} 66 | */ 67 | public long getElementCountInMemory() { 68 | return -1; 69 | } 70 | 71 | /** 72 | * {@inheritDoc} 73 | */ 74 | public long getElementCountOnDisk() { 75 | return -1; 76 | } 77 | 78 | /** 79 | * {@inheritDoc} 80 | */ 81 | public Map toMap() { 82 | return new HashMap(); 83 | } 84 | 85 | /** 86 | * {@inheritDoc} 87 | */ 88 | public long nextTimestamp() { 89 | return System.currentTimeMillis() / 100; 90 | } 91 | 92 | /** 93 | * {@inheritDoc} 94 | */ 95 | public int getTimeout() { 96 | return cacheLockTimeout; 97 | } 98 | 99 | /** 100 | * Return the RedisCache instance backing this Hibernate data region. 101 | */ 102 | public RedisCache getRedisCache() { 103 | return cache; 104 | } 105 | 106 | /** 107 | * Returns true if this region contains data for the given key. 108 | *

109 | * This is a Hibernate 3.5 method. 110 | */ 111 | public boolean contains(Object key) { 112 | return cache.exists(key.toString()); 113 | } 114 | 115 | public Object get(Object key){ 116 | return cache.get(key); 117 | } 118 | 119 | public void put(Object key, Object value){ 120 | cache.put(key, value); 121 | } 122 | 123 | public boolean writeLock(Object key){ 124 | try { 125 | return cache.lock(key, getTimeout()); 126 | } catch (InterruptedException e) { 127 | return false; 128 | } 129 | } 130 | 131 | public void releaseLock(Object key){ 132 | cache.unlock(key); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/net/daum/clix/hibernate/redis/region/RedisTimestampsRegion.java: -------------------------------------------------------------------------------- 1 | package net.daum.clix.hibernate.redis.region; 2 | 3 | import net.daum.clix.hibernate.redis.RedisCache; 4 | import net.daum.clix.hibernate.redis.strategy.RedisAccessStrategyFactory; 5 | import org.hibernate.cache.CacheException; 6 | import org.hibernate.cache.spi.TimestampsRegion; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.Properties; 11 | 12 | /** 13 | * @author jtlee 14 | * @author 84june 15 | */ 16 | public class RedisTimestampsRegion extends RedisRegion implements TimestampsRegion { 17 | 18 | private final Logger LOG = LoggerFactory.getLogger(getClass()); 19 | 20 | public RedisTimestampsRegion(RedisAccessStrategyFactory accessStrategyFactory, RedisCache cache, Properties properties) { 21 | super(accessStrategyFactory, cache, properties); 22 | } 23 | 24 | @Override 25 | public Object get(Object key) throws CacheException { 26 | LOG.debug("called get : {}", key); 27 | return cache.get(key); 28 | } 29 | 30 | @Override 31 | public void put(Object key, Object value) throws CacheException { 32 | LOG.debug("called put by K:{}, V:{}", key, value); 33 | cache.put(key, value); 34 | } 35 | 36 | @Override 37 | public void evict(Object key) throws CacheException { 38 | cache.remove(key); 39 | } 40 | 41 | @Override 42 | public void evictAll() throws CacheException { 43 | throw new UnsupportedOperationException("RedisTimestampsRegion#evictAll has not implemented yet!!"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/net/daum/clix/hibernate/redis/region/RedisTransactionalRegion.java: -------------------------------------------------------------------------------- 1 | package net.daum.clix.hibernate.redis.region; 2 | 3 | import net.daum.clix.hibernate.redis.RedisCache; 4 | import net.daum.clix.hibernate.redis.strategy.RedisAccessStrategyFactory; 5 | import org.hibernate.cache.spi.CacheDataDescription; 6 | import org.hibernate.cache.spi.TransactionalDataRegion; 7 | import org.hibernate.cfg.Settings; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.util.Properties; 12 | 13 | /** 14 | * @author 84june 15 | */ 16 | public class RedisTransactionalRegion extends RedisRegion implements TransactionalDataRegion { 17 | 18 | private final Logger LOG = LoggerFactory.getLogger(getClass()); 19 | 20 | /** 21 | * Hibernate settings associated with the persistence unit. 22 | */ 23 | protected final Settings settings; 24 | 25 | /** 26 | * Metadata associated with the objects stored in the region. 27 | */ 28 | protected final CacheDataDescription metadata; 29 | 30 | RedisTransactionalRegion(RedisAccessStrategyFactory accessStrategyFactory, RedisCache cache, Properties properties, 31 | CacheDataDescription metadata, Settings settings) { 32 | super(accessStrategyFactory, cache, properties); 33 | this.metadata = metadata; 34 | this.settings = settings; 35 | } 36 | 37 | @Override 38 | public boolean isTransactionAware() { 39 | return false; 40 | } 41 | 42 | @Override 43 | public CacheDataDescription getCacheDataDescription() { 44 | return metadata; 45 | } 46 | 47 | public Settings getSettings() { 48 | return settings; 49 | } 50 | 51 | public final void remove(Object key) { 52 | cache.remove(key); 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /src/main/java/net/daum/clix/hibernate/redis/strategy/AbstractReadWriteRedisAccessStrategy.java: -------------------------------------------------------------------------------- 1 | package net.daum.clix.hibernate.redis.strategy; 2 | 3 | import net.daum.clix.hibernate.redis.region.RedisTransactionalRegion; 4 | import org.hibernate.cache.CacheException; 5 | import org.hibernate.cache.spi.access.SoftLock; 6 | import org.hibernate.cfg.Settings; 7 | 8 | import java.io.Serializable; 9 | import java.util.Comparator; 10 | 11 | 12 | /** 13 | * Superclass for all Redis specific Hibernate AccessStrategy implementations. 14 | * 15 | * @param type of the enclosed region 16 | * @author 84june 17 | */ 18 | abstract class AbstractReadWriteRedisAccessStrategy extends AbstractRedisAccessStrategy { 19 | 20 | private final Comparator versionComparator; 21 | 22 | /** 23 | * Create an access strategy wrapping the given region. 24 | */ 25 | AbstractReadWriteRedisAccessStrategy(T region, Settings settings) { 26 | super(region, settings); 27 | this.versionComparator = region.getCacheDataDescription().getVersionComparator(); 28 | } 29 | 30 | public Object get(Object key, long txTimestamp) throws CacheException { 31 | Item item = (Item) region.get(key); 32 | if (null != item && item.isReadable(txTimestamp)) { 33 | return item.getValue(); 34 | } 35 | return null; 36 | } 37 | 38 | public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride) throws CacheException { 39 | 40 | if (region.writeLock(key)) { 41 | try { 42 | if (region.contains(key)) { 43 | Item item = (Item) region.get(key); 44 | if (!item.isWriteable(version, versionComparator)) { 45 | return false; 46 | } 47 | } 48 | region.put(key, new Item(version, txTimestamp, value)); 49 | } finally { 50 | region.releaseLock(key); 51 | } 52 | return true; 53 | } 54 | return false; 55 | } 56 | 57 | /** 58 | * Returns false since this is a read/write cache access strategy 59 | */ 60 | public boolean insert(Object key, Object value, Object version) throws CacheException { 61 | return false; 62 | } 63 | 64 | public boolean afterInsert(Object key, Object value, Object version) throws CacheException { 65 | if (region.writeLock(key)) { 66 | try { 67 | Item item = (Item) region.get(key); 68 | if (null != item && !item.isWriteable(version, versionComparator)) { 69 | return false; 70 | } 71 | region.put(key, new Item(version, region.nextTimestamp(), value)); 72 | } finally { 73 | region.releaseLock(key); 74 | } 75 | return true; 76 | } 77 | return false; 78 | } 79 | 80 | /** 81 | * Returns false since this is a read/write cache access strategy 82 | */ 83 | public boolean update(Object key, Object value, Object currentVersion, Object previousVersion) throws CacheException { 84 | return false; 85 | } 86 | 87 | public boolean afterUpdate(Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock) throws CacheException { 88 | if (region.writeLock(key)) { 89 | try { 90 | Item item = (Item) region.get(key); 91 | if (null != item && !item.isWriteable(currentVersion, versionComparator)) { 92 | return false; 93 | } 94 | region.put(key, new Item(currentVersion, region.nextTimestamp(), value)); 95 | } finally { 96 | region.releaseLock(key); 97 | } 98 | return true; 99 | } 100 | return false; 101 | } 102 | 103 | protected final static class Item implements Serializable { 104 | 105 | private static final long serialVersionUID = -9173693640486739767L; 106 | 107 | private final Object version; 108 | 109 | private final long txTimestamp; 110 | 111 | private final Object value; 112 | 113 | public Item(Object version, long txTimestamp, Object value) { 114 | this.version = version; 115 | this.txTimestamp = txTimestamp; 116 | this.value = value; 117 | } 118 | 119 | public boolean isReadable(long txTimestamp) { 120 | return txTimestamp > this.txTimestamp; 121 | } 122 | 123 | public boolean isWriteable(Object newVersion, Comparator versionComparator) { 124 | return version != null && versionComparator.compare(version, newVersion) < 0; 125 | } 126 | 127 | @Override 128 | public boolean equals(Object o) { 129 | if (this == o) return true; 130 | if (o == null || getClass() != o.getClass()) return false; 131 | 132 | Item item = (Item) o; 133 | 134 | if (txTimestamp != item.txTimestamp) return false; 135 | if (!value.equals(item.value)) return false; 136 | if (version != null ? !version.equals(item.version) : item.version != null) return false; 137 | 138 | return true; 139 | } 140 | 141 | @Override 142 | public int hashCode() { 143 | int result = version != null ? version.hashCode() : 0; 144 | result = 31 * result + (int) (txTimestamp ^ (txTimestamp >>> 32)); 145 | result = 31 * result + value.hashCode(); 146 | return result; 147 | } 148 | 149 | public Object getValue() { 150 | return value; 151 | } 152 | 153 | } 154 | } 155 | 156 | -------------------------------------------------------------------------------- /src/main/java/net/daum/clix/hibernate/redis/strategy/AbstractRedisAccessStrategy.java: -------------------------------------------------------------------------------- 1 | package net.daum.clix.hibernate.redis.strategy; 2 | 3 | import net.daum.clix.hibernate.redis.RedisCache; 4 | import net.daum.clix.hibernate.redis.region.RedisTransactionalRegion; 5 | import org.hibernate.cache.CacheException; 6 | import org.hibernate.cache.spi.access.SoftLock; 7 | import org.hibernate.cfg.Settings; 8 | 9 | 10 | /** 11 | * Superclass for all Redis specific Hibernate AccessStrategy implementations. 12 | * 13 | * @param type of the enclosed region 14 | * @author 84june 15 | */ 16 | abstract class AbstractRedisAccessStrategy { 17 | 18 | /** 19 | * The wrapped Hibernate cache region. 20 | */ 21 | protected final T region; 22 | /** 23 | * The settings for this persistence unit. 24 | */ 25 | protected final Settings settings; 26 | /** 27 | * RedisCache client instance 28 | */ 29 | protected final RedisCache cache; 30 | 31 | /** 32 | * Create an access strategy wrapping the given region. 33 | */ 34 | AbstractRedisAccessStrategy(T region, Settings settings) { 35 | this.region = region; 36 | this.settings = settings; 37 | this.cache = region.getRedisCache(); 38 | } 39 | 40 | /** 41 | * This method is a placeholder for method signatures supplied by interfaces pulled in further down the class 42 | * hierarchy. 43 | * 44 | */ 45 | public final boolean putFromLoad(Object key, Object value, long txTimestamp, Object version) throws CacheException { 46 | return putFromLoad(key, value, txTimestamp, version, settings.isMinimalPutsEnabled()); 47 | } 48 | 49 | /** 50 | * This method is a placeholder for method signatures supplied by interfaces pulled in further down the class 51 | * hierarchy. 52 | * 53 | */ 54 | public abstract boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride) 55 | throws CacheException; 56 | 57 | /** 58 | * Region locks are not supported. 59 | * 60 | * @return null 61 | */ 62 | public final SoftLock lockRegion() { 63 | return null; 64 | } 65 | 66 | public final void unlockRegion(SoftLock lock) throws CacheException { 67 | } 68 | 69 | /** 70 | * A no-op since this is an asynchronous cache access strategy. 71 | * 72 | */ 73 | public void remove(Object key) throws CacheException { 74 | } 75 | 76 | /** 77 | * Not supported yet!! Called to evict data from the entire region 78 | * 79 | */ 80 | public final void removeAll() throws CacheException { 81 | // region.clear(); 82 | } 83 | 84 | /** 85 | * Remove the given mapping without regard to transactional safety 86 | * 87 | */ 88 | public final void evict(Object key) throws CacheException { 89 | region.remove(key); 90 | } 91 | 92 | /** 93 | * Not Supported yet!! Remove all mappings without regard to transactional safety 94 | * 95 | */ 96 | public final void evictAll() throws CacheException { 97 | // region.clear(); 98 | } 99 | } 100 | 101 | -------------------------------------------------------------------------------- /src/main/java/net/daum/clix/hibernate/redis/strategy/NonStrictReadWriteRedisCollectionRegionAccessStrategy.java: -------------------------------------------------------------------------------- 1 | package net.daum.clix.hibernate.redis.strategy; 2 | 3 | import net.daum.clix.hibernate.redis.region.RedisCollectionRegion; 4 | import org.hibernate.cache.CacheException; 5 | import org.hibernate.cache.spi.CollectionRegion; 6 | import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy; 7 | import org.hibernate.cache.spi.access.SoftLock; 8 | import org.hibernate.cfg.Settings; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | /** 13 | * @author jtlee 14 | * @author 84june 15 | */ 16 | public class NonStrictReadWriteRedisCollectionRegionAccessStrategy extends AbstractRedisAccessStrategy 17 | implements CollectionRegionAccessStrategy { 18 | 19 | private final Logger LOG = LoggerFactory.getLogger(getClass()); 20 | 21 | public NonStrictReadWriteRedisCollectionRegionAccessStrategy(RedisCollectionRegion region, Settings settings) { 22 | super(region, settings); 23 | } 24 | 25 | @Override 26 | public CollectionRegion getRegion() { 27 | return region; 28 | } 29 | 30 | @Override 31 | public Object get(Object key, long txTimestamp) throws CacheException { 32 | LOG.debug("called get by K:{}", key); 33 | return cache.get(key); 34 | } 35 | 36 | /** 37 | * {@inheritDoc} 38 | */ 39 | public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride) 40 | throws CacheException { 41 | LOG.debug("called putFromLoad by K:{}, V:{}", key, value); 42 | if (minimalPutOverride && region.contains(key)) { 43 | return false; 44 | } else { 45 | cache.put(key, value); 46 | return true; 47 | } 48 | } 49 | 50 | /** 51 | * Since this is a non-strict read/write strategy item locking is not used. 52 | */ 53 | public SoftLock lockItem(Object key, Object version) throws CacheException { 54 | return null; 55 | } 56 | 57 | /** 58 | * Since this is a non-strict read/write strategy item locking is not used. 59 | */ 60 | public void unlockItem(Object key, SoftLock lock) throws CacheException { 61 | // LOG.debug("called unlockItem K:{}", key); 62 | // region.remove(key); 63 | } 64 | 65 | /** 66 | * {@inheritDoc} 67 | */ 68 | @Override 69 | public void remove(Object key) throws CacheException { 70 | LOG.debug("called remove K:{}", key); 71 | region.remove(key); 72 | } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /src/main/java/net/daum/clix/hibernate/redis/strategy/NonStrictReadWriteRedisEntityRegionAccessStrategy.java: -------------------------------------------------------------------------------- 1 | package net.daum.clix.hibernate.redis.strategy; 2 | 3 | import net.daum.clix.hibernate.redis.region.RedisEntityRegion; 4 | import org.hibernate.cache.CacheException; 5 | import org.hibernate.cache.spi.EntityRegion; 6 | import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; 7 | import org.hibernate.cache.spi.access.SoftLock; 8 | import org.hibernate.cfg.Settings; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | /** 13 | * @author jtlee 14 | * @author 84june 15 | */ 16 | public class NonStrictReadWriteRedisEntityRegionAccessStrategy extends AbstractRedisAccessStrategy 17 | implements EntityRegionAccessStrategy { 18 | 19 | private final Logger LOG = LoggerFactory.getLogger(getClass()); 20 | 21 | public NonStrictReadWriteRedisEntityRegionAccessStrategy(RedisEntityRegion region, Settings settings) { 22 | super(region, settings); 23 | } 24 | 25 | @Override 26 | public EntityRegion getRegion() { 27 | return this.region; 28 | } 29 | 30 | @Override 31 | public Object get(Object key, long txTimestamp) throws CacheException { 32 | LOG.debug("called get K:{}", key); 33 | return cache.get(key); 34 | } 35 | 36 | @Override 37 | public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride) 38 | throws CacheException { 39 | LOG.debug("called putFromLoad by K:{}, V:{}", key, value); 40 | if (minimalPutOverride && region.contains(key)) { 41 | return false; 42 | } else { 43 | cache.put(key, value); 44 | return true; 45 | } 46 | } 47 | 48 | /** 49 | * Since this is a non-strict read/write strategy item locking is not used. 50 | */ 51 | @Override 52 | public SoftLock lockItem(Object key, Object version) throws CacheException { 53 | return null; 54 | } 55 | 56 | /** 57 | * Since this is a non-strict read/write strategy item locking is not used. 58 | */ 59 | @Override 60 | public void unlockItem(Object key, SoftLock lock) throws CacheException { 61 | region.remove(key); 62 | } 63 | 64 | /** 65 | * Returns false since this is an asynchronous cache access strategy. 66 | */ 67 | @Override 68 | public boolean insert(Object key, Object value, Object version) throws CacheException { 69 | return false; 70 | } 71 | 72 | /** 73 | * Returns false since this is a non-strict read/write cache access strategy 74 | */ 75 | @Override 76 | public boolean afterInsert(Object key, Object value, Object version) throws CacheException { 77 | return false; 78 | } 79 | 80 | /** 81 | * Removes the entry since this is a non-strict read/write cache strategy. 82 | */ 83 | @Override 84 | public boolean update(Object key, Object value, Object currentVersion, Object previousVersion) throws CacheException { 85 | LOG.debug("called update by K:{}, V:{}", key, value); 86 | remove(key); 87 | return false; 88 | } 89 | 90 | /** 91 | * {@inheritDoc} 92 | */ 93 | @Override 94 | public boolean afterUpdate(Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock) throws CacheException { 95 | return false; 96 | } 97 | 98 | /** 99 | * {@inheritDoc} 100 | */ 101 | @Override 102 | public void remove(Object key) throws CacheException { 103 | cache.remove(key); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/net/daum/clix/hibernate/redis/strategy/ReadOnlyRedisCollectionRegionAccessStrategy.java: -------------------------------------------------------------------------------- 1 | package net.daum.clix.hibernate.redis.strategy; 2 | 3 | import net.daum.clix.hibernate.redis.region.RedisCollectionRegion; 4 | import org.hibernate.cache.CacheException; 5 | import org.hibernate.cache.spi.CollectionRegion; 6 | import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy; 7 | import org.hibernate.cache.spi.access.SoftLock; 8 | import org.hibernate.cfg.Settings; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | /** 13 | * @author jtlee 14 | * @author 84june 15 | */ 16 | public class ReadOnlyRedisCollectionRegionAccessStrategy extends AbstractRedisAccessStrategy 17 | implements CollectionRegionAccessStrategy { 18 | 19 | private final Logger LOG = LoggerFactory.getLogger(getClass()); 20 | 21 | public ReadOnlyRedisCollectionRegionAccessStrategy(RedisCollectionRegion region, Settings settings) { 22 | super(region, settings); 23 | } 24 | 25 | /** 26 | * {@inheritDoc} 27 | */ 28 | @Override 29 | public CollectionRegion getRegion() { 30 | return region; 31 | } 32 | 33 | /** 34 | * {@inheritDoc} 35 | */ 36 | @Override 37 | public Object get(Object key, long txTimestamp) throws CacheException { 38 | LOG.debug("called get by K:{}", key); 39 | return cache.get(key); 40 | } 41 | 42 | /** 43 | * {@inheritDoc} 44 | */ 45 | @Override 46 | public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride) 47 | throws CacheException { 48 | LOG.debug("called putFromLoad by K:{}, V:{}", key, value); 49 | if (minimalPutOverride && region.contains(key)) { 50 | return false; 51 | } else { 52 | cache.put(key, value); 53 | return true; 54 | } 55 | } 56 | 57 | /** 58 | * Throws UnsupportedOperationException since this cache is read-only 59 | * 60 | * @throws UnsupportedOperationException always 61 | */ 62 | @Override 63 | public SoftLock lockItem(Object key, Object version) throws UnsupportedOperationException { 64 | throw new UnsupportedOperationException("Can't write to a readonly object"); 65 | } 66 | 67 | /** 68 | * A no-op since this cache is read-only 69 | */ 70 | @Override 71 | public void unlockItem(Object key, SoftLock lock) throws CacheException { 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/net/daum/clix/hibernate/redis/strategy/ReadOnlyRedisEntityRegionAccessStrategy.java: -------------------------------------------------------------------------------- 1 | package net.daum.clix.hibernate.redis.strategy; 2 | 3 | import net.daum.clix.hibernate.redis.region.RedisEntityRegion; 4 | import org.hibernate.cache.CacheException; 5 | import org.hibernate.cache.spi.EntityRegion; 6 | import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; 7 | import org.hibernate.cache.spi.access.SoftLock; 8 | import org.hibernate.cfg.Settings; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | /** 13 | * @author jtlee 14 | * @author 84june 15 | */ 16 | public class ReadOnlyRedisEntityRegionAccessStrategy extends AbstractRedisAccessStrategy 17 | implements EntityRegionAccessStrategy { 18 | 19 | private final Logger LOG = LoggerFactory.getLogger(getClass()); 20 | 21 | public ReadOnlyRedisEntityRegionAccessStrategy(RedisEntityRegion region, Settings settings) { 22 | super(region, settings); 23 | } 24 | 25 | /** 26 | * {@inheritDoc} 27 | */ 28 | @Override 29 | public EntityRegion getRegion() { 30 | return region; 31 | } 32 | 33 | /** 34 | * {@inheritDoc} 35 | */ 36 | @Override 37 | public Object get(Object key, long txTimestamp) throws CacheException { 38 | LOG.debug("called get by K:{}", key); 39 | return cache.get(key); 40 | } 41 | 42 | /** 43 | * {@inheritDoc} 44 | */ 45 | @Override 46 | public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride) throws CacheException { 47 | LOG.debug("called put by K:{}, V:{}", key, value); 48 | if (minimalPutOverride && region.contains(key)) { 49 | return false; 50 | } else { 51 | cache.put(key, value); 52 | return true; 53 | } 54 | } 55 | 56 | /** 57 | * Throws UnsupportedOperationException since this cache is read-only 58 | * 59 | * @throws UnsupportedOperationException always 60 | */ 61 | @Override 62 | public SoftLock lockItem(Object key, Object version) throws UnsupportedOperationException { 63 | throw new UnsupportedOperationException("Can't write to a readonly object"); 64 | } 65 | 66 | /** 67 | * A no-op since this cache is read-only 68 | */ 69 | public void unlockItem(Object key, SoftLock lock) throws CacheException { 70 | //throw new UnsupportedOperationException("Can't write to a readonly object"); 71 | } 72 | 73 | /** 74 | * This cache is asynchronous hence a no-op 75 | */ 76 | public boolean insert(Object key, Object value, Object version) throws CacheException { 77 | return false; 78 | } 79 | 80 | /** 81 | * {@inheritDoc} 82 | */ 83 | public boolean afterInsert(Object key, Object value, Object version) throws CacheException { 84 | cache.put(key, value); 85 | return true; 86 | } 87 | 88 | /** 89 | * Throws UnsupportedOperationException since this cache is read-only 90 | * 91 | * @throws UnsupportedOperationException always 92 | */ 93 | public boolean update(Object key, Object value, Object currentVersion, Object previousVersion) throws UnsupportedOperationException { 94 | throw new UnsupportedOperationException("Can't write to a readonly object"); 95 | } 96 | 97 | /** 98 | * Throws UnsupportedOperationException since this cache is read-only 99 | * 100 | * @throws UnsupportedOperationException always 101 | */ 102 | public boolean afterUpdate(Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock) 103 | throws UnsupportedOperationException { 104 | throw new UnsupportedOperationException("Can't write to a readonly object"); 105 | } 106 | } 107 | 108 | -------------------------------------------------------------------------------- /src/main/java/net/daum/clix/hibernate/redis/strategy/ReadWriteRedisCollectionRegionAcessStrategy.java: -------------------------------------------------------------------------------- 1 | package net.daum.clix.hibernate.redis.strategy; 2 | 3 | import net.daum.clix.hibernate.redis.region.RedisCollectionRegion; 4 | import org.hibernate.cache.CacheException; 5 | import org.hibernate.cache.spi.CollectionRegion; 6 | import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy; 7 | import org.hibernate.cache.spi.access.SoftLock; 8 | import org.hibernate.cfg.Settings; 9 | import sun.reflect.generics.reflectiveObjects.NotImplementedException; 10 | 11 | /** 12 | * User: jtlee 13 | * Date: 3/5/13 14 | * Time: 1:50 PM 15 | */ 16 | public class ReadWriteRedisCollectionRegionAcessStrategy extends AbstractReadWriteRedisAccessStrategy implements CollectionRegionAccessStrategy { 17 | 18 | /** 19 | * Create an access strategy wrapping the given region. 20 | */ 21 | ReadWriteRedisCollectionRegionAcessStrategy(RedisCollectionRegion region, Settings settings) { 22 | super(region, settings); 23 | } 24 | 25 | @Override 26 | public CollectionRegion getRegion() { 27 | return region; 28 | } 29 | 30 | @Override 31 | public SoftLock lockItem(Object key, Object version) throws CacheException { 32 | //Do not support client-side locking 33 | return null; 34 | } 35 | 36 | @Override 37 | public void unlockItem(Object key, SoftLock lock) throws CacheException { 38 | //Do not support client-side locking 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/net/daum/clix/hibernate/redis/strategy/ReadWriteRedisEntityRegionAcessStrategy.java: -------------------------------------------------------------------------------- 1 | package net.daum.clix.hibernate.redis.strategy; 2 | 3 | import net.daum.clix.hibernate.redis.region.RedisEntityRegion; 4 | import org.hibernate.cache.CacheException; 5 | import org.hibernate.cache.spi.EntityRegion; 6 | import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; 7 | import org.hibernate.cache.spi.access.SoftLock; 8 | import org.hibernate.cfg.Settings; 9 | 10 | /** 11 | * User: jtlee 12 | * Date: 3/5/13 13 | * Time: 2:52 PM 14 | */ 15 | public class ReadWriteRedisEntityRegionAcessStrategy extends AbstractReadWriteRedisAccessStrategy implements EntityRegionAccessStrategy { 16 | 17 | /** 18 | * Create an access strategy wrapping the given region. 19 | */ 20 | ReadWriteRedisEntityRegionAcessStrategy(RedisEntityRegion region, Settings settings) { 21 | super(region, settings); 22 | } 23 | 24 | 25 | 26 | @Override 27 | public SoftLock lockItem(Object key, Object version) throws CacheException { 28 | //Do not support client side lock 29 | return null; 30 | } 31 | 32 | @Override 33 | public void unlockItem(Object key, SoftLock lock) throws CacheException { 34 | //Do not support client side lock 35 | } 36 | 37 | @Override 38 | public EntityRegion getRegion() { 39 | return region; 40 | } 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/net/daum/clix/hibernate/redis/strategy/RedisAccessStrategyFactory.java: -------------------------------------------------------------------------------- 1 | package net.daum.clix.hibernate.redis.strategy; 2 | 3 | import net.daum.clix.hibernate.redis.region.RedisCollectionRegion; 4 | import net.daum.clix.hibernate.redis.region.RedisEntityRegion; 5 | import org.hibernate.cache.spi.access.AccessType; 6 | import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy; 7 | import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; 8 | 9 | /** 10 | * Factory to create {@link EntityRegionAccessStrategy} 11 | * 12 | * @author 84june 13 | */ 14 | public interface RedisAccessStrategyFactory { 15 | 16 | /** 17 | * Create {@link EntityRegionAccessStrategy} for the input {@link RedisEntityRegion} and {@link AccessType} 18 | * 19 | * @param entityRegion 20 | * @param accessType 21 | * @return the created {@link EntityRegionAccessStrategy} 22 | */ 23 | public EntityRegionAccessStrategy createEntityRegionAccessStrategy(RedisEntityRegion entityRegion, AccessType accessType); 24 | 25 | /** 26 | * Create {@link CollectionRegionAccessStrategy} for the input {@link RedisCollectionRegion} and {@link AccessType} 27 | * 28 | * @param collectionRegion 29 | * @param accessType 30 | * @return the created {@link CollectionRegionAccessStrategy} 31 | */ 32 | public CollectionRegionAccessStrategy createCollectionRegionAccessStrategy(RedisCollectionRegion collectionRegion, 33 | AccessType accessType); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/net/daum/clix/hibernate/redis/strategy/RedisAccessStrategyFactoryImpl.java: -------------------------------------------------------------------------------- 1 | package net.daum.clix.hibernate.redis.strategy; 2 | 3 | import net.daum.clix.hibernate.redis.region.RedisCollectionRegion; 4 | import net.daum.clix.hibernate.redis.region.RedisEntityRegion; 5 | import org.hibernate.cache.spi.access.AccessType; 6 | import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy; 7 | import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | /** 12 | * An implementation of {@link RedisAccessStrategyFactory} 13 | * 14 | * @author 84june 15 | */ 16 | public class RedisAccessStrategyFactoryImpl implements RedisAccessStrategyFactory { 17 | 18 | private final Logger LOG = LoggerFactory.getLogger(getClass()); 19 | 20 | @Override 21 | public EntityRegionAccessStrategy createEntityRegionAccessStrategy(RedisEntityRegion entityRegion, AccessType accessType) { 22 | LOG.debug("called createEntityRegionAccessStrategy by accessType:{}", accessType); 23 | 24 | if (AccessType.READ_ONLY.equals(accessType)) { 25 | return new ReadOnlyRedisEntityRegionAccessStrategy(entityRegion, entityRegion.getSettings()); 26 | } else if (AccessType.NONSTRICT_READ_WRITE.equals(accessType)) { 27 | return new NonStrictReadWriteRedisEntityRegionAccessStrategy(entityRegion, entityRegion.getSettings()); 28 | } else if (AccessType.READ_WRITE.equals(accessType)){ 29 | return new ReadWriteRedisEntityRegionAcessStrategy(entityRegion, entityRegion.getSettings()); 30 | } 31 | 32 | throw new UnsupportedOperationException("Hibernate-redis supports READ_ONLY and NONSTRICT_READ_WRITE as concurrency strategies only."); 33 | } 34 | 35 | @Override 36 | public CollectionRegionAccessStrategy createCollectionRegionAccessStrategy(RedisCollectionRegion collectionRegion, AccessType accessType) { 37 | LOG.debug("called createCollectionRegionAccessStrategy by accessType:{}", accessType); 38 | if (AccessType.READ_ONLY.equals(accessType)) { 39 | return new ReadOnlyRedisCollectionRegionAccessStrategy(collectionRegion, collectionRegion.getSettings()); 40 | } else if (AccessType.NONSTRICT_READ_WRITE.equals(accessType)) { 41 | return new NonStrictReadWriteRedisCollectionRegionAccessStrategy(collectionRegion, collectionRegion.getSettings()); 42 | } else if (AccessType.READ_WRITE.equals(accessType)) { 43 | return new ReadWriteRedisCollectionRegionAcessStrategy(collectionRegion, collectionRegion.getSettings()); 44 | } 45 | 46 | throw new UnsupportedOperationException("Hibernate-redis supports READ_ONLY and NONSTRICT_READ_WRITE as concurrency strategies only."); 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/test/java/net/daum/clix/Campaign.java: -------------------------------------------------------------------------------- 1 | package net.daum.clix; 2 | 3 | import org.hibernate.annotations.Cache; 4 | import org.hibernate.annotations.CacheConcurrencyStrategy; 5 | 6 | import javax.persistence.Cacheable; 7 | import javax.persistence.Entity; 8 | import javax.persistence.GeneratedValue; 9 | import javax.persistence.Id; 10 | 11 | /** 12 | * Created with IntelliJ IDEA. 13 | * User: jtlee 14 | * Date: 8/10/12 15 | * Time: 4:20 PM 16 | * To change this template use File | Settings | File Templates. 17 | */ 18 | @Cacheable 19 | @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) 20 | @Entity 21 | public class Campaign { 22 | private Long seq; 23 | private String name; 24 | private Long budget; 25 | 26 | @Id 27 | @GeneratedValue 28 | public Long getSeq() { 29 | return seq; 30 | } 31 | 32 | public void setSeq(Long seq) { 33 | this.seq = seq; 34 | } 35 | 36 | public String getName() { 37 | return name; 38 | } 39 | 40 | public void setName(String name) { 41 | this.name = name; 42 | } 43 | 44 | public Long getBudget() { 45 | return budget; 46 | } 47 | 48 | public void setBudget(Long budget) { 49 | this.budget = budget; 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return seq + ":\t" + name + ":\t" + budget; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/net/daum/clix/test/CampaignTest.java: -------------------------------------------------------------------------------- 1 | package net.daum.clix.test; 2 | 3 | import net.daum.clix.Campaign; 4 | import org.hibernate.Criteria; 5 | import org.hibernate.Session; 6 | import org.hibernate.SessionFactory; 7 | import org.hibernate.Transaction; 8 | import org.hibernate.cfg.Configuration; 9 | import org.hibernate.criterion.Order; 10 | import org.hibernate.criterion.Restrictions; 11 | import org.junit.AfterClass; 12 | import org.junit.Before; 13 | import org.junit.BeforeClass; 14 | import org.junit.Test; 15 | import org.junit.internal.runners.JUnit4ClassRunner; 16 | import org.junit.runner.RunWith; 17 | import redis.clients.jedis.Jedis; 18 | import redis.clients.jedis.JedisPool; 19 | import redis.clients.jedis.JedisPoolConfig; 20 | 21 | import java.util.List; 22 | 23 | import static junit.framework.Assert.assertFalse; 24 | import static org.junit.Assert.assertNotNull; 25 | import static org.junit.Assert.assertTrue; 26 | 27 | /** 28 | * User: jtlee 29 | * Date: 8/10/12 30 | * Time: 4:29 PM 31 | */ 32 | @RunWith(JUnit4ClassRunner.class) 33 | public class CampaignTest { 34 | 35 | private static SessionFactory sessionFactory; 36 | 37 | @Test 38 | public void testSave() { 39 | 40 | Session session = sessionFactory.getCurrentSession(); 41 | 42 | Transaction tx = session.beginTransaction(); 43 | 44 | Campaign campaign = new Campaign(); 45 | campaign.setName("campaign"); 46 | campaign.setBudget(1000L); 47 | Long id = (Long) session.save(campaign); 48 | 49 | tx.commit(); 50 | 51 | } 52 | 53 | @BeforeClass 54 | public static void setUp() { 55 | 56 | sessionFactory = new Configuration().configure().buildSessionFactory(); 57 | 58 | Session session = sessionFactory.getCurrentSession(); 59 | 60 | Transaction tx = session.beginTransaction(); 61 | 62 | Campaign campaign = new Campaign(); 63 | campaign.setName("campaign2"); 64 | campaign.setBudget(1000L); 65 | Long id = (Long) session.save(campaign); 66 | 67 | tx.commit(); 68 | } 69 | 70 | @Test 71 | public void testSelect() { 72 | 73 | Session session = sessionFactory.getCurrentSession(); 74 | 75 | Transaction tx = session.beginTransaction(); 76 | 77 | Campaign campaign = (Campaign) session.get(Campaign.class, 1L); 78 | 79 | tx.commit(); 80 | 81 | assertNotNull(campaign); 82 | 83 | } 84 | 85 | @Test 86 | public void testQueryCache(){ 87 | Session session = sessionFactory.getCurrentSession(); 88 | 89 | Transaction tx = session.beginTransaction(); 90 | 91 | Criteria criteria = session.createCriteria(Campaign.class); 92 | criteria.add(Restrictions.like("name", "campaign%")); 93 | criteria.addOrder(Order.asc("name")); 94 | criteria.setFirstResult(0); 95 | criteria.setMaxResults(10); 96 | criteria.setCacheable(true); 97 | // criteria.setCacheRegion("@Sorted_queryCache"); 98 | List campaigns = criteria.list(); 99 | 100 | tx.commit(); 101 | 102 | assertFalse("It should not be empty",campaigns.isEmpty()); 103 | } 104 | 105 | @Test 106 | public void testCachedQuery(){ 107 | Session session = sessionFactory.getCurrentSession(); 108 | 109 | Transaction tx = session.beginTransaction(); 110 | 111 | Criteria criteria = session.createCriteria(Campaign.class); 112 | criteria.add(Restrictions.like("name", "campaign%")); 113 | criteria.addOrder(Order.asc("name")); 114 | criteria.setFirstResult(0); 115 | criteria.setMaxResults(10); 116 | criteria.setCacheable(true); 117 | // criteria.setCacheRegion("@Sorted_queryCache"); 118 | List campaigns = criteria.list(); 119 | 120 | tx.commit(); 121 | 122 | assertFalse("It should not be empty",campaigns.isEmpty()); 123 | 124 | } 125 | 126 | @Test 127 | public void testEvictCachedQuery(){ 128 | 129 | Session session = sessionFactory.getCurrentSession(); 130 | 131 | Transaction tx = session.beginTransaction(); 132 | 133 | Campaign campaign = (Campaign) session.get(Campaign.class, 1L); 134 | campaign.setName("kampaign100"); 135 | session.saveOrUpdate(campaign); 136 | 137 | Criteria criteria = session.createCriteria(Campaign.class); 138 | criteria.add(Restrictions.like("name", "campaign%")); 139 | criteria.addOrder(Order.asc("name")); 140 | criteria.setFirstResult(0); 141 | criteria.setMaxResults(10); 142 | criteria.setCacheable(true); 143 | // criteria.setCacheRegion("@Sorted_queryCache"); 144 | List campaigns = criteria.list(); 145 | 146 | tx.rollback(); 147 | 148 | assertFalse("It should not be empty",campaigns.isEmpty()); 149 | } 150 | 151 | @AfterClass 152 | public static void tearDown(){ 153 | // JedisPool pool = new JedisPool(new JedisPoolConfig(), "localhost"); 154 | // Jedis jedis = pool.getResource(); 155 | // jedis.flushDB(); 156 | } 157 | 158 | } 159 | -------------------------------------------------------------------------------- /src/test/resources/hibernate.cfg.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | jdbc:hsqldb:file:db/hsqltestdb;shutdown=true 8 | sa 9 | 10 | org.hsqldb.jdbcDriver 11 | org.hibernate.dialect.HSQLDialect 12 | 13 | true 14 | 15 | true 16 | create 17 | 23 | 24 | 0 25 | thread 26 | true 27 | true 28 | 10000 29 | net.daum.clix.hibernate.redis.RedisRegionFactory 30 | 31 | localhost 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Direct log messages to stdout 2 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 3 | log4j.appender.stdout.Target=System.out 4 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 5 | log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n 6 | 7 | # Root logger option 8 | log4j.rootLogger=INFO, stdout 9 | 10 | # Hibernate logging options (INFO only shows startup messages) 11 | log4j.logger.org.hibernate=INFO 12 | 13 | # Log JDBC bind parameter runtime arguments 14 | log4j.logger.org.hibernate.type=trace --------------------------------------------------------------------------------