该Grails教程将教授使用HQL的基础知识。 Grails支持动态查找器,这使执行简单的数据库查询变得很方便。 但是对于更复杂的情况,Grails提供了Criteria API和HQL。 本教程将重点讨论后者。
介绍
众所周知,Grails位于Spring和Hibernate(这是两种最受欢迎的Java框架)的顶部。 Hibernate用作Grails对象关系映射(GORM)的基础技术。
Hibernate与数据库无关。 这意味着,由于Grails基于它,因此我们可以编写与大多数流行数据库兼容的应用程序。 我们不需要为每个可能的数据库编写不同的查询。
执行数据库查询的最简单方法是通过动态查找器。 简单而直观。 查看我以前的帖子 ,以获取有关此主题的教程。 但是动态查找器非常有限。 它可能不适用于复杂的要求以及开发人员需要较低控制级别的情况。 HQL是非常好的替代方法,因为它与SQL非常相似。
HQL完全面向对象,并且了解继承,多态性和关联。 使用它将提供一个非常强大且灵活的API,同时又可以使您的应用程序与数据库无关。 在Grails中,有两种域方法可用于调用HQL
- executeQuery –执行HQL查询(SELECT操作)
- executeUpdate –使用DML样式的操作(UPDATE和DELETE)更新数据库
executeQuery
这是一个示例Domain类,我们将从中进行查询:
package asia.grails.test
class Person {
String firstName
String lastName
int age
static constraints = {
}
}
检索所有域对象
这是从数据库检索所有Person对象的代码:
def listOfAllPerson = Person.executeQuery("from Person")
注意:
- executeQuery是Domain类的一种方法,用于检索信息(SELECT语句)
- 与SQL类似,需要from标识符
- 无需指定表,而是在from关键字之后指定域类。 我们也可以这样写查询
def listOfAllPerson = Person.executeQuery("from asia.grails.test.Person")
- 不指定select子句是有效的。 默认情况下,它将返回指定Domain类的对象实例。 在该示例中,它将返回所有Person对象的列表。
这是我们如何使用结果的示例代码
listOfAllPerson.each { person ->
println "First Name = ${person.firstName}"
println "Last Name = ${person.lastName}"
println "Age = ${person.age}"
}
由于listOfAllPerson是Person实例的列表,因此我们可以对其进行迭代并打印详细信息。
选择子句
当显式使用select子句时,HQL将不返回域对象的列表。 相反,它将返回一个二维列表。 这是一个示例,假定数据库中至少有1条记录:
def list = Person.executeQuery("select firstName, lastName from Person")
def firstPerson = list[0]
def firstName = firstPerson[0]
def lastName = firstPerson[1]
println "First Name = ${firstName}"
println "Last Name = ${lastName}"
变量列表将被分配一个项目列表。 每个项目都是一个列表,对应于select子句中枚举的值。
还可以这样编写代码,以帮助可视化数据结构:
def list = Person.executeQuery("select firstName, lastName from Person")
def firstName = list[0][0]
def lastName = list[0][1]
println "First Name = ${firstName}"
println "Last Name = ${lastName}"
Where子句
与SQL一样,我们可以使用where子句过滤查询结果。 这里有些例子:
母鹿的人
def peopleWithSurnameDoe = Person.executeQuery("from Person where lastName = 'Doe'")
至少18岁的人
def adults = Person.executeQuery("from Person where age >= 18")
名字包含John的人
def peopleWithFirstNameLikeJohn = Person.executeQuery("from Person where firstName like '%John%'")
组条款
组子句也是允许的。 该行为类似于SQL。 这是一个例子:
def list = Person.executeQuery("select age, count(*) from Person group by age")
list.each { item ->
def age = item[0]
def count = item[1]
println "There are ${count} people with age ${age} years old"
}
这将打印出表格中找到的所有年龄以及该年龄的人数。
有条款
Haven子句可用于过滤出分组依据的结果。 这是一个例子:
def list = Person.executeQuery(
"select age, count(*) from Person group by age having count(*) > 1")
list.each { item ->
def age = item[0]
def count = item[1]
println "There are ${count} people with age ${age} years old"
}
如果年龄组中有1个以上的人,它将打印表中找到的所有年龄以及具有该年龄的人数。
分页
一次检索所有表中的所有记录对性能不利。 分页结果更有效。 例如,一次获取10条记录。 这是有关如何执行此操作的代码示例:
def listPage1 = Person.executeQuery("from Person order by id", [offset:0, max:10])
def listPage2 = Person.executeQuery("from Person order by id", [offset:10, max:10])
def listPage3 = Person.executeQuery("from Person order by id", [offset:20, max:10])
参数max通知GORM仅获取最多10条记录。 偏移量表示在读取第一个结果之前要跳过多少条记录。
- 在第1页上,我们不会跳过任何记录并获得前10个结果
- 在第2页上,我们跳过前10条记录,而获得第11至20条记录。
- 在第3页上,我们跳过前20条记录,而获得第21至30条记录。
GORM / Hibernate会根据数据库将分页信息转换为正确的SQL语法。
注意:在对结果进行分页时,通常最好使用order by子句,否则大多数数据库无法保证每个查询之间的记录排序方式。
清单参数
HQL语句可以具有参数。 这是一个例子:
def result = Person.executeQuery(
"from Person where firstName = ? and lastName = ?", ['John', 'Doe'])
参数可以作为列表传递。 第一个参数(John)用于第一个问号,第二个参数(Doe)用于第二个问号,依此类推。
结果也可以分页
def result = Person.executeQuery(
"from Person where firstName = ? and lastName = ?", ['John', 'Doe'], [offset:0, max:5])
命名参数
提供列表参数通常很难读取并且容易出错。 使用命名参数更容易。 例如:
def result = Person.executeQuery(
"from Person where firstName = :searchFirstName and lastName = :searchLastName",
[searchFirstName:'John', searchLastName:'Doe'])
冒号表示命名参数变量。 然后可以将值作为值的映射传递。
结果也可以分页:
def result = Person.executeQuery(
"from Person where firstName = :searchFirstName and lastName = :searchLastName",
[searchFirstName:'John', searchLastName:'Doe'], [offset:0, max:5])
这是一个简短的版本:
def result = Person.executeQuery(
"from Person where firstName = :searchFirstName and lastName = :searchLastName",
[searchFirstName:'John', searchLastName:'Doe'], [offset:0, max:5])
如何执行JOIN
这是一对多关系域类的示例:
package asia.grails.test
class Purchase {
static hasMany = [items:PurchaseItem]
String customer
Date dateOfPurchase
double price
}
package asia.grails.test
class PurchaseItem {
static belongsTo = Purchase
Purchase parentPurchase
String product
double price
int quantity
}
这是连接两个表的示例代码:
def customerWhoBoughtPencils = Purchase.executeQuery(
"select p.customer from Purchase p join p.items i where i.product = 'Pencil' ")
这将返回所有购买铅笔的客户
executeUpdate
我们可以使用executeUpdate更新或删除记录。 特别是在处理大量记录时,这有时会更有效率。
删除
以下是一些有关如何使用executeUpdated删除记录的示例。
删除数据库中的所有人员记录
Purchase.executeUpdate("delete Person")
这是删除名字为John的人的不同方法
Person.executeUpdate("delete Person where firstName = 'John'")
Person.executeUpdate("delete Person where firstName = ? ", ['John'])
Person.executeUpdate("delete Person where firstName = :firstNameToDelete ", [firstNameToDelete:'John'])
更新资料
以下是一些有关如何使用executeUpdated删除记录的示例。
以下是关于如何使所有人都达到15岁的不同方法
Person.executeUpdate("update Person set age = 15")
Person.executeUpdate("update Person set age = ?", [15])
Person.executeUpdate("update Person set age = :newAge", [newAge:15])
以下是将John Doe的年龄设置为15岁的不同方法。
Person.executeUpdate(
"update Person set age = 15 where firstName = 'John' and lastName = 'Doe'")
Person.executeUpdate(
"update Person set age = ? where firstName = ? and lastName = ?", [15, 'John', 'Doe'])
Person.executeUpdate(
"update Person set age = :newAge where firstName = :firstNameToSearch and lastName = :lastNameToSearch",
[newAge:15, firstNameToSearch:'John', lastNameToSearch:'Doe'])