@cacheable 缓存时间的缓存怎么使用

15:02 提问
spring缓存@Cacheable@CachePut存入的数据是方法执行的返回值吗
比如我要修改一条数据
@CachePut(value = "users",key = "1001")
public void update(User user){
userDao.update(user);
为什么修改后存进去的数据是空的?
@CachePut(value = "users",key = "1001")
public User update(User user){
userDao.update(user);
只能这样才能存进去吗?
按赞数排序
其他相关推荐Spring缓存注解@Cache,@CachePut , @CacheEvict,@CacheConfig使用 - CSDN博客
Spring缓存注解@Cache,@CachePut , @CacheEvict,@CacheConfig使用
@Cacheable、@CachePut、@CacheEvict 注释介绍
表 1. @Cacheable 作用和配置方法
@Cacheable 的作用&主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
@Cacheable 主要的参数
缓存的名称,在 spring 配置文件中定义,必须指定至少一个
@Cacheable(value=”mycache”) 或者&
@Cacheable(value={”cache1”,”cache2”}
缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合
@Cacheable(value=”testcache”,key=”#userName”)
缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存
@Cacheable(value=”testcache”,condition=”#userName.length()&2”)
------------------------------------------------------------
--////////////////////////////////////////////////////////////////////////////////
表 2. @CachePut 作用和配置方法
@CachePut 的作用&主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用
@CachePut 主要的参数
缓存的名称,在 spring 配置文件中定义,必须指定至少一个
@Cacheable(value=”mycache”) 或者&
@Cacheable(value={”cache1”,”cache2”}
缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合
@Cacheable(value=”testcache”,key=”#userName”)
缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存
@Cacheable(value=”testcache”,condition=”#userName.length()&2”)
//////////////////////////////////////////////////////
表 3. @CacheEvict 作用和配置方法
@CachEvict 的作用&主要针对方法配置,能够根据一定的条件对缓存进行清空
@CacheEvict 主要的参数
缓存的名称,在 spring 配置文件中定义,必须指定至少一个
@CachEvict(value=”mycache”) 或者&
@CachEvict(value={”cache1”,”cache2”}
缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合
@CachEvict(value=”testcache”,key=”#userName”)
缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才清空缓存
@CachEvict(value=”testcache”,
condition=”#userName.length()&2”)
allEntries
是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存
@CachEvict(value=”testcache”,allEntries=true)
beforeInvocation
是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存
@CachEvict(value=”testcache”,beforeInvocation=true)
--------------
额外补充:@cache(“something&);这个相当于save()操作,@cachePut相当于Update()操作,只要他标示的方法被调用,那么都会缓存起来,而@cache则是先看下有没已经缓存了,然后再选择是否执行方法。@CacheEvict相当于Delete()操作。用来清除缓存用的。
这写配置的声明需要配置好了@enableCache才有用,具体的配置可以看这篇文章
http://blog.csdn.net/sanjay_f/article/details/
如果忘记了SpEL怎么用了,&do yourself a favor and read&:
-------------
importorg.springframework.stereotype.S
importcom.springcache.annotation.C
@Cacheable
public class MemcachedService{
@Cacheable(name=&remote&,key=&'USER_NAME_'+#args[0]&)
public String storeUserName(String accountId,String name)
@Cacheable(name=&remote&)
public String storeUserAddress(String accountId,String address){
不知道你们注意到一个问题没有,就是所有的@Cacheable()里面都有一个name=“xxx”的属性,这显然如果方法多了,写起来也是挺累的,如果可以一次性声明完 那就省事了,
所以,有了@CacheConfig这个配置,@CacheConfig&is
a class-level annotation that allows to share the cache names,不过不用担心,如果你在你的方法写别的名字,那么依然以方法的名字为准。
@CacheConfig(&books&)
public class BookRepositoryImpl implements BookRepository {
@Cacheable
public Book findBook(ISBN isbn) {...}
当然还有另外一个情况,@Cacheable(name=&remote&,key=&'USER_NAME_'+#args[0]&
,conditional=“xxx”,allEntries=true,beforeInvocation=true)&,像这样的配置就很长,
@Cacheable(name = &book&, key=&#isbn&,conditional=“xxx”,allEntries=true,beforeInvocation=true)&
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
这样的配置很长,而且有可能声明在很多个方法的,所以我们很想精简点,容易配置些。所以
@findBookByIsbnervice
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
新建一个文件findBookByIsbn,
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Cacheable(cacheNames=&books&, key=&#isbn&)
public @interface findBookByIsbn {
-------------------------------
Features summary
For those who are familiar with Spring’s caching annotations, the following table describes the main differences between the Spring annotations and the JSR-107 counterpart:
Table&35.3.&Spring vs. JSR-107 caching annotations
@Cacheable
@CacheResult
Fairly similar.&@CacheResult&can
cache specific exceptions and force the execution of the method regardless of the content of the cache.
While Spring updates the cache with the result of the method invocation, JCache requires to pass it as an argument that is annotated with&@CacheValue.
Due to this difference, JCache allows to update the cache before or after the actual method invocation.
@CacheEvict
@CacheRemove
Fairly similar.&@CacheRemove&supports
a conditional evict in case the method invocation results in an exception.
@CacheEvict(allEntries=true)
@CacheRemoveAll
See&@CacheRemove.
@CacheConfig
@CacheDefaults
Allows to configure the same concepts, in a similar fashion.
--------------
JCache can manage exceptions thrown by annotated methods:
this can prevent an update of the cache but it can also cache the exception as an indicator of the failure instead of calling the method again.&
Let’s assume that&InvalidIsbnNotFoundException&is
thrown if the structure of the ISBN is invalid.
This is a permanent failure,&no book could ever be retrieved with such parameter.&
The following caches the exception so that further calls with the same,
invalid ISBN, throws the cached exception directly instead of invoking the method again.
@CacheResult(cacheName=&books&, exceptionCacheName=&failures&
cachedExceptions = InvalidIsbnNotFoundException.class)
public Book findBook(ISBN isbn)
本文已收录于以下专栏:
相关文章推荐
Spring 3.1
引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如
EHCache 或者OSCache),而是一个对...
1.标准目录结构:
  -main
      –bin 脚本库
      –java java源代码文件
    &#1...
在Spring中通过获取MemCachedClient来实现与memcached服务器进行数据读取的方式。不过,在实际开发中,我们往往是通过Spring的@Cacheable来实现数据的缓存的,所以,...
在软件开发中使用缓存已经有一个非常久的历史了。缓存是一种很好的设计思想,一旦你用了他,你将会发现他确实很有用。Spring3.1版本的核心对缓存做了实现。在Java推出Annotation特性之前,实...
/developerworks/cn/opensource/os-cn-spring-cache/
http://swiftlet.net/a...
@Cacheable、@CachePut、@CacheEvict 注释介绍
表 1. @Cacheable 作用和配置方法
@Cacheable 的作用 主要针对方法配置,能够根据方法的请求参...
转自:http://tom-/blog/2104430
缓存注解有以下三个:
@Cacheable      @CacheEvict     @CachePut...
spring cache相关注解
CacheEvict annotation
CacheConfig
http://blog.csdn.net/sanjay_f/article/details/
@Cacheable、@CachePut、@CacheEvict 注...
介绍 spring 3.1 激动人心的新特性:注释驱动的缓存,本文通过一个简单的例子进行展开,通过对比我们原来的自定义缓存和 spring 的基于注释的 cache 配置方法,展现了 spring c...
他的最新文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)博客分类:
本文所有测试用代码在/wwlleo0730/restjplat 的分支addDB上
目前在使用spring-data-jpa和hibernate4的时候,对于缓存关系不是很清楚,以及二级缓存和查询缓存的各种配置等等,于是就有了这篇初级的jpa+hibernate缓存配置使用的文章。
JPA和hibernate的缓存关系,以及系统demo环境说明
JPA全称是:Java Persistence API
引用JPA itself is just a specification, it cannot perform persistence or anything else by itself.
JPA仅仅只是一个规范,而不是产品;使用JPA本身是不能做到持久化的。
所以,JPA只是一系列定义好的持久化操作的接口,在系统中使用时,需要真正的实现者,在这里,我们使用Hibernate作为实现者。所以,还是用spring-data-jpa+hibernate4+spring3.2来做demo例子说明本文。
JPA规范中定义了很多的缓存类型:一级缓存,二级缓存,对象缓存,数据缓存,等等一系列概念,搞的人糊里糊涂,具体见这里:
不过缓存也必须要有实现,因为使用的是hibernate,所以基本只讨论hibernate提供的缓存实现。
很多其他的JPA实现者,比如toplink(EclipseLink),也许还有其他的各种缓存实现,在此就不说了。
先直接给出所有的demo例子
hibernate实现中只有三种缓存类型:
一级缓存,二级缓存和查询缓存。
在hibernate的实现概念里,他把什么集合缓存之类的统一放到二级缓存里去了。
1. 一级缓存测试:
文件配置:
&bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"&
&property name="dataSource" ref="dataSource" /&
&property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter" /&
&property name="packagesToScan" value="com.restjplat.quickweb" /&
&property name="jpaProperties"&
&prop key="hibernate.show_sql"&${hibernate.show_sql}&/prop&
&prop key="hibernate.format_sql"&true&/prop&
&/property&
可见没有添加任何配置项。
private void firstCacheTest(){
EntityManager em = emf.createEntityManager();
Dict d1 = em.find(Dict.class, 1); //find id为1的对象
Dict d2 = em.find(Dict.class, 1); //find id为1的对象
((d1==d2)+""); //true
EntityManager em1 = emf.createEntityManager();
Dict d3 = em1.find(Dict.class, 1); //find id为1的对象
EntityManager em2 = emf.createEntityManager();
Dict d4 = em2.find(Dict.class, 1); //find id为1的对象
((d3==d4)+""); //false
输出为:因为sql语句打出来太长,所以用*号代替
Hibernate: ***********
20:41:44,819
INFO [main] (DictTest.java:76) - true
Hibernate: ***********
Hibernate: ***********
20:41:44,869
INFO [main] (DictTest.java:84) - false
由此可见:同一个session内部,一级缓存生效,同一个id的对象只有一个。不同session,一级缓存无效。
2. 二级缓存测试:
文件配置:
1:实体类直接打上 javax.persistence.Cacheable 标记。
@Table(name ="dict")
@Cacheable
public class Dict extends IdEntity{}
2:配置文件修改,在 jpaProperties 下添加,用ehcache来实现二级缓存,另外因为加入了二级缓存,我们将hibernate的统计打开来看看到底是不是被缓存了。
&prop key="hibernate.cache.region.factory_class"&org.hibernate.cache.ehcache.EhCacheRegionFactory&/prop&
&prop key="javax.persistence.sharedCache.mode"&ENABLE_SELECTIVE&/prop&
&prop key="hibernate.generate_statistics"&true&/prop&
注1:如果在配置文件中加入了
&prop key="javax.persistence.sharedCache.mode"&ENABLE_SELECTIVE&/prop&,则不需要在实体内配置hibernate的 @cache标记,只要打上JPA的@cacheable标记即可默认开启该实体的2级缓存。
注2:如果不使用javax.persistence.sharedCache.mode配置,直接在实体内打@cache标记也可以。
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class Dict extends IdEntity{}
至于 hibernate的 hibernate.cache.use_second_level_cache这个属性,文档里是这么写的:
引用Can be used to completely disable the second level cache, which is enabled by default for classes which specify a &cache& mapping.
即打上只要有@cache标记,自动开启。
所以有两种方法配置开启二级缓存:
第一种不使用hibernate的@cache标记,直接用@cacheable标记和缓存映射配置项。
第二种用hibernate的@cache标记使用。
另外javax.persistence.sharedCache.mode的其他配置如下:
The javax.persistence.sharedCache.mode property can be set to one of the following values:
ENABLE_SELECTIVE (Default and recommended value): entities are not cached unless explicitly marked as cacheable.
DISABLE_SELECTIVE: entities are cached unless explicitly marked as not cacheable.
NONE: no entity are cached even if marked as cacheable. This option can make sense to disable second-level cache altogether.
ALL: all entities are always cached even if marked as non cacheable.
如果用all的话,连实体上的@cacheable都不用打,直接默认全部开启二级缓存
测试代码:
private void secondCachetest(){
EntityManager em1 = emf.createEntityManager();
Dict d1 = em1.find(Dict.class, 1); //find id为1的对象
(d1.getName());
em1.close();
EntityManager em2 = emf.createEntityManager();
Dict d2 = em2.find(Dict.class, 1); //find id为1的对象
(d2.getName());
em2.close();
Hibernate: **************
===================L2======================
com.restjplat.quickweb.model.Dict : 1
可见二级缓存生效了,只输出了一条sql语句,同时监控中也出现了数据。
另外也可以看看如果是配置成ALL,并且把@cacheable删掉,输出如下:
Hibernate: ************
===================L2======================
com.restjplat.quickweb.model.Children : 0
com.restjplat.quickweb.model.Dict : 1
org.hibernate.cache.spi.UpdateTimestampsCache : 0
org.hibernate.cache.internal.StandardQueryCache : 0
com.restjplat.quickweb.model.Parent : 0
=================query cache=================
并且可以看见,所有的实体类都加入二级缓存中去了
3. 查询缓存测试:
一,二级缓存都是根据对象id来查找,如果需要加载一个List的时候,就需要用到查询缓存。
在Spring-data-jpa实现中,也可以使用查询缓存。
文件配置:
在 jpaProperties 下添加,这里必须明确标出增加查询缓存。
&prop key="hibernate.cache.use_query_cache"&true&/prop&
然后需要在方法内打上@QueryHint来实现查询缓存,我们写几个方法来测试如下:
public interface DictDao extends JpaRepository&Dict, Integer&,JpaSpecificationExecutor&Dict&{
// spring-data-jpa默认继承实现的一些方法,实现类为
// SimpleJpaRepository。
// 该类中的方法不能通过@QueryHint来实现查询缓存。
@QueryHints({ @QueryHint(name = "org.hibernate.cacheable", value ="true") })
List&Dict& findAll();
@Query("from Dict")
@QueryHints({ @QueryHint(name = "org.hibernate.cacheable", value ="true") })
List&Dict& findAllCached();
@Query("select t from Dict t where t.name = ?1")
@QueryHints({ @QueryHint(name = "org.hibernate.cacheable", value ="true") })
Dict findDictByName(String name);
测试方法:
private void QueryCacheTest(){
//无效的spring-data-jpa实现的接口方法
//输出两条sql语句
dao.findAll();
dao.findAll();
System.out.println("================test 1 finish======================");
//自己实现的dao方法可以被查询缓存
//输出一条sql语句
dao.findAllCached();
dao.findAllCached();
System.out.println("================test 2 finish======================");
//自己实现的dao方法可以被查询缓存
//输出一条sql语句
dao.findDictByName("a");
dao.findDictByName("a");
System.out.println("================test 3 finish======================");
输出结果:
Hibernate: **************
Hibernate: **************
================test 1 finish======================
Hibernate: ***********
================test 2 finish======================
Hibernate: ***********
================test 3 finish======================
===================L2======================
com.restjplat.quickweb.model.Dict : 5
org.hibernate.cache.spi.UpdateTimestampsCache : 0
org.hibernate.cache.internal.StandardQueryCache : 2
=================query cache=================
select t from Dict t where t.name = ?1
select generatedAlias0 from Dict as generatedAlias0
很明显,查询缓存生效。但是为什么第一种方法查询缓存无法生效,原因不明,只能后面看看源代码了。
4.集合缓存测试:
根据hibernate文档的写法,这个应该是算在2级缓存里面。
@Table(name ="parent")
@Cacheable
public class Parent extends IdEntity {
private static final long serialVersionUID = 1L;
private List&Children&
public String getName() {
public void setName(String name) {
this.name =
@OneToMany(fetch = FetchType.EAGER,mappedBy = "parent")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public List&Children& getClist() {
public void setClist(List&Children& clist) {
this.clist =
@Table(name ="children")
@Cacheable
public class Children extends IdEntity{
private static final long serialVersionUID = 1L;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
public Parent getParent() {
public void setParent(Parent parent) {
this.parent =
public String getName() {
public void setName(String name) {
this.name =
测试方法:
private void cellectionCacheTest(){
EntityManager em1 = emf.createEntityManager();
Parent p1 = em1.find(Parent.class, 1);
List&Children& c1 = p1.getClist();
em1.close();
System.out.println(p1.getName()+" ");
for (Children children : c1) {
System.out.print(children.getName()+",");
System.out.println();
EntityManager em2 = emf.createEntityManager();
Parent p2 = em2.find(Parent.class, 1);
List&Children& c2 = p2.getClist();
em2.close();
System.out.println(p2.getName()+" ");
for (Children children : c2) {
System.out.print(children.getName()+",");
System.out.println();
Hibernate: ********************
kate,Jam,Jason,Brain,
kate,Jam,Jason,Brain,
===================L2======================
com.restjplat.quickweb.model.Children : 4
com.restjplat.quickweb.model.Dict : 0
org.hibernate.cache.spi.UpdateTimestampsCache : 0
com.restjplat.quickweb.model.Parent.clist : 1
org.hibernate.cache.internal.StandardQueryCache : 0
com.restjplat.quickweb.model.Parent : 1
=================query cache=================
在统计数据里可见二级缓存的对象数量。
本文我们不讨论关于缓存的更新策略,脏数据等等的东西,只是讲解配置方式。
接下来是源代码篇
理清楚各种配置以后,我们来看一下hibernate和spring-data-jpa的一些缓存实现源代码。
上面有个遗留问题,为什么spring-data-jpa默认实现的findAll()方法无法保存到查询缓存?只能啃源代码了。
打断点跟踪吧
入口方法是spring-data-jpa里的 SimpleJpaRepository类
public List&T& findAll() {
return getQuery(null, (Sort) null).getResultList();
然后到 QueryImpl&X&类的
private List&X& list() {
if (getEntityGraphQueryHint() != null) {
SessionImplementor sessionImpl = (SessionImplementor) getEntityManager().getSession();
HQLQueryPlan entityGraphQueryPlan = new HQLQueryPlan( getHibernateQuery().getQueryString(), false,
sessionImpl.getEnabledFilters(), sessionImpl.getFactory(), getEntityGraphQueryHint() );
// Safe to assume QueryImpl at this point.
unwrap( org.hibernate.internal.QueryImpl.class ).setQueryPlan( entityGraphQueryPlan );
return query.list();
进入query.list();
query类的代码解析google一下很多,于是直接到最后:
进入QueryLoader的list方法。
protected List list(
final SessionImplementor session,
final QueryParameters queryParameters,
final Set&Serializable& querySpaces,
final Type[] resultTypes) throws HibernateException {
final boolean cacheable = factory.getSettings().isQueryCacheEnabled() &&
queryParameters.isCacheable();
if ( cacheable ) {
return listUsingQueryCache( session, queryParameters, querySpaces, resultTypes );
return listIgnoreQueryCache( session, queryParameters );
果然有个cacheable,值为false,说明的确是没有从缓存里取数据。
用自定义的jpa查询方法测试后发现,这个值为true。
于是接着看cacheable的取值过程:
final boolean cacheable = factory.getSettings().isQueryCacheEnabled() &&
queryParameters.isCacheable();
factory.getSettings().isQueryCacheEnabled() 这个一定是true,因为是在配置文件中打开的。那只能是queryParameters.isCacheable() 这个的问题了。
在query.list()的方法内部:
public List list() throws HibernateException {
verifyParameters();
Map namedParams = getNamedParams();
return getSession().list(
expandParameterLists(namedParams),
getQueryParameters(namedParams)
getQueryParameters(namedParams)这个方法实际获取的是query对象的cacheable属性的值,也就是说,query对象新建的时候cacheable的值决定了这个query方法能不能被查询缓存。
接下来query的建立过程:
在 SimpleJpaRepository 类中 return applyLockMode(em.createQuery(query));
直接由emcreate,再跟踪到 AbstractEntityManagerImpl中
public &T& QueryImpl&T& createQuery(
String jpaqlString,
Class&T& resultClass,
Selection selection,
QueryOptions queryOptions) {
org.hibernate.Query hqlQuery = internalGetSession().createQuery( jpaqlString );
return new QueryImpl&T&( hqlQuery, this, queryOptions.getNamedParameterExplicitTypes() );
catch ( RuntimeException e ) {
throw convert( e );
即通过session.createQuery(jpaqlString ) 创建初始化对象。
在query类定义中
public abstract class AbstractQueryImpl implements Query {
cacheable不是对象类型,而是基本类型,所以不赋值的情况下默认为“false”。
也就是说spring-data-jpa接口提供的简单快速的各种接口实现全是不能使用查询缓存的,完全不知道为什么这么设计。
接下来看看我们自己实现的查询方法实现:
直接找到query方法的setCacheable()方法打断点,因为肯定改变这个值才能有查询缓存。
于是跟踪到 SimpleJpaQuery类中
protected Query createQuery(Object[] values) {
return applyLockMode(applyHints(doCreateQuery(values), method), method);
在返回query的过程中通过applyHints()方法读取了方法上的QueryHint注解从而设置了查询缓存。
浏览 17658
你们好。 谢谢分享你的文章。 请问你的说的测试代码 在哪里?/wwlleo0730/restjplat&& 这个上面找不到你说的addDB分支这个缓存方面的问题也弄的我头痛。 希望你能给个例子 学习学习! 3Q/wwlleo0730/restjplat/tree/addDB/
leobluewing
浏览: 144671 次
来自: 宁波
想要查询这样channel,其子栏目 channels 数列里 ...
LZ的头像是国見比呂顺便问一下 html2canvas是异步请 ...
sping3.1.2下测试,rest异常捕获不到,没有返回js ...
你好,我想问一下,为什么像xml等配置文件也会有这个东西,它会 ...
小丑皇吃巧克力 写道请问 图片黑色遮罩是怎么回事 啊?应该是h ...
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'}

我要回帖

更多关于 spring cacheable 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信