【Ehcache技术专题】「入门到精通」带你一起从零基础进行分析和开发Ehcache框架的实战指南(5-检索开发)

系列文章目录

本系列课程主要针对于Ehcache缓存框架功能的开发实践全流程技术指南!

  • 第一节:Ehcache缓存框架的基本概念和简介
  • 第二节:Ehcache缓存框架的配置分析和说明
  • 第三节:Ehcache缓存框架的缓存方式的探索
  • 第四节:Ehcache缓存框架的配置分析和说明
  • 第五节:Ehcache缓存框架的查询分析和说明
  • 第六节:Ehcache缓存框架的监听器功能扩展
  • 第七节:Ehcache缓存框架的并发功能的开发
  • 第八节:Ehcache缓存框架的同步阻塞的开发
  • 第九节:Ehcache缓存框架的页面缓存的开发
  • 第十节:Ehcache缓存框架之结合Spring整合

带你一起从零基础进行分析和开发Ehcache框架的实战指南

  • 系列文章目录
  • 缓存查询
    • Cache可查询特性机制
      • 基于Xml配置
        • xml文件的配置
      • config代码配置
        • Java代码
    • 指定可搜索的属性机制
      • Xml代码配置
    • 可查询属性类型
      • 可查询/可索引能力定义
        • 关闭对应查询机制
        • Xml代码配置
        • 属性的提取
          • 定义自己的AttributeExtractor
          • Java代码
          • Xml代码
          • Xml代码
        • JavaBeanAttributeExtractor
          • Xml代码
        • ReflectionAttributeExtractor
          • 配置案例
            • Xml代码
            • Xml代码
            • Xml代码
        • DynamicAttributesExtractor
          • 实现原理
          • xml代码
          • DynamicAttributesExtractor实际案例
            • Java代码
      • 通过程序指定可查询属性
        • Java代码
        • Java代码
      • 查询
        • 创建查询与筛选条件
          • Java代码
        • Criteria接口
          • 获取查询属性
            • Java代码
          • 筛选类型
            • Java代码
            • Criteria的案例
      • 查询内容
        • Java代码
    • execute方法
    • Results & Result
      • 结果处理
        • Java代码
        • List集合操作
    • 统计
      • 计算维度
      • Java代码
    • 排序
      • Java代码
    • 分组
      • Java代码
    • 让Query不可变
    • 对BeanShell的支持
      • Java代码
    • 总结

缓存查询

Ehcache中为我们提供了可以对Cache中缓存的元素进行查找的方式。其逻辑类似于SQL中的查找。通过给定各种限制条件,我们可以构造各种复杂的查询,然后返回结果集,也可以对查询进行分组和排序等。

Cache可查询特性机制

Ehcache中的查询是针对于Cache而言的。但并不是所有的Cache都可以进行查询操作,我们需要指定其为一个可查询的Cache之后才可以对该Cache进行查询操作。因为在配置Cache的时候有基于xml文件的配置和基于程序代码的配置,所以对应的使一个Cache可查询也有两种方式。

基于Xml配置

当我们的Cache定义是基于Xml文件的配置时,我们只需在对应Cache定义下声明一个子元素searchable即可使当前Cache拥有可查询的功能。

xml文件的配置
<cache name="searchableCache" maxBytesLocalHeap="100M">  <searchable/>  
</cache>  

config代码配置

基于代码的配置是通过新建Searchable对象,然后指定需要设置为可查询Cache对应的CacheConfiguration的Searchable对象为我们新建的Searchable对象即可。

