where 方法,在Grails 2.0被引入,构建在 Detached Criteria 之上,为普通查询提供了一个增强的运行时检查的查询DSL。where 方法比动态查询更灵活,比criteria的代码更简洁而且为撰写查询提供了一个强大的机制。
基本查询
where 方法接受一个闭包看起来非常类似于Groovy的规范集合方法。闭包用规范的Groovy语法定义一个逻辑条件。例如:
def query = Person.where {
firstName == "Bart"
}
Person bart = query.find()
返回的对象是一个 DetachedCriteria 实例,这意味着他不语任何特定的数据库连接或会话相关联。意味着你可以使用 where在类层面去定义普通查询:
class Person {
static simpsons = where {
lastName == "Simpson"
}
…
}
…
Person.simpsons.each {
println it.firstname
}
查询执行是懒加载,仅仅在使用 DetachedCriteria实例之后才发生。如果你要执行一个立即生效的where样式的查询,可以改用 findAll 和 find方法的来完成:
def results = Person.findAll {
lastName == "Simpson"
}
def results = Person.findAll(sort:"firstName") {
lastName == "Simpson"
}
Person p = Person.find { firstName =="Bart" }
每一个Groovy操作映射到一个常规的criteria方法。下表提供了一个Groovy操作映射方法的列示:
Operator | Criteria Method | Description |
== | eq | Equal to |
!= | ne | Not equal to |
> | gt | Greater than |
< | lt | Less than |
>= | ge | Greater than or equal to |
<= | le | Less than or equal to |
in | inList | Contained within the given list |
==~ | like | Like a given string |
=~ | ilike | Case insensitive like |
这使我们利用规范的Groovy比较操作符和逻辑来定制复杂的查询成为可能:
def query = Person.where {
(lastName != "Simpson" &&firstName !="Fred") || (firstName =="Bart" && age > 9)
}
def results = query.list(sort:"firstName")
Groovy的正则表达式匹配操作符映射到like和ilike查询。除非在表达式的右边是一个 Pattern 对象,这种情况下,他们映射到一个 rlike 查询:
def query = Person.where {
firstName ==~ ~/B.+/
}
注意,只有底层数据库支持正则表达式, rlike 查询才会被支持。
一个 between 条件查询可以使用 in 关键词来组合一个。
def query = Person.where {
age in 18..65
}
最后,你可以用null和标准的比较运算符来做 isNull 和isNotNull 样式查询:
def query = Person.where {
middleName == null
}
查询组合
由于 where方法的返回值是一个 DetachedCriteria实例,你可以从原来的查询组合成新的查询:
def query = Person.where {
lastName == "Simpson"
}
def bartQuery = query.where {
firstName == "Bart"
}
Person p = bartQuery.find()
注意,你不能在where方法闭包中定义变量传递,除非他已经明确地被转换成了 DetachedCriteria 实例。也就是说下面的语句是错误的:
def callable = {
lastName == "Simpson"
}
def query = Person.where(callable)
上面的代码必须这样写:
import grails.gorm.DetachedCriteria
def callable = {
lastName == "Simpson"
} as DetachedCriteria<Person>
def query = Person.where(callable)
这如你所看到的,闭包定义是转换到 (使用Groovy as 关键字)一个在Person类中标记的 DetachedCriteria实例。
关联、分离和取反
就像前面提到的,你可以组合Groovy标准的逻辑运算符 (|| 和 &&),形成关联和分离:
def query = Person.where {
(lastName != "Simpson" &&firstName !="Fred") || (firstName =="Bart" && age > 9)
}
你还可以使用逻辑比较符!进行取反。
def query = Person.where {
firstName == "Fred" &&!(lastName == 'Simpson')
}
属性比较查询
如果你在表达式的两边都是用属性名,那么相应的属性比较条件会被自动使用:
def query = Person.where {
firstName == lastName
}
下表描述了每个比较操作映射到每个条件属性比较法:
Operator | Criteria Method | Description |
== | eqProperty | Equal to |
!= | neProperty | Not equal to |
> | gtProperty | Greater than |
< | ltProperty | Less than |
>= | geProperty | Greater than or equal to |
<= | leProperty | Less than or equal to |
查询关联
关联可以被查询,使用点操作符来指定关联查询的属性名:
def query = Pet.where {
owner.firstName == "Joe" ||owner.firstName =="Fred"
}
你可以将一组条件放进一个闭包方法中进行调用,在哪里,方法名匹配关联名:
def query = Person.where {
pets { name == "Jack" || name == "Joe" }
}
这个技术可以结合其他顶层规范:
def query = Person.where {
pets { name == "Jack" } || firstName=="Ed"
}
集合关联还可以用来查询集合的大小:
def query = Person.where {
pets.size() == 2
}
下表显示了查询集合大小的标准比较运算符:
Operator | Criteria Method | Description |
== | sizeEq | The collection size is equal to |
!= | sizeNe | The collection size is not equal to |
> | sizeGt | The collection size is greater than |
< | sizeLt | The collection size is less than |
>= | sizeGe | The collection size is greater than or equal to |
<= | sizeLe | The collection size is less than or equal to |
子查询
可能会在where查询中使用子查询。例如,找出所有年龄大于平均年龄的人:
final query = Person.where {
age > avg(age)
}
下面是相关子查询的列表:
Method | Description |
avg | The average of all values |
sum | The sum of all values |
max | The maximum value |
min | The minimum value |
count | The count of all values |
property | Retrieves a property of the resulting entities |
你可以使用of 方法申请添加条件到任何子查询,通过一个闭包来传递条件:
def query = Person.where {
age > avg(age).of { lastName =="Simpson" } &&firstName =="Homer"
}
因为属性子查询返回多个结果,可以使用比较结果作为条件。例如,下面的例子查询一个比“simpson”年龄小的人。
Person.where {
age < property(age).of {lastName =="Simpson" }
}
其他函数
有几个函数可以加入到你的查询内容中。列示如下:
Method | Description |
second | The second of a date property |
minute | The minute of a date property |
hour | The hour of a date property |
day | The day of the month of a date property |
month | The month of a date property |
year | The year of a date property |
lower | Converts a string property to upper case |
upper | Converts a string property to lower case |
length | The length of a string property |
trim | Trims a string property |
目前函数只能应用于领域类的属性和关联,你不能在诸如子查询的结果中使用函数。
例如,下面的查询寻找所有在2011年出生的人:
def query = Pet.where {
year(birthDate) == 2011
}
你还可以在关联中应用函数:
def query = Person.where {
year(pets.birthDate) == 2009
}
批量更新和删除
由于每一个 where 方法调用返回一个 DetachedCriteria 实例,你可以使用 where 查询执行批量操作。例如,批量更新和删除。
例如,下面的查询将所有 lastName为"Simpson"的人的lastName更新为"Bloggs":
def query = Person.where {
lastName == 'Simpson'
}
inttotal = query.updateAll(lastName:"Bloggs")
注意,一个批量操作方面的限制是连接查询(查询查询关联)是不允许的。
批量删除记录你可以使用deleteAll 方法:
def query = Person.where {
lastName == 'Simpson'
}
inttotal = query.deleteAll()