11. SODA Evaluations SODA评估
In the SODA API chapter we already mentioned Evaluations as a means of providing user-defined custom constraints and as a means to run any arbitrary code in a SODA query. Let's have a closer look.
作为一种让用户自定义约束的、在SODA查询中执行任意代码的手段,我们在SODA API一章就提过Evaluation(评估)了。下面,让我们作更进一步了解吧。
11.1. Evaluation API Evaluation API
The evaluation API consists of two interfaces, Evaluation and Candidate . Evaluation implementations are implemented by the user and injected into a query. During a query, they will be called from db4o with a candidate instance in order to decide whether to include it into the current (sub-)result.
Evaluation API包括两个接口:Evaluation和Candidate。Evaluation是由用户来实现并注入到一个查询中的。在查询中,db4o将通过使用一个candidate示例作为参数来调用它,这样作是为了检查该candidate实例是否应该包含到当前(子集)结果。
The Evaluation interface contains a single method only:
Evaluation接口仅包含了一个方法。
public void evaluate(Candidate candidate);
This will be called by db4o to check whether the object encapsulated by this candidate should be included into the current candidate set.
此方法将被db4o调用,来检查该candidate所封装的对象是否应该被包含到当前的candidate集合中。
The Candidate interface provides three methods:
Candidate接口提供了三个方法:
public Object getObject();
public void include(boolean flag);
public ObjectContainer objectContainer();
An Evaluation implementation may call getObject() to retrieve the actual object instance to be evaluated, it may call include() to instruct db4o whether or not to include this object in the current candidate set, and finally it may access the current database directly by calling objectContainer().
每个Evaluation实现都可以通过getObject()方法获得真正要被评估的对象,它可以调用include()告知db4o是否要将该对象加入到当前candidate集。最后,它可以通过调用objectContainer()访问到当前的数据库。
11.2. Example 示例
For a simple example, let's go back to our Pilot/Car implementation from the Collections chapter. Back then, we kept a history of SensorReadout instances in a List member inside the car. Now imagine that we wanted to retrieve all cars that have assembled an even number of history entries. A quite contrived and seemingly trivial example, however, it gets us into trouble: Collections are transparent to the query API, it just 'looks through' them at their respective members.
作为一个简单的示例,让我们回顾在Collection章节中的Pilot/Car的例子。那时,我们把传感器读数实例的历史保存在Car的一个List成员中。现在假设我们要找出所有拥有偶数条历史纪录的赛车。这是一个巧妙而看起来常见的例子,但是它把我们带入麻烦之中:集合对查询API来说是透明的, 从集合中的各个成员看来,查询API就好像是"没有看见"它们一样。
So how can we get this done? Let's implement an Evaluation that expects the objects passed in to be instances of type Car and checks their history size.
我们该怎么做?我们来实现一个Evaluation,希望传入的参数是Car的实例并检查它们的历史纪录数量。
package com.db4o.f1.chapter6;
import com.db4o.f1.chapter3.*;
import com.db4o.query.*;
public class EvenHistoryEvaluation implements Evaluation {
public void evaluate(Candidate candidate) {
Car car=(Car)candidate.getObject();
candidate.include(car.getHistory().size() % 2 == 0);
}
}
To test it, let's add two cars with history sizes of one, respectively two:
为了测试,让我们分别加进两辆有历史纪录的赛车。
Pilot pilot1=new Pilot("Michael Schumacher",100);
Car car1=new Car("Ferrari");
car1.setPilot(pilot1);
car1.snapshot();
db.set(car1);
Pilot pilot2=new Pilot("Rubens Barrichello",99);
Car car2=new Car("BMW");
car2.setPilot(pilot2);
car2.snapshot();
car2.snapshot();
db.set(car2);
and run our evaluation against them:
用它们来运行我们的Evaluation:
Query query=db.query();
query.constrain(Car.class);
query.constrain(new EvenHistoryEvaluation());
ObjectSet result=query.execute();
Util.listResult(result);
OUTPUT:
1
BMW[Rubens Barrichello/99]/2
11.3. Drawbacks 不足之处
While evaluations offer you another degree of freedom for assembling queries, they come at a certain cost: As you may already have noticed from the example, evaluations work on the fully instantiated objects, while 'normal' queries peek into the database file directly. So there's a certain performance penalty for the object instantiation, which is wasted if the object is not included into the candidate set.
Evaluation允许你更加自由地构造查询,同时也带来一些需要付出的代价:也许你已经从示例中注意到了,Evaluation工作在完全实例化的对象上,而"一般"的查询是直接窥视数据库文件的。所以就肯定会存在进行对象实例化所造成的性能损耗。
Another restriction is that, while 'normal' queries can bypass encapsulation and access candidates' private members directly, evaluations are bound to use their external API, just as in the language itself.
另一个限制是:"一般"的查询可以绕过封装(encapsulation )、直接访问Candidate的私有属性,而Evaluation则只能使用它们的外部API。
One last hint: Evaluations are expected to be serializable for client/server operation. So be careful when implementing them as (anonymous) inner classes and keep in mind that those will carry an implicit reference to their surrounding class and everything that belongs to it. Best practice is to always implement evaluations as normal top level or static inner classes.
最后提醒:在客户端/服务器环境下的操作中,Evaluation要求可以被序列化。当用(匿名)内部类来实现它们的时候要特别小心,记得这些实现类将对它们的外部类及其所有成员都存在有隐式的引用。最佳实践是永远都以普通顶级类或静态内部类的形式来实现Evaluation。
11.4. Conclusion 总结
With the introduction of evaluations we finally completed our query toolbox. Evaluations provide a simple way of assemble arbitrary custom query building blocks, however, they come at a price.
伴随着介绍Evaluation的过程,我们终于完成了我们的"查询工具箱"。Evaluation提供了一条简单的路径让我们构建自定义的查询组件,当然,使用它要付出一定的代价。
11.5. Full source 完整的源码
package com.db4o.f1.chapter6;
import java.io.*;
import com.db4o.*;
import com.db4o.f1.*;
import com.db4o.f1.chapter3.*;
import com.db4o.query.*;
public class EvaluationExample extends Util {
public static void main(String[] args) {
new File(Util.DB4OFILENAME).delete();
ObjectContainer db=Db4o.openFile(Util.DB4OFILENAME);
try {
storeCars(db);
queryWithEvaluation(db);
}
finally {
db.close();
}
}
public static void storeCars(ObjectContainer db) {
Pilot pilot1=new Pilot("Michael Schumacher",100);
Car car1=new Car("Ferrari");
car1.setPilot(pilot1);
car1.snapshot();
db.set(car1);
Pilot pilot2=new Pilot("Rubens Barrichello",99);
Car car2=new Car("BMW");
car2.setPilot(pilot2);
car2.snapshot();
car2.snapshot();
db.set(car2);
}
public static void queryWithEvaluation(ObjectContainer db) {
Query query=db.query();
query.constrain(Car.class);
query.constrain(new EvenHistoryEvaluation());
ObjectSet result=query.execute();
Util.listResult(result);
}
}