【EhCache三】EhCache查询

本文介绍EhCache查询缓存中数据,EhCache提供了类似Hibernate的查询API,可以按照给定的条件进行查询。

 

要对EhCache进行查询,需要在ehcache.xml中设定要查询的属性

 

数据准备

    @Before
    public void setUp() {
        //加载EhCache配置文件
        InputStream in = EhcacheTest.class.getClassLoader().getResourceAsStream("ehcache-query.xml");
        CacheManager cm = CacheManager.create(in);

        cache = cm.getCache("data-query");

        Person p1 = new Person(1, "Tom", 34);
        Person p2 = new Person(2, "Tom", 34);
        Person p3 = new Person(3, "Jack", 32);

        Person p4 = new Person(4, "Jack", 32);

        //添加三个元素,Key是Person对象的ID
        Element element = new Element(p1.getId(), p1);
        cache.putIfAbsent(element);
        element = new Element(p2.getId(), p2);
        cache.putIfAbsent(element);
        element = new Element(p3.getId(), p3);
        cache.putIfAbsent(element);

        //添加一个元素,Key和Value都是Person对象

        cache.putIfAbsent(new Element(p4, p4));
    }

 1. 在上面的测试setup方法中,为缓存添加了四个元素。前三个以person的id作为key,最后一个以person对象本身作为key 2.四个person对象,有些数据是相同的,方便后面按照Value的指定属性进行查询

 

基本查询

 对EhCache最基本的需求是对缓存元素的Key和Value进行查询,这在ehcache.xml中需要做如下配置

 

    <cache>
        <searchable/>
    </cache>

 

 

searchable有两个可选属性,keys和values,它们的默认值都是true,即默认情况下,EhCache已经将Key和Value的查询添加上了,查询属性分别是key和value。上面的设置等价于

 

<cache>
    <searchable keys="true" values="true"/>
</cache>

 

 如果不允许针对keys或者values进行查询,将它置为false即可。

 

测试一:查询所有数据

 

    //查询缓存中所有的数据
    @org.junit.Test
    public void test0() {
        Query query = cache.createQuery();
        Results results = query.includeKeys().includeValues().execute();
        List<Result> resultList = results.all();
        Assert.assertEquals(4, resultList.size());
    }

 

测试二: 根据Key(简单类型查询)进行条件查询

 

    //根据简单查询属性类型(Key是整型)进行查询
    @org.junit.Test
    public void test1() {
        Query query = cache.createQuery();
        query.includeKeys(); //查询结果中包含Key
        query.includeValues(); //查询结果中包含Value
        Attribute<Integer> attribute = cache.getSearchAttribute("key"); //按照EhCache内置的可查询属性key进行查询
        query.addCriteria(attribute.eq(2)); //查询条件:key为2,2是简单类型
        Results results = query.execute();
        List<Result> resultList = results.all();
        Result result = resultList.get(0);
        Integer key = (Integer) result.getKey();
        org.junit.Assert.assertEquals(2, key.intValue());
        Person value = (Person) result.getValue();
        Assert.assertEquals(value.getAge(), 34);
    }

 

测试三: 根据Key(复杂类型查询)进行条件查询

样例数据中,第四个元素的key和value都是Person对象,如何创建把它查询出来?

 

@org.junit.Test
    public void test2() {
        Query query = cache.createQuery();
        query.includeKeys();
        query.includeValues();
        Attribute<Person> attribute = cache.getSearchAttribute("key");
        Person p4 = new Person(4, "Jack", 32);
        query.addCriteria(attribute.eq(p4));
        Results results = query.execute();
        List<Result> resultList = results.all();
        Assert.assertNotNull(resultList);
        Assert.assertEquals(resultList.size() , 1);
    }
 这个查询会抛出如下异常,表明,Person是一个不支持的查询类型,至于为什么会抛出这个异常,在后面讲到
net.sf.ehcache.search.SearchException: Unsupported type for search attribute [key]: com.tom.Person
	at net.sf.ehcache.search.attribute.AttributeType.typeFor(AttributeType.java:236)
	at net.sf.ehcache.search.expression.EqualTo.<init>(EqualTo.java:54)
	at net.sf.ehcache.search.Attribute.eq(Attribute.java:161)
 

查询结果排序

对查询的结果进行排序,也是常见的需求,下面是一个查询结果倒排序的例子:
测试四: 查询结果排序
   @org.junit.Test
    public void test3() {
        Query query = cache.createQuery();
        Attribute<Integer> attribute = cache.getSearchAttribute("key");
        //降序
        Results results = query.includeKeys().includeValues().addOrderBy(attribute, Direction.DESCENDING).execute();
        List<Result> resultList = results.all();
        org.junit.Assert.assertEquals(4, resultList.size());
        Result result = resultList.get(0);
        Object key = result.getKey();
        Object value = result.getValue();
        Assert.assertTrue(value instanceof Person);
        Person person = (Person) value;
        //排序结果,id为3的Person对象最大
    }
  