Java代码
   public void test() {  CacheManager cacheManager = CacheManager.create();  CacheConfiguration cacheConfig = new CacheConfiguration();  cacheConfig.name("cache1").maxBytesLocalHeap(100, MemoryUnit.MEGABYTES);  Searchable searchable = new Searchable();  //指定Cache的Searchable对象。  cacheConfig.searchable(searchable);  //如下指定也行  
//    cacheConfig.addSearchable(searchable);  Cache cache1 = new Cache(cacheConfig);  cacheManager.addCache(cache1);  }  

指定可搜索的属性机制

配置了Cache可查询后,我们还需要配置当前Cache可以对哪些属性进行查询,即可以把哪些属性作为条件来对Cache进行查询。在Ehcache中使用一个net.sf.ehcache.search.Attribute来表示一个可查询的属性。这些可查询的属性可以是我们的key、value或者它们对应的属性。定义可查询属性是通过searchable元素的子元素searchAttribute来定义的,如:

Xml代码配置

<cache name="userCache" maxBytesLocalHeap="50M">  <searchable>  <searchAttribute name="name"/>  </searchable>  
</cache>  

其中name表示我们所定义的可查询属性的名称,是必须指定的属性。这里会通过属性提取机制提取key或者value中name所对应的属性,这里是name属性,来进行索引。关于属性提取机制将在后续讲解。

可查询属性类型

一下属性都可以用来作为Cache的可查询属性,它必须是以下类型之一:
在这里插入图片描述

数据类型描述
Boolean代表布尔类型的对象。值可以是truefalse
Byte代表一个8位的有符号整数。它的值可以在-128到127之间。
Short代表一个16位的有符号整数。它的值可以在-32768到32767之间。
Character代表一个单个的16位Unicode字符。
Integer代表一个32位的有符号整数。它的值可以在-2147483648到2147483647之间。
Long代表一个64位的有符号整数。它的值可以在-9223372036854775808到9223372036854775807之间。
Float代表一个单精度32位的IEEE 754浮点数。
Double代表一个双精度64位的IEEE 754浮点数。
String代表一串字符。它是Java里面最常用的数据类型。
java.util.Date此类表示特定的瞬间,精确到毫秒。它主要用于表示日期和时间。
java.sql.Date此类继承自java.util.Date,主要用于JDBC中,以与SQL DATE类型交互。
Enum代表了Java中的枚举类型,用于声明一组预定义的常量。

可查询/可索引能力定义

默认情况下,系统会自动把我们存入可查询Cache中元素的key和value作为可查询属性,命名为key和value,当它们是以上可查询类型时我们可以直接对它们进行查询。

关闭对应查询机制

如果不需要默认将我们的key和value作为可查询属性的话,我们可以在指定Cache为一个可查询Cache时指定searchable元素的keys属性和values属性为false即可。如:

Xml代码配置
<cache name="searchableCache" maxBytesLocalHeap="100M">  
.   <searchable keys="false" values="false"/>  
</cache>  
属性的提取

当我们的key或者value不是可查询类型,然而我们又希望对它们进行查询时,我们就需要把key或者value中的属性提取出来作为Cache的一个可查询属性。这是通过AttributeExtractor来进行的,AttributeExtractor是一个接口,其中只定义了一个方法Object attributeFor(Element element, String attributeName)。其返回值必须是可查询属性类型之一。当然,返回null也是可以的。下面我们来看看如何定义自己的AttributeExtractor。

定义自己的AttributeExtractor

假设我们有一个名叫userCache的缓存,其中存放的元素值都是一个User对象。而我们的User对象有一个String类型的name属性。假设我们现在指定了我们的userCache的一个可查询属性为user,而其真正对应的内容是我们的Element中存放的value的name。那么这个时候我们的AttributeExtractor实现大概会是这个样子:

Java代码
public class UserAttributeExtractor implements AttributeExtractor {     @Override  public Object attributeFor(Element element, String attributeName)  throws AttributeExtractorException {  User user = (User) element.getObjectValue();  return user.getName();  }    
}

定义好了AttributeExtractor之后,我们要告诉Ehcache,缓存userCache的可查询属性user对应的AttributeExtractor是我们定义的UserAttributeExtractor,这只需要指定searchAttribute元素的class属性即可。

Xml代码
<cache name="userCache" maxBytesLocalHeap="50M">  <searchable>  <searchAttribute name="user" class="com.xxx.UserAttributeExtractor"/>  </searchable>  
</cache>  

之后我们通过user属性来查询时就可以通过User对象的name属性来过滤一些结果集了。如果我们的AttributeExtractor还需要接收其它的参数的话,我们可以通过searchAttribute元素的properties属性来指定,其对应的参数是键值对的形式,中间用等号“=”隔开,多个参数之间用逗号隔开。如:

Xml代码
<cache name="userCache" maxBytesLocalHeap="50M">  <searchable>  <searchAttribute name="user" class="com.xxx.UserAttributeExtractor" properties="a=1,b=2"/>  </searchable>  
</cache>  

指定了properties属性后,我们对应的AttributeExtractor必须给定一个以Properties对象为参数的构造方法才可以接收到这些指定的参数。

除了定义自己的属性提取实现类之外,Ehcache还为我们提供了一些实现类。包括KeyObjectAttributeExtractor、ValueObjectAttributeExtractor,这两个属性提取器就是默认情况下Ehcache用来把key和value提取为一个可查询属性的方式。此外还有JavaBeanAttributeExtractor和ReflectionAttributeExtractor。

JavaBeanAttributeExtractor

当定义一个可查询属性searchAttribute只指定其name属性,系统所使用的AttributeExtractor就是JavaBeanAttributeExtractor。该AttributeExtractor会从元素的key或者value中取searchAttribute的name属性值所对应的属性。

如果我们有如下这样一个可查询缓存的定义,Ehcache在给可查询属性address建立索引时就会获取元素key的address属性或者value的address属性来作为查询属性address的值。

Xml代码
<cache name="searchableCache" maxBytesLocalHeap="100M">  <searchable keys="false" values="false">  <searchAttribute name="address"/>  </searchable>  
</cache>  

注意:使用JavaBeanAttributeExtractor时,如果key和value中都包含可查询属性,则系统会抛出异常,如果都不包含的话也会抛出异常

ReflectionAttributeExtractor

当我们定义一个可查询属性searchAttribute时指定了expression属性时,系统就会使用ReflectionAttributeExtractor来提取属性的值。此属性提取器是通过反射来提取属性值的。expression必须以key、value或element开始,然后中间以点“.”来连接它们所对应的属性或方法,以及属性的属性,方法的方法。key表示元素的key,value表示元素的value,element表示元素本身。

配置案例
  1. 查询属性address的值是对应的value的address属性。
Xml代码
<cache name="searchableCache" maxBytesLocalHeap="100M">  <searchable keys="false" values="false">  <searchAttribute name="address" expression="value.address"/>  </searchable>  
</cache> 
  1. 查询属性address的值是对应的value的extraInfo属性的getAddress()方法的返回值。
Xml代码
<cache name="searchableCache" maxBytesLocalHeap="100M">  <searchable keys="false" values="false">  <searchAttribute name="address" expression="value.extraInfo.getAddress()"/>  </searchable>  
</cache> 

3.查询属性hitCount的值是对应的element的getHitCount()方法的返回值。

Xml代码
<cache name="searchableCache" maxBytesLocalHeap="100M">  <searchable keys="false" values="false">  <searchAttribute name="hitCount" expression="element.getHitCount()"/>  </searchable>  
</cache>  
DynamicAttributesExtractor

上面介绍的AttributeExtractor都是在Cache实例化之前定义的,其会在Cache实例化时初始化这些可查询属性。而DynamicAttributesExtractor允许我们在Cache实例化后添加可查询属性。

实现原理

DynamicAttributesExtractor是一个接口,它跟AttributeExtractor接口没有任何关系。该接口中仅定义了一个方法attributesFor(),该方法将接收一个Element对象作为参数,然后返回一个将作为可查询属性的Map,该Map的key对应可查询属性的名称,而value则对应可查询属性的值。那么我们在实现DynamicAttributesExtractor接口时只需要实现attributesFor()方法即可。

使用DynamicAttributeExtractor时我们的Cache对应的Searchable必须是支持该提取器才行,这是通过Searchable对象的allowDynamicIndexing属性来指定的,使用xml配置时该属性是直接配置在searchable元素上的,而使用程序来定义时则需要通过Searchable对象来指定了。

之后我们需要把它注册给我们的Cache。通过Cache的registerDynamicAttributesExtractor()方法我们就可以给Cache注册一个动态的属性提取器了,该提取器将在往Cache中put或者replace元素时被调用。

xml代码
<cache name="userCache" maxBytesLocalHeap="50M">  <searchable allowDynamicIndexing="true">  <searchAttribute name="name" expression="value.getName()"/>  </searchable>  
</cache>  

上面定义了如下这样一个专门用来缓存User的Cache,其中User中含有属性name。我们在定义该Cache的时候即指定了其是一个可查询的Cache,同时通过指定allowDynamicIndexing为true使其支持动态属性提取,我们还给该Cache指定了一个可查询属性name。

DynamicAttributesExtractor实际案例

我们在userCache初始化后给其注册了一个DynamicAttributesExtractor,在DynamicAttributesExtractor实现类中我们实现了attributesFor方法,在该方法体内我们构造了一个Map,并往其中放入了一个key为hitCount的元素。

Java代码
@Test  
public void dynamicExtractor() {  CacheManager cacheManager = CacheManager.create();  Cache userCache = cacheManager.getCache("userCache");  userCache.registerDynamicAttributesExtractor(new DynamicAttributesExtractor() {  @Override  public Map<String, Object> attributesFor(Element element) {  Map<String, Object> attrMap = new HashMap<String, Object>();  attrMap.put("hitCount", element.getHitCount());  return attrMap;  }         });  this.listSearchableAttrs(userCache); //key、value和name  userCache.put(new Element("1", new User()));  this.listSearchableAttrs(userCache); //key、value、name和hitCount  
}  /** * 输出当前Ehcache中可查询的属性 * @param cache */  
private void listSearchableAttrs(Ehcache cache) {  Set<Attribute> attrSet = cache.getSearchAttributes();  for (Attribute attr : attrSet) {  System.out.println(attr.getAttributeName());  }  
}  

当我们往userCache中put或者replace元素的时候,就会触发我们注册的DynamicAttributesExtractor的attributesFor方法,然后Ehcache会对返回的动态可查询属性hitCount进行索引。在下面的代码中,我们的在给userCache注册了DynamicAttributesExtractor之后,马上列出其中包含的可查询属性,这个时候肯定只会包含预定义好的key、value和name,因为我们注册的DynamicAttributesExtractor还没有被执行。之后往其中放入元素之后,userCache中包含的可查询属性才会有通过DynamicAttributesExtractor返回的hitCount。

一个Cache只能注册有一个DynamicAttributesExtractor,当同时注册多个时,后者会将前者覆盖。但是DynamicAttributesExtractor和其它AttributeExtractor是可以并存的,所以因为其它AttributeExtractor是在Cache初始化前定义的,所以DynamicAttributesExtractor不能返回已经通过AttributeExtractor提取过的同名属性。

通过程序指定可查询属性

通过前面的内容我们知道设置可查询属性时除了DynamicAttributesExtractor可以在Cache初始化后再添加可查询属性外,我们的可查询属性必须是在Cache初始化之前进行指定,否则在对Cache进行查询时我们就不能使用该查询属性进行查询。如下面这一段代码,我们在Cache初始化后通过获取其配置信息,再往其对应的Searchalbe对象中新增一个名叫hello的查询属性,那么我们在今后对该Cache进行查询时将不能使用hello属性进行查询。

Java代码
@Test  
public void setSearchAttrInProgram() {  CacheManager cacheManager = CacheManager.create();  Cache cache = cacheManager.getCache("searchableCache");  CacheConfiguration cacheConfig = cache.getCacheConfiguration();  Searchable searchable = cacheConfig.getSearchable();  SearchAttribute searchAttribute = new SearchAttribute();  searchAttribute.name("hello");  searchable.addSearchAttribute(searchAttribute);  this.listSearchableAttrs(cache);  
}  

由于定义非动态查询属性时需要在Cache初始化时定义,所以当我们需要在程序中定义查询属性时对应的Cache也需要是在程序中声明的才行。下面是在程序中指定可查询属性的一个示例。

Java代码
@Test  
public void setSearchAttrInProgram() {  CacheManager cacheManager = CacheManager.create();  CacheConfiguration cacheConfig = new CacheConfiguration();  cacheConfig.name("cacheName").maxBytesLocalHeap(100, MemoryUnit.MEGABYTES);  //新建一个Searchable对象  Searchable searchable = new Searchable();  //给Cache配置Searchable对象,表明该Cache是一个可查询的Cache  cacheConfig.searchable(searchable);  //新建一个查询属性  SearchAttribute searchAttribute = new SearchAttribute();  //指定查询属性的名称和属性提取器的类名  searchAttribute.name("查询属性名称");  //searchAttribute.className("属性提取器的类名");  //Searchalbe对象添加查询属性  searchable.addSearchAttribute(searchAttribute);  //使用CacheConfig创建Cache对象  Cache cache = new Cache(cacheConfig);  //把Cache对象纳入CacheManager的管理中  cacheManager.addCache(cache);  this.listSearchableAttrs(cache);  
}  

查询

在Ehcache中是通过一个net.sf.ehcache.search.Query对象来表示一个查询的,通过该对象我们可以对缓存中的元素进行查询,查询条件就是我们之前定义好的可查询属性,而查询结果可以是缓存的key、value或可查询属性,也可以是针对于可查询属性的一些统计结果。

创建查询与筛选条件

在对Cache进行查询前我们需要先创建一个Query对象。Query对象是通过EhCache接口定义的createQuery()方法创建的,Cache类对它进行了实现。有了Query对象之后,我们需要使用Query对象的addCriteria(Criteria criteria)方法给该Query对象添加一些限制条件来对其中缓存的元素进行筛选,否则返回的结果将是针对于所有的缓存元素的。

Java代码
@Test  
public void search () {  CacheManager cacheManager = CacheManager.create();  Cache userCache = cacheManager.getCache("userCache");  User user;  for (int i=0; i<10; i++) {  user = new User(i, "name"+(i%2), 30+i);  userCache.put(new Element(user.getId(), user));  }  Query query = userCache.createQuery();  
}  
Criteria接口

Criteria是一个接口,在net.sf.ehcache.search.expression定义了其一系列的实现类,我们也可以直接通过new一个Criteria实现类的实例来对Query结果进行筛选。但通常我们不需要这样做,因为Ehcache中已经为我们实现了的Criteria通常已经可以满足我们的需求了。Ehcache中代表查询属性的Attribute类已经为我们提供了获取针对于该属性的各种Criteria的方法。好,现在我们已经知道了可以通过查询属性直接获取到针对于该属性的限制Criteria对象,那么我们该如何获取查询属性呢?

获取查询属性

获取查询属性Attribute主要有两种方式,

  1. 直接new一个Attribute实例对象;
  2. 通过Ehcache接口定义的getSearchAttribute(String attrName)获取到可查询缓存中对应属性名称的可查询属性对象Attribute。

常用的还是通过getSearchAttribute(String attrName)方法来获取对应的查询属性Attribute。
当调用可查询Cache的getSearchAttribute(String attrName)方法来获取当前缓存的可查询属性时,如果对应名称的可查询属性不存在,则会抛出异常。

Java代码
CacheManager cacheManager = CacheManager.create();  
Cache cache = cacheManager.getCache("userCache");  
Attribute<String> name = cache.getSearchAttribute("name"); 

Attribute类使用了泛型定义,其表示当前属性值的类型。

筛选类型

有了可查询属性Attribute之后,我们就可以通过Attribute类定义的一系列方法获取到当前Attribute的某种限制,从而对Query的查询结果进行筛选。如我们要筛选name为“name1”的查询结果时我们可以通过name.eq(“name1”)来进行筛选。

Java代码
public void search2() {  CacheManager cacheManager = CacheManager.create();  Cache userCache = cacheManager.getCache("userCache");  User user;  for (int i=0; i<10; i++) {  user = new User(i, "name"+(i%2), 30+i);  userCache.put(new Element(user.getId(), user));  }  //获取名称为name的可查询属性Attribute对象  Attribute<String> name = userCache.getSearchAttribute("name");  //创建一个用于查询的Query对象  Query query = userCache.createQuery();  //给当前query添加一个筛选条件——可查询属性name的值等于“name1”  query.addCriteria(name.eq("name1"));  
}  

接下来我们来看一下Attribute类为我们提供的获取对应Criteria的方法有哪些。

Attribute方法对应Criteria实现类描述
betweenBetween属性值在给定的范围之间
inInCollection在给定的集合之中
neNotEqualTo不等于给定的值
eqEqualTo等于给定的值
ltLessThan小于给定的值
leLessThanOrEqual小于或等于给定的值
gtGreaterThan大于给定的值
geGreaterThanOrEqual大于或等于给定的值
ilikeILike匹配给定的表达式,表达式中可以使用“*”来代表任意多个字符,使用“?”来代表任意一个字符
notIlikeNotILike不匹配给定的表达式
isNullIsNull等于null
notNullNotNull不等于null

那当我们要实现与或非的逻辑时怎么办呢?Criteria为我们提供了对应的方法,分别对应and(Criteria criteria)方法、or(Criteria criteria)方法和not()方法,然后这三个方法的返回结果还是一个Criteria,它们对应的Criteria实现类分别为And、Or和Not。当我们使用Query的addCriteria(Criteria criteria)方法来添加一个筛选条件时默认都是对应的and操作。

Criteria的案例

下面我们来看一些使用Criteria的例子。先假设我们有如下定义的一个Cache,其中存放的元素的value都是一个User对象,下面将给出一些针对于该Cache使用Criteria进行筛选查询的一些示例。

<cache name="userCache" maxBytesLocalHeap="50M">  <searchable>  <searchAttribute name="name" expression="value.getName()"/>  <searchAttribute name="age"/>  <searchAttribute name="unitNo" expression="value.unit.unitNo"/>  <searchAttribute name="unitName" expression="value.unit.getUnitName()"/>  <searchAttribute name="mobile" expression="value.getMobile()"/>  <searchAttribute name="hitCount" expression="element.getHitCount()"/>  </searchable>  
</cache>  

1、年龄在25岁到35岁之间且属于单位002的。

Attribute<Integer> age = userCache.getSearchAttribute("age");  
Attribute<String> unitNo = userCache.getSearchAttribute("unitNo");  
query.addCriteria(age.between(25, 35).and(unitNo.eq("002")));  
//或者使用两次addCriteria  
// query.addCriteria(age.between(25, 35)).addCriteria(unitNo.eq("002"));

2、属于单位002或者单位003,手机号码以137开始且年龄大于35岁的。

Attribute<Integer> age = userCache.getSearchAttribute("age");  
Attribute<String> unitNo = userCache.getSearchAttribute("unitNo");  
Attribute<String> mobile = userCache.getSearchAttribute("mobile");  
query.addCriteria(age.gt(35).and(unitNo.eq("002").or(unitNo.eq("003"))).and(mobile.ilike("137*")));  

3、不属于单位002且年龄小于30的。

Attribute<Integer> age = userCache.getSearchAttribute("age");  
Attribute<String> unitNo = userCache.getSearchAttribute("unitNo");  
query.addCriteria(unitNo.ne("002").and(age.lt(30)));  
//或者使用not()方法  
query.addCriteria(unitNo.eq("002").not().and(age.lt(30)));  

查询内容

一个Query在查询之前,我们必须告诉它需要查询什么内容,也就是说查询的结果中会包含哪些信息。如果在执行查询操作之前没有告诉Query我们要查询什么内容,Ehcache将抛出异常。可以查询的内容包括缓存中存入元素的key、value,可查询属性对应的值,以及针对于当前查询结果中某个可查询属性的统计信息。针对于这四种可以查询内容Query中提供了四个include方法来表示当前Query的查询结果中会包含对应的内容。下面用一个表格来做个展示。

Query方法描述
includeKeys()查询结果中包含所存元素的key
includeValues()查询结果中包含所存元素的value
includeAttribute(Attribute<?>… attributes)查询结果中要包含的可查询属性
includeAggregator(Aggregator… aggregators)查询结果中所要包含的统计信息,关于Aggregator将在后文介绍统计的时候进行讲解

如下的代码表示我们的查询结果中会包含元素的key、可查询属性name和age对应的值。

Java代码
Attribute<String> name = userCache.getSearchAttribute("name");  
Attribute<Integer> age = userCache.getSearchAttribute("age");  
query.includeAttribute(name, age);  

在实际应用中,为了让我们的程序具有更好的性能,我们的查询结果最好只包含我们需要的信息。如只需要获取某个属性的值就不必返回整个value。

execute方法

有了Query之后我们就可以来执行对应的查询操作,获取返回的查询结果。通过调用Query的execute()方法就可以对当前Query执行查询操作,并获取其返回的结果。Ehcache中使用一个Results接口来代表一个Query的查询结果,使用Result接口来代表对应的一条记录。

Results & Result

  • Results中定义了一个方法all()用于返回查询出来的所有Result组成的List,查询的缓存中有多少元素满足查询条件,查询结果Results中就会包含多少个Result对象。

  • Result中定义有getKey()、getValue()、getAttribute()和getAggregatorResults()方法用于获取查询结果中对应元素的key、value、可查询属性对应的值,以及针对于当前查询的统计信息组成的List。

Results和Result这两个接口Ehcache中都已经存在对应的实现了,我们在使用时只要直接利用接口来进行操作就可以了。

结果处理

如果查询结果中不包含对应的信息,那么在Result调用对应方法获取信息时将抛出异常。Results针对于查询结果中是否包含这四方面的信息给我们提供了四个has方法:hasKeys()、hasValues()、hasAttributes()和hasAggregators()。

Java代码
//执行查询操作,返回查询结果Results  
Results results = query.execute();  
//获取Results中包含的所有的Result对象  
List<Result> resultList = results.all();  
if (resultList != null && !resultList.isEmpty()) {  for (Result result : resultList) {  //结果中包含key时可以获取key  if (results.hasKeys()) {  result.getKey();  }  //结果中包含value时可以获取value  if (results.hasValues()) {  result.getValue();  }  //结果中包含属性时可以获取某个属性的值  if (results.hasAttributes()) {  Attribute<String> attribute = userCache.getSearchAttribute("name");  result.getAttribute(attribute);  }  //结果中包含统计信息时可以获取统计信息组成的List  if (results.hasAggregators()) {  result.getAggregatorResults();  }  }  
}  

当然,如果你已经清楚的知道了查询结果中已经包含了key时你在获取key前就可以不用调用Results的hasKeys()方法进行判断了,其它结果也一样。

List集合操作

Results中的all()方法可以返回当前查询的结果中的所有Result组成的List。另外,Results中还提供了一个range(int start, int count)方法用于获取当前结果集的一个子集,其底层默认实现使用的是List的subList()方法。该方法可以用于对查询结果的分页操作。

默认情况下,我们在对Cache进行查询时,查询结果将返回所有满足查询条件的记录。当返回的记录非常多时,系统可能会因为内存不足而报错。Query中定义了一个maxResults(int maxResults)方法用于限制当前查询返回查询结果的最大记录数。

注意:由于元素过期的问题,我们查询结果中的元素不一定还存在。当我们利用完Results之后,我们需要通过调用Results的discard()方法来释放资源

统计

Ehcache为我们提供了一个Aggregator接口用于在查询过程中对某个查询属性进行统计。我们可以实现自己的Aggregator,也可以使用Ehcache为我们提供的实现类。Ehcache中已经为我们提供了五个Aggregator实现类,分别是Min、Max、Sum、Count和Average。看了名称我应该就知道这五个Aggregator分别是做什么用的。

计算维度

Min是求最小值、Max是求最大值、Sum是求和、Count是计数、Average是求平均值。那么在使用这五个Aggregator时也是非常方便的,因为我们的Attribute已经为我们针对这五个Aggregator定义了对应的方法。方法名称就是对应Aggregator实现类简称的首字母小写,如Min在Attribute中就对应min()方法。

当我们需要对某个查询属性进行统计时,我们需要把对应的Aggregator通过调用Query的includeAggregator()方法添加到查询的结果中。

Java代码

//创建一个用于查询的Query对象  
Query query = userCache.createQuery();  
Attribute<Integer> age = userCache.getSearchAttribute("age");  
//查询结果中包含age的平均值和age的最大值  
query.includeAggregator(age.average(), age.max());  
Results results = query.execute();  
List<Result> resultList = results.all();  
if (resultList != null && !resultList.isEmpty()) {  //每一个查询结果Result中都会包含对查询结果的统计信息。  Result result = resultList.get(0);  //多个统计信息将会组成一个List进行返回  List<Object> aggregatorResults = result.getAggregatorResults();  Number averageAge = (Number)aggregatorResults.get(0);  Integer maxAge = (Integer)aggregatorResults.get(1);  System.out.println(averageAge + "---" + maxAge);  
}  

当我们的查询结果中只包含有统计信息时,我们的查询结果Results中只会有一条记录,即一个Result对象。当包含其它信息时查询结果就可能会有多条记录,而且每条记录中都会包含有对应的统计信息。

排序

Ehcache中对于Cache的查询也是可以进行排序的,这是通过Query的addOrderBy()方法来指定的。该方法接收两个参数,第一个参数表示需要进行排序的属性Attribute,第二个参数是排序的方向Direction。Direction有两个可选值,Direction.ASCENDING和Direction.DESCENDING。当需要对多个属性进行排序时则需要调用多次addOrderBy()方法。

Java代码

Attribute<String> unitNo = userCache.getSearchAttribute("unitNo");  
Attribute<Integer> age = userCache.getSearchAttribute("age");  
//查询结果按部门编号的升序和年龄的降序进行排列  
query.addOrderBy(unitNo, Direction.ASCENDING).addOrderBy(age, Direction.DESCENDING);  

分组

Ehcache也支持对查询的缓存进行分组。这是通过Query的addGroupBy()方法来定义的,该方法接收一个Attribute作为参数,表示要对哪个Attribute进行分组,当需要对多个Attribute进行分组时,则需要调用多次addGroupBy()方法。使用分组的语法基本上跟SQL里面分组的语法是一样的,当使用分组时查询结果只能包含分组的属性和统计信息,统计信息是对分组后的情况进行统计。唯一不同的是Ehcache中查询分组时无法对分组后的情况进行筛选。

Java代码

//创建一个用于查询的Query对象  
Query query = userCache.createQuery();  
Attribute<String> unitNo = userCache.getSearchAttribute("unitNo");  
Attribute<Integer> age = userCache.getSearchAttribute("age");  
//对单位编号进行分组  
query.addGroupBy(unitNo);  
//各单位年龄的平均值、最大值以及人数。  
query.includeAggregator(age.average(), age.max(), age.count());  
//查询结果中还包含单位编码  
query.includeAttribute(unitNo);  
Results results = query.execute();  
List<Result> resultList = results.all();  
if (resultList != null && !resultList.isEmpty()) {  for (Result result : resultList) {  String unitNoVal = result.getAttribute(unitNo);  //多个统计信息将会组成一个List进行返回  List<Object> aggregatorResults = result.getAggregatorResults();  Number averageAge = (Number)aggregatorResults.get(0);  Integer maxAge = (Integer)aggregatorResults.get(1);  Integer count = (Integer)aggregatorResults.get(2);  System.out.println("单位编号:" + unitNoVal + "---" + averageAge + "," + maxAge + "," + count);  }  
}  

让Query不可变

默认情况下,我们的Query可以在执行后修改某些属性后继续查询。但是一旦我们调用了Query的end()方法之后我们将不能够再更改Query的一些属性。这包括调用include来定义返回结果中需要包含的信息、指定排序的属性、指定分组的属性、添加Criteria限制条件和调用maxResults()方法指定最大返回记录数。

对BeanShell的支持

BeanShell是用Java写的,能够对Java字符串表达式进行解释执行的一个工具。如果在实际应用中我们需要让用户来自定义查询的脚本时,我们就可以使用BeanShell来对查询脚本进行解释执行了。使用BeanShell前我们需加入BeanShell的jar包到类路径,笔者下面的示例中使用的是BeanShell2.0的第4个测试版本。

Java代码

@Test  
2.public void beanShell() throws EvalError {  CacheManager cacheManager = CacheManager.create();  Cache userCache = cacheManager.getCache("userCache");  User user;  for (int i=0; i<10; i++) {  user = new User(i, "name"+(i%2), 25+i);  userCache.put(new Element(user.getId(), user));  }  //BeanShell解释器,需引入BeanShell相关jar包  Interpreter interpreter = new Interpreter();  Query query = userCache.createQuery().includeValues();  //Interpreter进行计算的字符串中出现的变量都需要放入Interpreter的环境中  interpreter.set("query", query);//把query放入Interpreter环境中  //把age放入Interpreter环境中  interpreter.set("age", userCache.getSearchAttribute("age"));  String queryStr = "query.addCriteria(age.lt(30)).execute();";  //BeanShell执行字符串表达式对userCache进行查询,并返回Results  Results results = (Results)interpreter.eval(queryStr);  for (Result result : results.all()) {  System.out.println(result);  }  results.discard();  
}  

关于BeanShell的更多了解请访问BeanShell的官方网站www.beanshell.org。

总结

纵观整个Ehcahce中对于Cache的查询Query,我们可以发现其基本的逻辑和规则与SQL查询是一样的。可以进行筛选、选择要查询的结果、统计、排序和分组。Ehcache中的查询也是先通过Criteria进行筛选,再进行分组和排序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/234944.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PostgresSQL数据库中分区和分表的区别以及PostgresSQL创建表分区分表示例

1.分区分表理解 数据库分区和分表都是数据库中常用的数据分散存储技术&#xff0c;但它们的实现方式和应用场景有所不同。 分表&#xff1a;将一个大的表拆分成多个小的表&#xff0c;每个子表存储一部分数据。分表可以减轻单个表的数据量&#xff0c;提高查询效率&#xff0c…

计算机组成原理(复习题)

更多复习详情请见屌丝笔记 一、选择题 计算机系统概述 1、至今为止&#xff0c;计算机中的所有信息仍以二进制方式表示的理由是&#xff08; C &#xff09;。 A.运算速度快 B.信息处理方便 C.物理器件性能所致 D.节约元件 2、运算器的核心功能部件是&#xff08; D &am…

【离散数学】——期末刷题题库(树其二)

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…

[CVPR-23] PointAvatar: Deformable Point-based Head Avatars from Videos

[paper | code | proj] 本文的形变方法被成为&#xff1a;Forward DeformationPointAvatar基于点云表征动态场景。目标是根据给定的一段单目相机视频&#xff0c;重建目标的数字人&#xff0c;并且数字人可驱动&#xff1b;通过标定空间&#xff08;canonical space&#xff09…

http -- 跨域问题详解(浏览器)

参考链接 参考链接 1. 跨域报错示例 Access to XMLHttpRequest at http://127.0.0.1:3000/ from origin http://localhost:3000 has been blocked by CORS policy: Response to preflight request doesnt pass access control check: No Access-Control-Allow-Origin header…

【QT】解决QTableView修改合并单元格内容无法修改到合并范围内的单元格

问题:修改合并单元格的内容 修改合并单元格的内容时,希望直接修改到合并范围内的单元格,Qt没有实现这个功能,需要自己写出 Delegate来实现 方案:Delegate class EditDelegate : public QStyledItemDelegate {public:EditDelegate(QTableView *view): tableView(view){}pu…

JS基础之模块化

JS基础之模块化 JS模块化模块化前端发展 什么是模块&#xff1f;怎么定义模块化IIFE匿名函数自调用IIFE模式增强模块化的好处 JS模块化 模块化 JS DOM操作 代码规范管理的标准 不同模块间的管理模块内部自组织 标准bundler (模块构建工具) ESNext TS -> ES5 前端发展 生态 …

Python数据处理必备:Pandas DataFrame中行迭代技巧大曝光!

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 在数据分析和处理中&#xff0c;Pandas是Python中最常用的库之一&#xff0c;而DataFrame是Pandas的核心数据结构之一。迭代DataFrame中的行是一种常见的操作&#xff0c;本文将详细介绍几种迭代DataFrame行的方…

k8s集群内部署nexus

一、前言 在k8s集群中部署nexus服务需要使用到pv、pvc服务来存储nexus的数据&#xff0c;需要使用service服务来提供对外访问nexus服务的端口&#xff0c;需要使用deployment服务来管理nexus服务&#xff0c;接下来就是用这些服务来在k8s集群中搭建nexus&#xff0c;pv服务使用…

爱芯派pro通过无线网卡rtl8188eu连接热点

爱芯派pro通过无线网卡rtl8188eu连接热点 爱芯派pro目前的底板的pcie的复位有问题&#xff0c;所以pcie接口无法挂载上去&#xff0c;所以自己购买的rtl8822网卡也用不了&#xff0c;然后想起来自己还有正点原子的rtl8188eu网卡&#xff0c;但是没有和工作人员进行摸索后才知道…

如何禁止外网访问公司内网服务器

对于企业网络&#xff0c;经常会用到访问控制&#xff0c;例如限制员工的上网时间&#xff1f;或如何控制各部门之间的网络互通等等&#xff0c;在实际企业网络项目中经常会遇到&#xff0c;这里面我们就可以用到ACL访问列表控制了&#xff0c;本期我们一起来看下&#xff0c;如…

SpringBoot已经禁掉了循环依赖!

还在问循环依赖嘛&#xff1f;SpringBoot已经禁掉了循环依赖&#xff01; 首发2023-12-18 11:26yuan人生 如果现在面试时还有人问你循环依赖&#xff0c;你就这样怼他&#xff1a;循环依赖是一种代码质量低下的表现&#xff0c;springboot2.6之后的版本已经默认禁用了。 Spr…

保护电脑安全,火绒安全助手 V5.0

火绒安全软件&#xff0c;也称为“火绒安全助手”是一款由中国研发的综合安全软件。火绒安全软件旨在为计算机用户提供全面的安全保护&#xff0c;包括防病毒、反恶意软件、网络安全、系统优化等功能。 火绒安全的功能 病毒防护&#xff1a;火绒提供实时的病毒和恶意软件扫描…

排序算法——快排

快速排序算法最早是由图灵奖获得者Tony Hoare设计出来的,他在形式化方法理论以 及ALGOL.60编程语言的发明中都有卓越的贡献,是20世纪最伟大的计算机科学家之—。 而这快速排序算法只是他众多贡献中的—个小发明而已。 快速排序&#xff08;Quick Sort&#xff09;的基本算法思…

C语言—每日选择题—Day57

指针相关博客 打响指针的第一枪&#xff1a;指针家族-CSDN博客 深入理解&#xff1a;指针变量的解引用 与 加法运算-CSDN博客 第一题 1. 下面程序段&#xff08;&#xff09; char *str[] {"ABC", "DEF", "GHI"}; puts(str[1]); A&#xff1a;A…

用户行为分析遇到的问题-ubantu16,hadoop3.1.3

用户行为分析传送门 我的版本 ubantu16 hadoop 3.1.3 habse 2.2.2 hive3.1.3 zookeeper3.8.3 sqoop 1.46/1.47 我sqoop把MySQL数据往hbase导数据时候有问题 重磅&#xff1a;大数据课程实验案例&#xff1a;网站用户行为分析&#xff08;免费共享&#xff09; 用户行为分析-小…

5G+云渲染技术:将如何快速推进XR和元宇宙?

XR&#xff08;扩展现实&#xff09;领域正在以惊人的速度增长。目前&#xff0c;到 2024 年&#xff0c;一些专家表示这个行业的价值将达到 3000 亿美元。 这个行业发展如此迅速的部分原因是 XR 将在商业环境中的带来巨大利益。近年来&#xff0c;很多企业遇到了将增强现实和…

RabbitMQ不公平分发问题分析及问题解决

1.不公平分发 1.1 不公平分发策略是什么&#xff1f; 在 RabbitMQ 中&#xff0c;不公平分发&#xff08;Unfair Dispatch&#xff09;是指当多个消费者&#xff08;Consumers&#xff09;同时订阅同一个队列&#xff08;Queue&#xff09;时&#xff0c;消息的分发机制是不公…

I.MX6ULL_Linux_驱动篇(48)linux I2C驱动

I2C 是很常用的一个串行通信接口&#xff0c;用于连接各种外设、传感器等器件。本章我们来学习一下如何在 Linux 下开发 I2C 接口器件驱动&#xff0c;重点是学习 Linux 下的 I2C 驱动框架&#xff0c;按照指定的框架去编写 I2C 设备驱动。本章同样以 I.MX6U-ALPHA 开发板上的 …

Java数据结构-模拟ArrayList集合思想,手写底层源码(1),底层数据结构是数组,编写add添加方法,正序打印和倒叙打印

package com.atguigu.structure; public class Demo02_arrayList {public static void main(String[] args) {MyGenericArrayListV1 arrayListV1 new MyGenericArrayListV1();//arr.add(element:100,index:1);下标越界&#xff0c;无法插入//初始化&#xff08;第一次添加&…