redis作为hibernate的二级缓存

来源:互联网 发布:java电商架构 编辑:程序博客网 时间:2024/05/16 10:41

hibernate的二级缓存有好多,像ehcache。不过项目的缓存使用的是redis,而redis官方没有实现hibernate的二级缓存接口,只得自己实现。看看公司的高手如何做的吧。

先看配置:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- entityManagerFactory -->
    <beanid="entityManagerFactory"class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
        depends-on="cacheManagerFactory">
        ...
        <propertyname="jpaProperties">
            <props>
                ...
                <propkey="hibernate.cache.use_second_level_cache">true</prop>
                <!-- <prop key="hibernate.cache.use_query_cache">true</prop> -->
                <propkey="hibernate.cache.region.factory_class">xxx.xxx.framework.cache.hibernate.CacheRegionFactory</prop>
                ...
            </props>
        </property>
    </bean>  
     
    <!-- cache -->
    <beanid="cacheManager"class="xxx.xxx.framework.cache.redis.RedisCacheManager">
        <propertyname="connectionFactory"ref="redisConnectionFactory"/>
        <propertyname="namespace"value="payment"/>
    </bean>
     
    <beanid="cacheManagerFactory"class="xxx.xxx.framework.cache.hibernate.CacheManagerFactory">
        <propertyname="cacheManager"ref="cacheManager"/>
    </bean>

cacheManager是redis缓存的配置,二级缓存实现类CacheRegionFactory里面会用到它,但是hibernate缓存配置的只是配置实现类,没法注入CacheRegionFactory对象,所以这边多了个cacheManagerFactory,注意配置中的depends-on。它的代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
publicfinal class CacheManagerFactory implementsDisposableBean {
    privatestatic CacheManager CACH_EMANAGER;
 
    publicvoid setCacheManager(CacheManager cacheManager) {
        CACH_EMANAGER = cacheManager;
    }
 
    publicstatic CacheManager getCacheManager() {
        returnCACH_EMANAGER;
    }
 
    @Override
    publicvoid destroy() throwsException {
        CACH_EMANAGER = null;
    }
}
它就是负责生成cacheManager。注意getCacheManager是静态方法。然后我们看redis二级缓存的实现类:


?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
publicclass CacheRegionFactory implementsRegionFactory {
    privatestatic final long serialVersionUID = -1557439471733872383L;
    privateCacheManager cacheManager;
    privatestatic final AtomicLong CURRENT = newAtomicLong();
    protectedSettings settings;
 
    @Override
    publicvoid start(Settings settings, Properties properties) throwsCacheException {
        cacheManager = CacheManagerFactory.getCacheManager();
        this.settings = settings;
        Assert.notNull(cacheManager,"cacheManager is required,CacheManagerFactory must be init first");
    }
 
    @Override
    publicvoid stop() {
        cacheManager = null;
    }
 
    @Override
    publicboolean isMinimalPutsEnabledByDefault() {
        returntrue;
    }
 
    @Override
    publicAccessType getDefaultAccessType() {
        returnAccessType.NONSTRICT_READ_WRITE;
    }
 
    @Override
    publiclong nextTimestamp() {
        returnCURRENT.incrementAndGet();
    }
 
    @Override
    publicEntityRegion buildEntityRegion(String regionName, Properties properties, CacheDataDescription metadata)throwsCacheException {
        returnnew EntityRegionImpl(regionName, cacheManager.getCache(shortRegionName(regionName)), settings, properties, metadata);
    }
 
    @Override
    publicNaturalIdRegion buildNaturalIdRegion(String regionName, Properties properties, CacheDataDescription metadata)throwsCacheException {
        returnnew NaturalIdRegionImpl(regionName, cacheManager.getCache(shortRegionName(regionName)), settings, properties, metadata);
    }
 
    @Override
    publicCollectionRegion buildCollectionRegion(String regionName, Properties properties, CacheDataDescription metadata)throwsCacheException {
        returnnew CollectionRegionImpl(regionName, cacheManager.getCache(shortRegionName(regionName)), settings, properties, metadata);
    }
 
    @Override
    publicQueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties)throwsCacheException {
        returnnew QueryResultsRegionImpl(regionName, cacheManager.getCache(shortRegionName(regionName)), settings, properties);
    }
 
    @Override
    publicTimestampsRegion buildTimestampsRegion(String regionName, Properties properties) throwsCacheException {
        returnnew TimestampsRegionImpl(regionName, cacheManager.getCache(shortRegionName(regionName)), settings, properties);
    }
 
    privatestatic String shortRegionName(String regionName) {
        ...
    }
}
RegionFactory是hibernate的接口。在start里面通过调用CacheManagerFactory来获取redis的CacheManager。这里主要还是buildXX的几个方法,在这些方法里面通过 CacheManager和region获取cache,然后再生成hibernate需要的缓存对象


以buildEntityRegion为例,返回的对象EntityRegion是hibernate的接口,EntityRegionImpl怎是我们的实现。先看看它的实现:

