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([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.sctms

import 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...PASSED
Running test com.grs.sctms.ValidationIntegrationTests...PASSED
Tests Completed in 2485ms ...
-------------------------------------------------------
Tests passed: 5
Tests 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...

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值