六狼论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

新浪微博账号登陆

只需一步,快速开始

搜索
查看: 44|回复: 0

Grails hack之改写findAll和find方法

[复制链接]

升级  34.67%

26

主题

26

主题

26

主题

秀才

Rank: 2

积分
102
 楼主| 发表于 2013-1-15 02:28:31 | 显示全部楼层 |阅读模式
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([sort:'dateCreated',order:'asc'],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[0];                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[0];                Object arg1 = arguments[1];                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[0]; // sortInfoMap                Object arg1 = arguments[1]; // example                Object arg2 = arguments[2]; // 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([sort:'dateCreated',order:'asc'],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", [sortMapOrExampleOrClosure] as Object[])}mc.static.findAll = {Map map, Object eoc ->    findAllMethod.invoke(mc.javaClass, "findAll", [map, eoc] as Object[])}mc.static.findAll = {Map map, Object example, Closure c->    findAllMethod.invoke(mc.javaClass, "findAll", [map, example, c] 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[0].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([sort:'username',order:'asc'], 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[0].username    }    void testFindAllWithThreeArguments() {        def users = User.findAll([sort:'username',order:'asc'], [password:"sam's password"]) {        }        assertEquals 1, users.size()        assertEquals "sam.ds.chen@xxx.com", users[0].username        users = User.findAll([sort:'username',order:'asc'], new User(password:"sam's password")) {            it.descend('password').constrain("sam's password")        }        assertEquals 1, users.size()        assertEquals "sam.ds.chen@xxx.com", users[0].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[0].username通不过。每次跑的
users[0].username的值都不一样 - 看来测试环境下排序没生效。而用grails run-app把它跑起来的时候,经debug证实数据是排好序的!怀疑是Grails的bug...
 
 
您需要登录后才可以回帖 登录 | 立即注册 新浪微博账号登陆

本版积分规则

快速回复 返回顶部 返回列表