?
1
2
3
4
5
6
7
8
9
10
11
publicclass EntityRegionImpl extendsAbstractTransactionalDataRegion implementsEntityRegion {
 
    publicEntityRegionImpl(String regionName, Cache cache, Settings settings, Properties properties, CacheDataDescription metadata) {
        super(regionName,cache, settings, properties, metadata);
    }
 
    @Override
    publicEntityRegionAccessStrategy buildAccessStrategy(AccessType accessType) throwsCacheException {
        returnnew EntityRegionAccessStrategyImpl(this, settings);
    }
}
这边可以分两部分看了一个是它的父类AbstractTransactionalDataRegion,另外一个是由EntityRegion继承而来的buildAccessStrategy(Hibernate EntityRegion的接口实现)。

先看它的父类,父类实现了hibernate的TansactionalDateRegion:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
publicclass AbstractTransactionalDataRegion extendsAbstractRegion implementsTransactionalDataRegion {
    privatefinal CacheDataDescription metadata;
 
    publicAbstractTransactionalDataRegion(String regionName, Cache cache, Settings settings, Properties properties, CacheDataDescription metadata) {
        super(regionName, cache, settings, properties);
        this.metadata = metadata;
    }
 
    @Override
    publicboolean isTransactionAware() {
        returnfalse;
    }
 
    @Override
    publicCacheDataDescription getCacheDataDescription() {
        returnmetadata;
    }
}
isTransactionAware是表示是否支持jta事务。然后再看它的父类AbstractRegion:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
publicclass AbstractRegion implementsRegion {
    privatestatic final AtomicLong CURRENT = newAtomicLong();
    privateString regionName;
    protectedfinal Cache cache;
    protectedfinal Properties properties;
    protectedfinal Settings settings;
    protectedfinal KeyGenerator keyGenerator = DefaultKeyGenerator.INSTANCE;
 
    publicAbstractRegion(String regionName, Cache cache, Settings settings, Properties properties) {
        this.regionName = regionName;
        this.cache = cache;
        this.settings = settings;
        this.properties = properties;
    }
 
    @Override
    publicString getName() {
        returnregionName;
    }
 
    @Override
    publicvoid destroy() throwsCacheException {
    }
 
    @Override
    publicboolean contains(Object key) {
        returncache.exists(key);
    }
 
    @Override
    publiclong getSizeInMemory() {
        return-1;
    }
 
    @Override
    publiclong getElementCountInMemory() {
        returncache.getStatistics().getSize();
    }
 
    @Override
    publiclong getElementCountOnDisk() {
        return-1;
    }
 
    @Override
    publicMap toMap() {
        returnCollections.EMPTY_MAP;
    }
 
    @Override
    publiclong nextTimestamp() {
        returnCURRENT.incrementAndGet();
    }
 
    @Override
    publicint getTimeout() {
        return300;
    }
 
    publicObject get(Object key) throwsCacheException {
        try{
            returnpostGet(cache.get(toKey(key)));
        }catch(Throwable e) {
            thrownew CacheException(e);
        }
    }
 
    publicvoid put(Object key, Object value) throwsCacheException {
        try{
            cache.put(toKey(key), value);
        }catch(Exception e) {
            thrownew CacheException(e);
        }
    }
 
    publicvoid evict(Object key) throwsCacheException {
        try{
            cache.evict(toKey(key));
        }catch(Exception e) {
            thrownew CacheException(e);
        }
    }
 
    publicvoid evictAll() throwsCacheException {
        try{
            cache.clear();
        }catch(Exception e) {
            thrownew CacheException(e);
        }
    }
 
    privateObject toKey(Object key) {
        if(key instanceofCacheKey) {
            key = ((CacheKey) key).getKey();
        }
        returnkeyGenerator.generate(key);
    }
......
这里实现了hibernate region的一些接口,另外一些对于缓存的一些操作方法如:put\get\evict等也做了实现。

现在我们回过头看看EntityRegionImpl里面的EntityRegionAccessStrategyImpl实现:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
publicclass EntityRegionAccessStrategyImpl extendsAbstractAccessStrategy<EntityRegionImpl> implementsEntityRegionAccessStrategy {
 
 
   publicEntityRegionAccessStrategyImpl(EntityRegionImpl region, Settings settings) {
        super(region, settings);
   }
 
 
   @Override
   publicEntityRegion getRegion() {
        returnregion;
   }
}
它也实现了hibernate的接口:EntityRegionAccessStrategy,字面意思就是缓存的访问策略。

先看看hibernate的这个接口的定义:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
publicinterface EntityRegionAccessStrategy extendsRegionAccessStrategy{
 
    /**
     * Get the wrapped entity cache region
     *
     * @return The underlying region
     */
    publicEntityRegion getRegion();
 
    /**
     * Called after an item has been inserted (before the transaction completes),
     * instead of calling evict().
     * This method is used by "synchronous" concurrency strategies.
     *
     * @param key The item key
     * @param value The item
     * @param version The item's version value
     * @return Were the contents of the cache actual changed by this operation?
     * @throws CacheException Propogated from underlying {@link org.hibernate.cache.spi.Region}
     */
    publicboolean insert(Object key, Object value, Object version) throwsCacheException;
 
    /**
     * Called after an item has been inserted (after the transaction completes),
     * instead of calling release().
     * This method is used by "asynchronous" concurrency strategies.
     *
     * @param key The item key
     * @param value The item
     * @param version The item's version value
     * @return Were the contents of the cache actual changed by this operation?
     * @throws CacheException Propogated from underlying {@link org.hibernate.cache.spi.Region}
     */
    publicboolean afterInsert(Object key, Object value, Object version) throwsCacheException;
 
    /**
     * Called after an item has been updated (before the transaction completes),
     * instead of calling evict(). This method is used by "synchronous" concurrency
     * strategies.
     *
     * @param key The item key
     * @param value The item
     * @param currentVersion The item's current version value
     * @param previousVersion The item's previous version value
     * @return Were the contents of the cache actual changed by this operation?
     * @throws CacheException Propogated from underlying {@link org.hibernate.cache.spi.Region}
     */
    publicboolean update(Object key, Object value, Object currentVersion, Object previousVersion) throwsCacheException;
 
    /**
     * Called after an item has been updated (after the transaction completes),
     * instead of calling release().  This method is used by "asynchronous"
     * concurrency strategies.
     *
     * @param key The item key
     * @param value The item
     * @param currentVersion The item's current version value
     * @param previousVersion The item's previous version value
     * @param lock The lock previously obtained from {@link #lockItem}
     * @return Were the contents of the cache actual changed by this operation?
     * @throws CacheException Propogated from underlying {@link org.hibernate.cache.spi.Region}
     */
    publicboolean afterUpdate(Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock)throwsCacheException;
}
它的父接口里面还有一些其他的接口如get,remove等可以直接看看源码注释上面都写了。

我们的实现类同时有个父类AbstractAccessStrategy<EntityRegionImpl>,很多EntityRegionAccessStrategy因为都是公用的所以在AbstractAccessStrategy中实现了。看代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
publicabstract class AbstractAccessStrategy<T extendsAbstractTransactionalDataRegion> {
    protectedfinal T region;
    protectedfinal Settings settings;
 
    publicAbstractAccessStrategy(T region, Settings settings) {
        this.region = region;
        this.settings = settings;
    }
 
    publicboolean insert(Object key, Object value) throwsCacheException {
        returninsert(key, value, null);
    }
 
    publicboolean insert(Object key, Object value, Object version) throwsCacheException {
        returnfalse;
    }
 
    publicboolean afterInsert(Object key, Object value) throwsCacheException {
        returnafterInsert(key, value, null);
    }
 
    publicboolean afterInsert(Object key, Object value, Object version) throwsCacheException {
        returnfalse;
    }
 
    publicboolean update(Object key, Object value) throwsCacheException {
        returnupdate(key, value, null,null);
    }
 
    publicboolean update(Object key, Object value, Object currentVersion, Object previousVersion) throwsCacheException {
        remove(key);
        returnfalse;
    }
 
    publicboolean afterUpdate(Object key, Object value, SoftLock lock) throwsCacheException {
        returnafterUpdate(key, value, null,null, lock);
    }
 
    publicboolean afterUpdate(Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock)throwsCacheException {
        unlockItem(key, lock);
        returnfalse;
    }
 
    publicObject get(Object key, longtxTimestamp) throwsCacheException {
        returnregion.get(key);
    }
 
    publicboolean putFromLoad(Object key, Object value, longtxTimestamp, Object version) throwsCacheException {
        returnputFromLoad(key, value, txTimestamp, version, settings.isMinimalPutsEnabled());
    }
 
    publicboolean putFromLoad(Object key, Object value, longtxTimestamp, Object version, booleanminimalPutOverride) throwsCacheException {
        if(minimalPutOverride && region.contains(key)) {
            returnfalse;
        }else{
            region.put(key, value);
            returntrue;
        }
    }
 
    publicSoftLock lockItem(Object key, Object version) throwsCacheException {
        returnnull;
    }
 
    publicSoftLock lockRegion() throwsCacheException {
        returnnull;
    }
 
    publicvoid unlockItem(Object key, SoftLock lock) throwsCacheException {
        region.evict(key);
    }
 
    publicvoid unlockRegion(SoftLock lock) throwsCacheException {
        region.evictAll();
    }
 
    publicvoid remove(Object key) throwsCacheException {
        region.evict(key);
    }
 
    publicvoid removeAll() throwsCacheException {
        region.evictAll();
    }
 
    publicvoid evict(Object key) throwsCacheException {
        region.evict(key);
    }
 
    publicvoid evictAll() throwsCacheException {
        region.evictAll();
    }
}

上面的方法有些怪异如insert方法,里面就直接返回false了,问了高手,他说参考ehcache的实现。看了下ehcache的源码它上面写了:A no-op since this is an asynchronous cache access strategy。这个和hibernate接口定义的insert注释有点出入:

?
1
2
3
* Called after an item has been inserted (before the transaction completes),
     * instead of calling evict().
     * This method is used by "synchronous"concurrency strategies.
总之这块不是很理解。

转http://my.oschina.net/yybear/blog/102216

0 0