spring data jpa + hibernate + redis二级缓存

目的

在spring data jpa + hibernate项目中,使用redis做二级缓存, 在使用过程中,存在很多的细节上的问题,在此做个记录

支持的缓存类型

  • 单个Domain按id缓存
  • 对Domain中的集合类型的缓存
  • 针对一个查询,根据查询和查询参数做缓存
  • Transaction类型,不是太懂

配置

使用hibernate-redis作为hibernate和redis之间的桥梁。

由于hibernate存在接口频繁变动的问题,所以hibernate-redis只针对hibernate的特定版本有效, 根据此项目中的pom文件,可确定hibernate在以下三个版本中可用

  • 4.3.11.Final
  • 5.1.4.Final
  • 5.2.8.Final

具体代码中的配置,可参考hibernate-redisREADME文件,其中有几个注意点

SingletonRedisRegionFactory的使用

SingletonRedisRegionFactory与hibernate版本对应关系如下

1
2
3
4.3.11.Final	: org.hibernate.cache.redis.hibernate4.SingletonRedisRegionFactory
5.1.4.Final : org.hibernate.cache.redis.hibernate5.SingletonRedisRegionFactory
5.2.8.Final : org.hibernate.cache.redis.hibernate52.SingletonRedisRegionFactory

redis序列化的选择与配置

在redisson.yaml文件中,存在如下配置

1
codec: !<org.redisson.codec.SnappyCodec> {}

其中<>之间为redisson所使用的序列化方案,虽然redisson支持很多种方案,但不是每一种都可以用在hibernate二级缓存中,测试过程中若存在类型转换错误,请尝试更换序列化方案
具体可用的序列化方案见org.redisson.codec包下的文件

@org.hibernate.annotations.Cache的使用

简单用法就是在每一个Domain上加上此注解

1
@Cache(region="common", usage = CacheConcurrencyStrategy.READ_WRITE)  // or @Cacheable(true) for JPA

其中
region可以不配,则默认为类全名(class.getName)
usage为读还是写等相关控制,其中涉及到是否加锁,对性能有影响,所以根据实际情况确定其取值

另外,Cache注解可使用在属性上,主要用作级联对象的缓存, 例如:

1
2
3
4
5
6
7
8
9
10
@Entity
class ClassDomain {
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
List<StudentDomain> students;
}

@Entity
class StudentDomain {
...
}

若上例中,List<StudentDomain> students中不添加Cache注解,则不会自动做二级缓存

Domain中需要注意的问题

因为redisson需要做序列化,所以对于Domain及其所有的UserType属性,都需要实现Serializable接口,例如:

1
2
3
4
5
6
7
8
9
10
11
12
@Entity
@TypeDefs(value = {
@TypeDef(name = "RuleItems", typeClass = JsonUserType.class, parameters = {
@org.hibernate.annotations.Parameter(name = "targetClass", value = "com.simpletour.commons.refund.RefundRule"),
@org.hibernate.annotations.Parameter(name = "jsonModel", value = "JSON_ARRAY")
})
}
})
class Order implement Serializable{
@Type(type = "RuleItems")
List<RefundRule> refundRules;
}

则,RefuldRule需要实现Serializable接口

使用spring data jpa接口

针对查询,需要设置org.hibernate.cacheabletrue, 具体的

SimpleJpaRepository继承并覆盖

针对org.springframework.data.jpa.repository.support.SimpleJpaRepository的查询接口,子类需覆盖下面的方法

1
2
3
4
5
6
protected Map<String, Object> getQueryHints() {
Map<String, Object> hints = super.getQueryHints();
Map<String, Object> res = new HashMap<>(hints);
res.put("org.hibernate.cacheable", true);
return res;
}

findByXXX类接口

针对findByXXX且不需要实现的接口,需要设置QueryHints

1
2
   @QueryHints({@QueryHint(name = "org.hibernate.cacheable", value = "true")})
PriceEntry findEntryByUionKey(UnionKey unionKey);

结果

简单的确定一个对象是否被缓存的办法就是看第一次查询和第二次查询的日志,当开启hibernate 的打印sql日志时第一次查询有sql语句而第二次无

另外也可以通过hibernate提供的缓存命中相关统计查看结果,具体的google之

文章目录
  1. 1. 目的
  2. 2. 支持的缓存类型
  3. 3. 配置
    1. 3.1. SingletonRedisRegionFactory的使用
    2. 3.2. redis序列化的选择与配置
    3. 3.3. @org.hibernate.annotations.Cache的使用
    4. 3.4. Domain中需要注意的问题
    5. 3.5. 使用spring data jpa接口
      1. 3.5.1. SimpleJpaRepository继承并覆盖
      2. 3.5.2. findByXXX类接口
  4. 4. 结果
,