前文已经介绍了如何对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部分的最后一部分内容:高级特性、编程性事务和约束。