这里按照Key进行倒排序查询,查看结果,可知,结果的key分别是3,2,1, person4,即使Person实现了Comparable接口,person4依然排在最后,这是为什么?
 

按照Value的某个字段查询

 按照缓存数据对象的某个字段进行查询,是常见的需求,比如要查询出年龄为32的Person。
1.首先在ehcache中设置要查询的属性

        <searchable> 
            <searchAttribute name="age" expression="value.getAge()"/>
        </searchable>
 
这里定义了查询属性age,expression属性表示对value调用哪个方法取值然后进行匹配,这里使用的JavaBean的规范,每个Element的value调用getAge方法。expression属性是可选属性,如下所示:
        <searchable> 
            <searchAttribute name="age"/>
        </searchable>
这么写,EhCache解释为查找所有的可查询属性(这里是key和value),然后对每个元素的Key和Value查找getAge方法,如果找到,就通过getAge方法得到对应的值然后进行匹配,这里要注意的是,如果key和value都有getAge方法,那么这种通过这种匹配将抛出异常。在上面的样例数据中,因为第四个元素的Key和Value都是Person对象,都有getAge方法,EhCache不知道取哪个值,因此抛出异常。因此,使用seachAttribute元素时,明确的添加expression属性是一个良好的习惯。
测试五: 根据元素的value的某个字段进行查询
    //查询年龄为32的Person
    @org.junit.Test
    public void test4() {
        Query query = cache.createQuery().includeKeys().includeValues();
        Attribute<Integer> attribute = cache.getSearchAttribute("age");
        query.addCriteria(attribute.eq(32));
        Results results = query.execute();
        List<Result> resultList = results.all();
    }
 

查询属性抽取

现在回过头来测试三: 根据Key(复杂类型查询)进行条件查询。EhCache限制查询属性必须是简单类型,也就是说不能把Person传给EhCache让它进行查询,只能把Person的属性传递给EhCache让EhCache按照指定属性进行查询。如果一个对象需要多个字段才能唯一确定一个对象(比如多对多关系的实体中间关系对象,需要两个实体的ID)那么就需要抽取多个属性进行查询。

 

   ehcache.xml配置

 

        <searchable> 
            <searchAttribute name="personName" class="com.tom.PersonExtractor"/>
            <searchAttribute name="personAge" class="com.tom.PersonExtractor"/>
        </searchable>

  

 

package com.tom;

import net.sf.ehcache.Element;
import net.sf.ehcache.search.attribute.AttributeExtractor;
import net.sf.ehcache.search.attribute.AttributeExtractorException;

public class PersonExtractor  implements AttributeExtractor{
    @Override
    public Object attributeFor(Element element, String attributeName) throws AttributeExtractorException {
        Person p  = (Person) element.getObjectValue(); //针对Value进行查询
        if (attributeName.equals("personAge")) {
            return p.getAge();
        } else if (attributeName.equals("personName")) {
            return p.getName();
        }
        return null;
    }
}

 

    //查询年龄为32,名字为Jack的Person
    @org.junit.Test
    public void test5() {
        Query query = cache.createQuery().includeKeys().includeValues();
        Attribute<Integer> ageAttribute = cache.getSearchAttribute("personAge");
        Attribute<String> nameAttribute = cache.getSearchAttribute("personName");
        query.addCriteria(ageAttribute.eq(32).and(nameAttribute.eq("Jack")));

        Results results = query.execute();
        List<Result> resultList = results.all();
        Assert.assertTrue(resultList != null);
        Assert.assertEquals(2, resultList.size());
    }

 

总结:

 

  如果key,value是简单类型,那么可以直接使用key,value查询属性进行查询。如果Key,Value是复杂类型,那么最直观的做法是使用自定义的属性抽取按属性进行查询。
EhCache内置了几种属性抽取的实现
1.KeyObjectAttributeExtractor
2.ValueObjectAttributeExtractor
如果Key,Value是简单类型(即,EhCache直接支持的查询属性类型),那么使用这两种Extractor进行查询
 
3. net.sf.ehcache.search.attribute.JavaBeanAttributeExtractor
例如:
        <searchable> 
            <searchAttribute name="age"/>
        </searchable>
JavaBeanAttributeExtractor的 工作方式是首先检查Key中是否有这个属性,然后检查Value,如果属性在Key和Value中都出现,则抛出一场
 
4. net.sf.ehcache.search.attribute.ReflectionAttributeExtractor

 

        <searchable> 
            <searchAttribute name="age" expression="value.getAge()"/>
        </searchable>

 

 

 其中的expression的语法是:

 
1.expression必须以key、value或者element开头,表示查询作用域
2.expression以某个方法调用结束,比如上面的getAge()
3.链式表达方法:value.person.getAge(),这个含义指的是,缓存元素的Value有一个person成员属性,person有个getAge方法,那么通过.来获得级联效果
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(ehcache)