sam.ds.chen 发表于 2013-1-15 02:28:31

Grails hack之改写findAll和find方法

GORM的查询方法很方便(可查看http://www.grails.org/doc/1.2.x/ref/Domain%20Classes/findAll.html)
 
但因为我现在用Db4o,根本就不需要ORM,GORM的finders派不上用场。但是GORM把finders以及validate注入到Domain Classes中的方式对我们很有启发性。
 
Db4o提供三种查询方式:QBE, NQ和SODA。其中NQ是type-safe的,在编译期可以作语法检查,在运行期可以被Db4o转化成高效的SODA。我不喜欢在Grails中使用NQ,一是因为这让代码看起来不太漂亮,二是无法保证所有的NQ都会被转化成SODA。
 
利用Groovy的Closure我写了两个方法:findAll和find,最终执行的都是SODA查询。改一改Grails的FindAllPersistentMethod代码:
 
package com.grs.db4opersistence.metaclass;import com.db4o.ObjectContainer;import com.db4o.query.Query;import com.grs.utils.GroovyDataUtils;import groovy.lang.MissingMethodException;import groovy.lang.Closure;import org.springframework.beans.SimpleTypeConverter;import java.util.Iterator;import java.util.Map;import java.util.regex.Pattern;/** * The "findAll" persistent static method allows searching for instances * * Usages: * (1) 0 arguments: User.findAll() * (2) 1 argument : User.findAll(sortInfoAndExampleMap) : User.findAll(sort:'dateCreated',order:'asc',department:department) * (3) 1 argument : User.findAll(exampleObject) * (4) 2 arguments: User.findAll(sortInfoMap, exampleObject): User.findAll(,userExample) * (5) 1 argument : User.findAll{it.descend('department').constrain(department)} * (6) 2 arguments: User.findAll(sort:'dateCreated',order:'asc'){it.descend('department').constrain(department)} * (7) 2 arguments: User.findAll(sort:'dateCreated',order:'asc',department:department){other complex constraints...} * (8) 3 arguments: User.findAll(sortInfoMap, exampleObject){other complex constraints} * * @author Sam Chen * * Created: Jun 2, 2010 * */public class FindAllPersistentMethod extends AbstractStaticPersistentMethod {    public static SimpleTypeConverter converter = new SimpleTypeConverter();public FindAllPersistentMethod(ObjectContainer objectContainer,ClassLoader classLoader) {super(objectContainer, classLoader, Pattern.compile("^findAll$"));}protected Object doInvokeInternal(final Class clazz, String methodName, Closure additionalCriteria, final Object[] arguments) {return doFindAll(clazz, methodName, additionalCriteria, arguments);}    protected final Object doFindAll(final Class clazz, String methodName, Closure additionalCriteria, final Object[] arguments) {            if (arguments.length == 0) {                Query q = getDb4oTemplate().query();                q.constrain(clazz);                q.descend("deleted").constrain(false);                return q.execute();            }            if (arguments.length == 1) {                Object arg = arguments;                if(clazz.isAssignableFrom(arg.getClass())) {                     return getDb4oTemplate().queryByExample(arg);                } else if(arg instanceof Map) {                  Query q = getDb4oTemplate().query();                  q.constrain(clazz);q.descend("deleted").constrain(false);                  Map map = (Map) arg;                  if (map.containsKey("sort") && map.containsKey("order")) {                        if (map.get("order").equals("desc")) {                            q.descend((String)map.get("sort")).orderDescending();                        } else {                            q.descend((String)map.get("sort")).orderAscending();                        }                  }                  for(Iterator<String> it = map.keySet().iterator(); it.hasNext();) {                        String key = it.next();                        if("sort".equals(key) || "order".equals(key)) {                            continue;                        }                        q.descend(key).constrain(map.get(key));                  }                  return q.execute();                } else if(arg instanceof Closure) {                  Query q = getDb4oTemplate().query();                  q.constrain(clazz);q.descend("deleted").constrain(false);                  ((Closure)arg).call(q);                  return q.execute();                } else {                  throw new MissingMethodException(methodName, clazz, arguments);                }            }            if (arguments.length == 2) {                Object arg0 = arguments;                Object arg1 = arguments;                if(!(arg0 instanceof Map)) {                  throw new MissingMethodException(methodName, clazz, arguments);                }                Query q = getDb4oTemplate().query();                q.constrain(clazz);q.descend("deleted").constrain(false);                Map map = (Map) arg0;                if (map.containsKey("sort") && map.containsKey("order")) {                  if (map.get("order").equals("desc")) {                        q.descend((String)map.get("sort")).orderDescending();                  } else {                        q.descend((String)map.get("sort")).orderAscending();                  }                }                for (Iterator<String> it = map.keySet().iterator(); it.hasNext();) {                  String key = it.next();                  if("sort".equals(key) || "order".equals(key)) {                        continue;                  }                  q.descend(key).constrain(map.get(key));                }                if (arg1 instanceof Closure) {                  ((Closure) arg1).call(q);                } else if (arg1 instanceof Map || clazz.isAssignableFrom(arg1.getClass())) {                  // extract properties from the domain object...                  applyConstraintsFromExample(arg1, q);                } else {                  throw new MissingMethodException(methodName, clazz, arguments);                }                return q.execute();            }            if (arguments.length == 3) {                Object arg0 = arguments; // sortInfoMap                Object arg1 = arguments; // example                Object arg2 = arguments; // closure                if(!(arg0 instanceof Map) || !(arg1 instanceof Map || clazz.isAssignableFrom(arg1.getClass())) || !(arg2 instanceof Closure)) {                  throw new MissingMethodException(methodName, clazz, arguments);                }                Query q = getDb4oTemplate().query();                q.constrain(clazz);q.descend("deleted").constrain(false);                Map map = (Map) arg0;                if (map.containsKey("sort") && map.containsKey("order")) {                  if (map.get("order").equals("desc")) {                        q.descend((String)map.get("sort")).orderDescending();                  } else {                        q.descend((String)map.get("sort")).orderAscending();                  }                }                // extract properties from the domain object...                applyConstraintsFromExample(arg1, q);                ((Closure)arg2).call(q);                return q.execute();            }            throw new MissingMethodException(methodName, clazz, arguments);      }    private void applyConstraintsFromExample(Object o, Query q) {      Map properties = (o instanceof Map ? (Map)o : GroovyDataUtils.getProperties(o));      for(Iterator<String> it = properties.keySet().iterator(); it.hasNext();) {            String key = it.next();            Object v = properties.get(key);            if(v != null) {                q.descend(key).constrain(v);            }      }    }} 
有了FindAllPersistentMethod方法,FindPersistentMethod就很简单了:
package com.grs.db4opersistence.metaclass;import com.db4o.ObjectContainer;import com.db4o.ObjectSet;import groovy.lang.Closure;import org.springframework.beans.SimpleTypeConverter;import java.util.regex.Pattern;/** * <p> * The "find" persistent static method allows searching for an instance ** @author Sam Chen * Created: Jun 2, 2010 **/public class FindPersistentMethod extends FindAllPersistentMethod {    public static SimpleTypeConverter converter = new SimpleTypeConverter();public FindPersistentMethod(ObjectContainer objectContainer, ClassLoader classLoader) {super(objectContainer, classLoader);      this.setPattern(Pattern.compile("^find$"));}protected Object doInvokeInternal(final Class clazz, String methodName, Closure additionalCriteria, final Object[] arguments) {ObjectSet os = (ObjectSet)this.doFindAll(clazz, methodName, additionalCriteria, arguments);      return os.size() > 0 ? os.get(0) : null;}}  
,同时修改它的method signature:
def findAllMethod = new FindAllPersistentMethod(objectContainer, application.classLoader)/** * Usages: * (1) 0 arguments: User.findAll() * (2) 1 argument : User.findAll(sortInfoAndExampleMap) : User.findAll(sort:'dateCreated',order:'asc',department:department) * (3) 1 argument : User.findAll(exampleObject) * (4) 2 arguments: User.findAll(sortInfoMap, exampleObject): User.findAll(,userExample) * (5) 1 argument : User.findAll{it.descend('department').constrain(department)} * (6) 2 arguments: User.findAll(sort:'dateCreated',order:'asc'){it.descend('department').constrain(department)} * (7) 2 arguments: User.findAll(sort:'dateCreated',order:'asc',department:department){other complex constraints...} * (8) 3 arguments: User.findAll(sortInfoMap, exampleObject){other complex constraints} */mc.static.findAll = {->    findAllMethod.invoke(mc.javaClass, "findAll", [] as Object[])}mc.static.findAll = {Object sortMapOrExampleOrClosure->    findAllMethod.invoke(mc.javaClass, "findAll", as Object[])}mc.static.findAll = {Map map, Object eoc ->    findAllMethod.invoke(mc.javaClass, "findAll", as Object[])}mc.static.findAll = {Map map, Object example, Closure c->    findAllMethod.invoke(mc.javaClass, "findAll", as Object[])} 再写一点测试代码:
package com.grs.sctmsimport grails.test.*class FindMethodIntegrationTests extends GrailsUnitTestCase {    protected void setUp() {      super.setUp()      for (int i = 0; i < 100; i++) {          new User(username:"user#$i", password:"pwd").save()      }      new User(username:"sam.ds.chen@xxx.com", password:"sam's password").save()    }    protected void tearDown() {      super.tearDown()    }    void testFindAllWithEmptyArgument() {      def users = User.findAll()      assertEquals 101, users.size()    }    void testFindAllWithOneArguments() {//      def users = User.findAll(sort:'username',order:'asc')//      assertEquals 'sam.ds.chen@xxx.com', users.username      def users = User.findAll(sort:'username',order:'asc', password:'pwd')      assertEquals 100, users.size()      users = User.findAll(new User(password:'pwd'))      assertEquals 100, users.size()      users = User.findAll{it.descend('password').constrain('pwd')}      assertEquals 100, users.size()    }    void testFindAllWithTwoArguments() {      def users = User.findAll(, new User(password:'pwd'))      assertEquals 100, users.size()      users = User.findAll(sort:'username',order:'asc') {          it.descend('username').constrain('user#').like()      }      assertEquals 100, users.size()      users = User.findAll(sort:'username',order:'asc',password:"sam's password") {          it.descend('username').constrain('sam.ds.chen@xxx.com')      }      assertEquals 1, users.size()      assertEquals "sam.ds.chen@xxx.com", users.username    }    void testFindAllWithThreeArguments() {      def users = User.findAll(, ) {      }      assertEquals 1, users.size()      assertEquals "sam.ds.chen@xxx.com", users.username      users = User.findAll(, new User(password:"sam's password")) {            it.descend('password').constrain("sam's password")      }      assertEquals 1, users.size()      assertEquals "sam.ds.chen@xxx.com", users.username    }} 测试结果
-------------------------------------------------------Running 2 integration tests...Running test com.grs.sctms.FindMethodIntegrationTests...PASSEDRunning test com.grs.sctms.ValidationIntegrationTests...PASSEDTests Completed in 2485ms ...-------------------------------------------------------Tests passed: 5Tests failed: 0-------------------------------------------------------不过有个小问题: 测试代码中被注释掉的部分
//      def users = User.findAll(sort:'username',order:'asc')//      assertEquals 'sam.ds.chen@xxx.com', users.username通不过。每次跑的
users.username的值都不一样 - 看来测试环境下排序没生效。而用grails run-app把它跑起来的时候,经debug证实数据是排好序的!怀疑是Grails的bug...
 
 
页: [1]
查看完整版本: Grails hack之改写findAll和find方法