Ehcahce 搜索类型简单扩展
简述:
Ehcache自2.4版本后就支持搜索功能,具体的使用语法及规范这里就不说了。在使用的过程中发现三个问题:
1.由于Ehcache搜索的实质就是遍历储存的对象,并用Criteria过滤过滤元素,用Ordering排序结果,最后再用Aggregator计算组合函数。除了Key-Value的检索方式,其他形式的搜索,只要在cache中缓存的结果达到3次方的数量级,效率就很堪忧。因为cache中缓存的对象达到这个数量级时,非Key-Value的检索一次要耗时10毫秒左右,这样的话,一旦有千次以上的检索,性能就会很差。
2.使用中发现一个小BUG,对于String类型的Attribute居然对大小写不敏感。
3.第三条就是要说的重点,ehcahce对于byte[]类型不支持。官方文档中写道,只支持以下几种类型:
-
Boolean
-
Byte
-
Character
-
Double
-
Float
-
Integer
-
Long
-
Short
-
String
-
java.util.Date
-
java.sql.Date
-
Enum
对于第一个问题,我只是简单限制了缓存对象的个数,只要在ehcache.xml对对应的缓存配置下。对于是选择FIFO,LFU还是LRU储存策略,这个我没做过具体研究,也不知道对检索的速度的影响。
第二个问题很小儿科,下文将给出代码。对于第三个问题,虽然支持了byte[]类型但只限于EqualTo和NotEqualTo的检索。因为其他的例如GreaterThan,LessThan 或者ILike对于byte[]的支持,第一没有意义,第二因为要让byte[]实现Comparable又或者要实现相应的算法,必须要自定义一个byte数组的类,对于原本的框架要有较大的改动。
对于ehcahce的search原理简单介绍:
-
当你执行query的时候,“储存器”Store将会遍历内存中的每个对象,并将Attribute所有抽取器传递给Query。
-
Query首先按照设置的Criteria来过滤对象,执行的顺序是(AND,NOT,OR)然后是(Ilike,EqualTo,NotEqualTo,ComparableValue),其中ComparableValue顾名思义是比较类的Criteria如between,GreaterThan,GreaterThanOrEqual,LessThan和LessThanOrEqual。
-
但不管是ILike,EqualTo都要去比对你输入值类型和通过抽取器AttributeExtractor获得该Attribute的类型。这是扩展Attribute支持的重要一环。
-
如果比对成功,将会执行对应类型的比较,否则抛出异常。
-
将符合Criteria的对象放入result中。
-
如果有Ordering就讲result排序。
-
如果有Aggregators就计算出相应的组合函数值并放入到result中。
源代码改写:
首先我们要先扩展AttriuteType.java这个类,所有的Attibute类型都是依靠它来识别的。这是一个枚举类,我们需要添加如下代码:
BYTEA{
/**
* {@inheritDoc}
*/
@Override
public void validateValue(String name, Object value) throws SearchException {
if(!(value instanceof byte[])){
throw new SearchException("Expecting a String value for attribute [" + name + "] but was " + type(value));
}
}
}
并且要加入到它的Mappings中,这个原因就不说了,有点繁琐。在这个类的静态代码块中:
static {
MAPPINGS.put(Boolean.class, BOOLEAN);
MAPPINGS.put(Byte.class, BYTE);
MAPPINGS.put(Character.class, CHAR);
MAPPINGS.put(Double.class, DOUBLE);
MAPPINGS.put(Float.class, FLOAT);
MAPPINGS.put(Integer.class, INT);
MAPPINGS.put(Long.class, LONG);
MAPPINGS.put(Short.class, SHORT);
MAPPINGS.put(String.class, STRING);
MAPPINGS.put(java.util.Date.class, DATE);
MAPPINGS.put(java.sql.Date.class, SQL_DATE);
MAPPINGS.put(byte[].class, BYTEA);//add byte array type
}
其次就是要改写EqualTo.java这个类的execute方法:
public boolean execute(Element e, Map<String, AttributeExtractor> attributeExtractors) {
Object attributeValue = attributeExtractors.get(attributeName).attributeFor(e, getAttributeName());
if (attributeValue == null) {
return false;
} else {
AttributeType attrType = AttributeType.typeFor(getAttributeName(), attributeValue);
if (!getType().equals(attrType)) {
throw new SearchException("Expecting attribute of type " + getType().name() + " but was " + attrType.name());
}
// if (getType().equals(AttributeType.STRING)) {
// return ((String) this.value).equalsIgnoreCase((String)
// attributeValue);
// } else {
// return this.value.equals(attributeValue);
// }
if (getType().equals(AttributeType.STRING)) {
return ((String) this.value).equals((String) attributeValue);//make it case sensitive
} else if (getType().equals(AttributeType.BYTEA)) {//new support type byte array
return Arrays.equals((byte[]) this.value, (byte[]) attributeValue);
} else {
return this.value.equals(attributeValue);
}
}
}
不需要改写NotEqualTo这个类,因为它调用的就是EqualTo这个类。
总的说来这都是对ehcahce的一些小改动,这样脚踏实地的一步步走,还是挺有成就感的。在使用的过程中我还发现ehcachesearch的一个硬伤,Query中的Criteria会跟你的输入的值绑定,例如有这样一个Attibute:
Attribute<Long> oid= Ehcache.getSearchAttribute("m_loid");
又有这样的Criteria:
oid.eq(new Long(2)).or(oid.eq(new Long(3)))
这个Criteria和
oid.eq(new Long(3)).or(oid.eq(new Long(3)))
完全不一样,因为Criteria会把值写入到对象中,只就导致同一个表达式不能重用。如果要实现Criteria的重用估计得从新设计框架。
引用:
http://ehcache.org/documentation/user-guide/search