前文已经介绍了如何对Domain Class进行持久化,然而光存不查,那是没有什么意义的。因此,本篇将介绍持久化操作的另一面:查询。
最基本的查询有两种:罗列全部实例和获取单个实例,它们分别对应Domain Class的list和get方法。使用例子如下:
  • 列举全部:Book.list()
  • 分页并排序(这些参数不需要同时出现,按需使用即可):Book.list(offset:10, max:20, sort:"asc", order:"title")
  • 获取单个实例:Book.get(23)
  • 获取多个实例:Book.getAll(23, 93, 81)
list只能无条件的列出实例,但在日常开发中,最常见的就是按条件列出实例。一般情况下,我们都是直接通过HQL或SQL来完成,而在此处,借助Groovy的动态语言特性,我们可以使用动态查找器来优雅地完成这一任务。
动态查找器由前缀为findBy和findAllBy的方法构成,这些方法的构造规则(以findBy为例):DomainClass.findBy([Property][Comparator] [Boolean Operator])?[Property][Comparator]。其中:
  • 属性即为Domain Class的属性
  • 比较符有:InList、LessThan、LessThanEquals、GreaterThan、GreaterThanEquals、Like、Ilike(类似like,大小写不敏感)、NotEqual、Between、IsNotNull、IsNull
  • 布尔操作符有:and和or
请注意,这些方法并不是一开始就存在的,它们是利用Groovy MOP在运行时动态产生的,关于Groovy MOP的详细介绍,请阅读《Programming Groovy》。
同时,这类方法可以应用于多个属性,只要按照同样的规则自行购建即可。这里有一些动态查找器的例子:
  • Book.findByTitle("The Stand")
  • Book.findByReleaseDateBetween( firstDate, secondDate )
  • Book.findAllByTitleLikeOrReleaseDateGreaterThan("%Java%", new Date()-30)
动态查找器同样可以对关联进行查询,例子如下:
def author = Author.findByName("Stephen King")
    def books = author ? Book.findAllByAuthor(author) : []
而且,还可以进行分页和排序,参数同前:Book.findAllByTitleLike("Harry Pot%", [max:3, offset:2, sort:"asc", order:"title"])
Criteria是Hibernate的一个重要功能,GORM当然不会忽略它。通过Domain Class的createCriteria或withCriteria方法即可使用Criteria。这两个方法返回的是HibernateCriteriaBuilder,利用这个Builder,我们可以来构造criteria查询。关于这个对象,请查看参考文档。它的使用如下:
def c = Account.createCriteria()
    def results = c {
        like("holderFirstName", "Fred%")
        and {
            between("balance", 500, 1000)
            eq("branch", "London")
        }
        maxResults(10)
        firstResult(50)
        fetchMode("aRelationship", FM.EAGER)
        order("holderLastName", "desc")
    }
对于关联的查询:
def c = Account.createCriteria()
    def results = c.list {
         or {
            between('created',now-10,now)
            //transactions是Account中的hasMany关联属性
            transactions {
                 between('date',tDate-10, tDate)
            }
         }
    }
投影:
def c = Account.createCriteria()
    //get方法只返回一条记录
    def numberOfBranches = c.get {
        projections {
            countDistinct('branch')
        }
    }
Hibernate里面我们可以直接使用SQL约束,这里也不例外,使用它时请注意对应用移植性的影响:
def c = Person.createCriteria()
    def peopleWithShortFirstNames = c.list {
        //这里面使用的是SQL
        sqlRestriction "char_length( first_name ) <= 4"
    }
缺省返回的结果集是无法来回移动游标的,如果有这样的需求,那么可以求助于滚动结果集:
def results = crit.scroll {
          maxResults(10)
    }
    def f = results.first()
    def l = results.last()
    def n = results.next()
    def p = results.previous()
    def future = results.scroll(10)
    def accountNumber = results.getLong('number')
我们还可以设置查询的分页、排序和Fetch模式:
import org.hibernate.FetchMode as FM
    ......
    def results = c.list {
        maxResults(10)
        firstResult(50)
        fetchMode("aRelationship", FM.EAGER)
    }
设置Eager取(注意join的使用):
def criteria = Task.createCriteria()
    def tasks = criteria.list{
         eq "assignee.id", task.assignee.id
         join 'assignee'
         join 'project'
         order 'priority', 'asc'
    }
对于熟悉SQL的开发者,HQL是他们感到最亲切的东西。我们同样也可以在Domain Class中使用,通过find和findAll即可。前者返回匹配的第一个值,后者则返回全部:
  • Book.find("from Book as b where b.author='Dan Brown'")
  • Book.find(new Book(author:"Dan Brown")),这也是一个QBE(Query by Example)的例子
  • Book.findAll()
  • Book.findAll("from Book as b where b.author=? order by b.releaseDate",['Dan Brown'],[max:10,offset:5])
如果查询比较简单,我们可以直接提供Where部分,使用findWhere和findAllWhere即可(分页和排序的参数同前。)。
  • Author.findWhere(name:'Stephen King')
  • Author.findWhere(name:'Stephen King')
Groovy中支持多行字符串,对此要说明Groovy的多行字符串不能直接用于GORM,得要有点变动(注意每行结尾):
def results = Book.findAll("""\
        from Book as b, \
            Author as a \
        where b.author = a and a.surname = ?""", ['Smith'])
除了find,你还可以使用executeQuery:Book. executeQuery("from Book as b where b.title like 'Lord of the%'")。
至此,GORM的查询就介绍完毕了,在下一篇中我们将迎来GORM部分的最后一部分内容:高级特性、编程性事务和约束。