注:本文档只针对spring boot工程使用的情况有针对性的翻译,完整版本请登录GORM的github官网浏览
[[Domain Class介绍]] == Domain类介绍
1. Domain 类的简介
在MVC模式(Model View Controller)中domain类履行M的职责,表示映射到基础数据库表上的持久性实体. 按照约定所有的domain类都存放在 domain
目录下.:
使用IDE或文本编译器书写以下代码.
import grails.persistence.Entity;
import org.grails.datastore.gorm.GormEntity
@Entity
class Book implements GormEntity<Book>{
String title;
Date releaseDate;
Author author;
}
domain类需要实现GormEntity接口和增加Entity注解.
默认情况下,类名称,映射到表名小写,若domain类使用驼峰表示法则使用下划线分隔.例如若类名是 BookStore`则默认映射的表名为 `book_store
. 每个属性映射到单个数据表列.
默认表命名方案的一个限制是,具有相同名称的2个域类是有问题的,即使它们被定义为不同的的包内.例如`com.bookstore.Book` and org.yunchen.utility.Book
都映射到一个表名 book
.应用中碰过这类问题,可以给不同的类名称或在mapping中提供具体的表名来解决,如:
import grails.persistence.Entity;
import org.grails.datastore.gorm.GormEntity
@Entity
class Book implements GormEntity<Book>{
String title;
Date releaseDate;
Author author;
static mapping = {
table (name:"book_table")
comment "图书表"
}
参看 reference 部分查看更多domain类的方法和使用.
参看 GORM 了解更多.
2. Domain类的方法列表
2.1. addTo方法
3. addTo* 方法
3.1. 目的
为一对多或多对多关系添加域类关系,其中以该方法的后缀的属性名称表示关系的映射属性
3.2. 示例
def fictBook = new Book(title: "IT")
def nonFictBook = new Book(title: "On Writing: A Memoir of the Craft")
def a = new Author(name: "Stephen King")
.addToFiction(fictBook)
.addToNonFiction(nonFictBook)
.save()
3.3. 描述
`addTo*`方法是一种动态的方法,使用一个会自动添加实例的关联。它还自动配置双向关系,使双方都设置。
考虑上面例子中使用的这些domain类:
@Entity
class Author implements GormEntity<Author>{
String name
static hasMany = [fiction: Book, nonFiction: Book]
}
@Entity
class Book implements GormEntity<Book>{
String title
static belongsTo = [author: Author]
}
这个例子创造了一本新的小说类书籍和一本新的非小说类书籍,归属于一个作者。
addToFiction`是一个动态方法,因为 `Fiction
部分表示的`fiction`集合定义在domain类的`hasMany`属性中,同样的关系也表现在 `NonFiction`和`nonFiction`上.
通过调用 `Author`对象的`save()`方法,关联的 `Book`示例也保存入库, 尽管我们没有显示的调用它们的`save()`方法.
此外,调用addTo*方法会初始化关联的集合,并且会设置`Book`的`author`的引用关系。
还有一种更紧凑的方法,该方法接受一个“map”而不是一个域类实例,如下调用,则自动初始化`Book`对象:
def a = new Author(name: "Stephen King")
.addToFiction(title: "IT")
.addToNonFiction(title: "On Writing: A Memoir of the Craft")
.save()
以上代码可以运行,是因为GORM知道addTo被添加类型,可以使用标准的`Map`构造函数来创建实例.
3.3.1. attach方法
4. attach方法
4.1. 目的
将“detached”状态的domain类实例与当前Hibernate会话关联在一起
4.2. 示例
def b = Book.get(1)
b.title = "Blah"
b.save(flush:true)
b.discard()
...
if (!b.isAttached()) {
b.attach()
}
4.3. 描述
Hibernate在持久会话session中管理持久实例。每个请求创建新会话,并在请求结束时关闭。如果一个对象是从会话中获取并放置到一个Web范围如HttpSession,状态将变为“detached”,一旦Hibernate会话session关闭和废弃。你可以使用` attach() `方法重新附加现有的持久化实例对当前请求的Hibernate session。
4.3.1. belongsTo静态属性
5. belongsTo 静态属性
5.1. 目的
Defines a "belongs to" relationship where the class specified by belongsTo
assumes ownership of the relationship. This has the effect of controlling how saves and deletes cascade. The exact behaviour depends on the type of relationship:
-
Many-to-one/one-to-one: saves and deletes cascade from the owner to the dependant (the class with the
belongsTo
). -
One-to-many: saves always cascade from the one side to the many side, but if the many side has
belongsTo
, then deletes also cascade in that direction. -
Many-to-many: only saves cascade from the "owner" to the "dependant", not deletes.
5.2. 示例
class Book {
String title
static belongsTo = [author: Author]
}
In this example the Book
class specifies that it belongs to the Author
class, hence when an Author
instance is deleted so are all its associated Book
instances.
5.3. 描述
The belongsTo
property abstracts the nature of the cascading behaviour in Hibernate. If you want one class to belong to another but not have a back reference, then you can specify a class or a list of classes as the value:
class Book {
static belongsTo = Author
}
or:
class Book {
static belongsTo = [Author, Library]
}
Back references, i.e. properties linking back to the owner, can be added in one of two ways:
class Book {
Author author
static belongsTo = Author
}
or:
class Book {
static belongsTo = [author: Author]
}
In these examples, both techniques create an Author
property named author
. Also, the Map property can specify multiple properties and types if the class belongs to more than one owner.
The belongsTo
property is simple and means you don’t have to worry about the Hibernate cascading strategies, but if you need more control over cascading you can use the ORM DSL. This allows fine grained control of cascading updates and deletes.
5.3.1. clearErrors方法
6. clearErrors方法
6.1. 目的
重装domain类的errors列表 . 如果domain类包含绑定或验证的错误时,将非常有用。这些错误可以通过程序纠正。如果不清除错误,验证将继续失败。
6.2. 示例
def b = new Book(title: "The Shining")
b.validate()
if (b.hasErrors()) {
// clear the list of errors
b.clearErrors()
// fix the validation issues
b.author = "Stephen King"
// re-validate
b.validate()
}
6.2.1. constraints静态属性
7. constraints
7.1. Purpose
Allows the definition of declarative validation constraints. See validation in the user guide.
7.2. Examples
class Book {
String title
Author author
static constraints = {
title blank: false, size: 5..150
author nullable: true
}
}
7.3. Description
Constraints are defined using the declarative constraints DSL as described in the validation section of the user guide. Once evaluated, validation can be applied through the use of the validate method:
def b = new Book()
assert !b.validate()
The static constrainedProperties
property is a Map such that the keys in the Map are property names and the values associated with the keys are instances of {apiDocs}grails/validation/ConstrainedProperty.html[ConstrainedProperty]:
def constraintsMap = Book.constrainedProperties
for(entry in constraintsMap) {
// propertyName is a String
def propertyName = entry.key
// constrainedProperty is a ConstrainedProperty
def constrainedProperty = entry.value
// ...
}
7.3.1. count方法
8. count方法
8.1. 目的
返回domain类对应的表的记录总数
8.2. 示例
int bookCount = Book.count()
8.2.1. countBy方法
9. countBy*方法
9.1. 目的
使用domain类中的属性来查询相应记录的数目的动态方法
9.2. 示例
这是`Book`的domain类:
class Book {
String title
Date releaseDate
String author
}
下面的用法都是可以的:
def c = Book.countByTitle("The Shining")
c = Book.countByTitleAndAuthor("The Sum of All Fears", "Tom Clancy")
c = Book.countByReleaseDateBetween(firstDate, new Date())
c = Book.countByReleaseDateGreaterThanEquals(firstDate)
c = Book.countByTitleLike("%Hobbit%")
c = Book.countByTitleNotEqual("Harry Potter")
c = Book.countByReleaseDateIsNull()
c = Book.countByReleaseDateIsNotNull()
9.3. 描述
GORM 支持 Dynamic Finders 的概念. `countBy*`方法对符合给定的表达的记录进行计数
下面的操作名称可以被用在各自的动态方法中:
-
LessThan
-
LessThanEquals
-
GreaterThan
-
GreaterThanEquals
-
Between
-
Like
-
Ilike
(i.e. ignorecase like) -
IsNotNull
-
IsNull
-
Not
-
Equal
-
NotEqual
-
And
-
Or
这些操作名称可以被看作关键字, 当您使用这些操作名称作为属性对domain类进行查询时可能会产生困惑. 更多信息请查看 dynamic finders 的用户向导.
9.3.1. createCriteria方法
10. createCriteria方法
10.1. 目的
创建并返回一个 Grails' HibernateCriteriaBuilder 的实例, 用作构建标准化查询.
单元测试时在内存中实现的并不是GORM Hibernate, 如`createCriteria()` 返回的是 CriteriaBuilder 的实例,而不是HibernateCriteriaBuilder的实例. |
10.2. 示例
用作构建criteria的方法 (like, and, or, between 等) 可以从HibernateCriteriaBuilder静态导入 (就像这篇文档下面的例子), 或者写为 "c.like(…)", "c.between(…)" 等.
def c = Account.createCriteria()
def results = c.list {
like("holderFirstName", "Fred%")
and {
between("balance", 500, 1000)
eq("branch", "London")
}
maxResults(10)
order("holderLastName", "desc")
}
要想使用分页, 你还需使用另一个查询来检索匹配结果的总数. 一个较好的方法是将分页值作为criteria方法的参数, 就像下面:
def c = Account.createCriteria()
def results = c.list (max: 10, offset: 10) {
like("holderFirstName", "Fred%")
and {
between("balance", 500, 1000)
eq("branch", "London")
}
order("holderLastName", "desc")
}
因为这个查询包括了分页参数 (max and offset), 它会返回一个含有`getTotalCount()`方法、可以得到分页数据总数的 PagedResultList. 两个查询依然执行,但执行得到的结果集和总数组合在`PagedResultList`中.
println "Rendering ${results.size()} Accounts of ${results.totalCount}"
10.3. 描述
Criteria queries are a type-safe, advanced way to query that uses a Groovy builder to construct potentially complex queries. It is a much better alternative to using a StringBuilder to dynamically construct an HQL query. Refer to the user guide section on Criteria for usage instructions.
方法相关:
方法 | 描述 |
---|---|
list |
The default method; returns all matching rows. |
get |
Returns a unique result, i.e. just one row. The criteria has to be formed that way, that it only queries one row. This method is not to be confused with a limit to just the first row. |
scroll |
Returns a scrollable result set |
listDistinct |
If subqueries or associations are used, one may end up with the same row multiple times in the result set. In Hibernate one would do a "CriteriaSpecification.DISTINCT_ROOT_ENTITY". In Grails one can do it by just using this method. |
The listDistinct() method does not work well with the pagination options maxResult and firstResult . If you need distinct results with pagination, we currently recommend that you use HQL. You can find out more information from this blog post.
|
If you invoke the builder with no method name
c { ... }
the list()
method will be invoked automatically. In other words, it’s the equivalent of
c.list { ... }
Below is a node reference for each criterion method:
Node | Description | Example |
---|---|---|
between |
Where the property value is between two distinct values |
|
eq |
Where a property equals a particular value. |
|
eq (case-insensitive) |
A version of eq that supports an optional 3rd Map parameter to specify that the query be case-insensitive. |
|
eqProperty |
Where one property must equal another |
|
gt |
Where a property is greater than a particular value |
|
gtProperty |
Where one property must be greater than another |
|
ge |
Where a property is greater than or equal to a particular value |
|
geProperty |
Where one property must be greater than or equal to another |
|
idEq |
Where an objects id equals the specified value |
|
ilike |
A case-insensitive like expression |
|
in |
Where a property is contained within the specified list of values. Can also be chained with the |
|
isEmpty |
Where a collection property is empty |
|
isNotEmpty |
Where a collection property is not empty |
|
isNull |
Where a property is |
|
isNotNull |
Where a property is not |
|
lt |
Where a property is less than a particular value |
|
ltProperty |
Where one property must be less than another |
|
le |
Where a property is less than or equal to a particular value |
|
leProperty |
Where one property must be less than or equal to another |
|
like |
Equivalent to SQL like expression |
|
ne |
Where a property does not equal a particular value |
|
neProperty |
Where one property does not equal another |
|
order |
Order the results by a particular property |
|
rlike |
Similar to like, but uses a regex. Only supported on Oracle and MySQL. |
|
sizeEq |
Where a collection property’s size equals a particular value |
|
sizeGt |
Where a collection property’s size is greater than a particular value |
|
sizeGe |
Where a collection property’s size is greater than or equal to a particular value |
|
sizeLt |
Where a collection property’s size is less than a particular value |
|
sizeLe |
Where a collection property’s size is less than or equal to a particular value |
|
sizeNe |
Where a collection property’s size is not equal to a particular value |
|
sqlRestriction |
Use arbitrary SQL to modify the resultset |
|
With dynamic finders, you have access to options such as max
, sort
, etc. These are available to criteria queries as well, but they have different names:
Name | Description | Example |
---|---|---|
order(String, String) |
Specifies both the sort column (the first argument) and the sort order (either asc or desc). |
|
firstResult(int) |
Specifies the offset for the results. A value of 0 will return all records up to the maximum specified. |
|
maxResults(int) |
Specifies the maximum number of records to return. |
|
cache(boolean) |
Indicates if the query should be cached (if the query cache is enabled). |
|
Criteria also support the notion of projections. A projection is used to change the nature of the results. For example the following query uses a projection to count the number of distinct branch
names that exist for each Account
:
def c = Account.createCriteria()
def branchCount = c.get {
projections {
countDistinct "branch"
}
}
The following table summarizes the different projections and what they do:
Name | Description | Example |
---|---|---|
property |
Returns the given property in the returned results |
|
distinct |
Returns results using a single or collection of distinct property names |
|
avg |
Returns the average value of the given property |
|
count |
Returns the count of the given property name |
|
countDistinct |
Returns the count of the given property name for distinct rows |
|
groupProperty |
Groups the results by the given property |
|
max |
Returns the maximum value of the given property |
|
min |
Returns the minimum value of the given property |
|
sum |
Returns the sum of the given property |
|
rowCount |
Returns count of the number of rows returned |
|
10.3.1. delete方法
11. delete
11.1. Purpose
Deletes a persistent instance.
11.2. Examples
def book = Book.get(1)
book.delete()
11.3. Description
The delete
method informs the persistence context that a persistent instance should be deleted. Equivalent to the Hibernate delete method.
Calling delete on a transient instance will result in an error
|
Parameters:
-
flush
- If set totrue
the persistent context will be flushed resulting in the instance being deleted immediately. For example:
def book = Book.get(1)
book.delete(flush: true)
11.3.1. discard方法
12. discard
12.1. Purpose
Discards any changes that have been made to a persistent instance.
12.2. Examples
def b = Book.get(1)
b.title = "Blah"
b.discard() // changes won't be applied now
12.3. Description
The discard
method informs the persistence context that the instance should not be saved. The discard
method is equivalent to using Hibernate’s evict method.
Note that this method will not clean or reset the object with the original values; it will just prevent it from being automatically saved by Grails. |
12.3.1. embedded静态属性
13. embedded
13.1. Purpose
Supports embedding domain components into domain classes - otherwise known as composition.
13.2. Examples
Given these domain classes:
class Person {
String name
Country bornInCountry
Country livesInCountry
static embedded = ['bornInCountry', 'livesInCountry']
}
// If you don't want an associated table created for this class, either
// define it in the same file as Person or put Country.groovy under the
// src/main/groovy directory.
class Country {
String iso3
String name
}
Grails will generate a person
database table that looks like:
Column | Type |
---|---|
NAME |
VARCHAR(255) |
BORN_IN_COUNTRY_ISO3 |
VARCHAR(255) |
BORN_IN_COUNTRY_NAME |
VARCHAR(255) |
LIVES_IN_COUNTRY_ISO3 |
VARCHAR(255) |
LIVES_IN_COUNTRY_NAME |
VARCHAR(255) |
13.3. Description
An embedded component does not store its data in its own table as a regular domain class relationship does. Instead, the data is included in the owner’s table. So in the above example, the Country
fields appear in the person
table. This means that queries are faster because there is no join required, but you may end up with duplicate data across tables.
The embedded component class is typically declared in the same source file as the owning class or in its own file under src/main/groovy
. You can put the component class under grails-app/domain
, but if you do so Grails will automatically create a dedicated table for it. Putting the class under src/main/groovy
is usually the best option because you can then share the component across multiple domain classes.
Querying on embedded properties is no different from querying on regular relationships, so you can for example still do:
Person.findAllByBornInCountry(brazil)
Person.findAllByLivesInCountry(france)
where brazil
and france
are instances of Country
.
13.3.1. errors属性
14. errors
14.1. Purpose
An instance of the Spring {springapi}org/springframework/validation/Errors.html[Errors] interface containing data binding and/or validation errors.
14.2. Examples
def user = new User(params)
if (user.validate()) {
// do something with user
}
else {
user.errors.allErrors.each {
println it
}
}
14.3. Description
The errors
property is used by Grails during data binding to store type conversion errors and during validation when calling the validate or save methods.
You can also add your own errors using the {springapi}org/springframework/validation/Errors#reject(java/lang/String).html[reject] and {springapi}org/springframework/validation/Errors#rejectValue(java/lang/String,%20java/lang/String).html[rejectValue] methods:
if (params.password != params.confirm_password) {
user.errors.reject(
'user.password.doesnotmatch',
['password', 'class User'] as Object[],
'[Property [{0}] of class [{1}] does not match confirmation]')
// The following helps with field highlighting in your view
user.errors.rejectValue(
'password',
'user.password.doesnotmatch')
render(view: 'signup', model: [user: user])
}
In the example of reject
above, 'user.password.doesnotmatch'
, is the error code corresponding to a value in grails-app/i18n/message.properties
, \['password', 'class User'\] as Object\[\]
is a Groovy cast from a List
to an Object
array to match the expected signature, and '\[Property \[{0}\] of class \[{1}\] does not match confirmation\]'
is the default mapping string.
In the rejectValue
example, 'password'
is the field in the view to highlight using a <g:hasErrors>
tag and 'user.password.doesnotmatch'
is the i18n error code.
14.3.1. executeQuery方法
15. executeQuery
15.1. Purpose
Executes HQL queries
15.2. Examples
// simple query
Account.executeQuery("select distinct a.number from Account a")
// using with list of parameters
Account.executeQuery("select distinct a.number from Account a " +
"where a.branch = ? and a.created > ?",
['London', lastMonth])
// using with a single parameter and pagination params
Account.executeQuery("select distinct a.number from Account a " +
"where a.branch = ?", ['London'],
[max: 10, offset: 5])
// using with Map of named parameters
Account.executeQuery("select distinct a.number from Account a " +
"where a.branch = :branch",
[branch: 'London'])
// using with Map of named parameters and pagination params
Account.executeQuery("select distinct a.number from Account a " +
"where a.branch = :branch",
[branch: 'London', max: 10, offset: 5])
// same as previous
Account.executeQuery("select distinct a.number from Account a " +
"where a.branch = :branch",
[branch: 'London'], [max: 10, offset: 5])
// tell underlying Hibernate Query object to not attach newly retrieved
// objects to the session, will only save with explicit `save`
Account.executeQuery("select distinct a.number from Account a",
null, [readOnly: true])
// time request out after 18 seconds
Account.executeQuery("select distinct a.number from Account a",
null, [timeout: 18])
// have Hibernate Query object return 30 rows at a time
Account.executeQuery("select distinct a.number from Account a",
null, [fetchSize: 30])
// modify the FlushMode of the Query (default is `FlushMode.AUTO`)
Account.executeQuery("select distinct a.number from Account a",
null, [flushMode: FlushMode.MANUAL])
15.3. Description
The executeQuery
method allows the execution of arbitrary HQL queries. HQL queries can return domain class instances, or `Array`s of specified data when the query selects individual fields or calculated values. The basic syntax is:
Book.executeQuery(String query)
Book.executeQuery(String query, List positionalParams)
Book.executeQuery(String query, List positionalParams, Map metaParams)
Book.executeQuery(String query, Map namedParams)
Book.executeQuery(String query, Map namedParams, Map metaParams)
Parameters:
-
query
- An HQL query -
positionalParams
- AList
of parameters for a positional parameterized query -
namedParams
- AMap
of named parameters for a named parameterized query -
metaParams
- AMap
of pagination parametersmax
or/andoffset
, as well as Hibernate query parametersreadOnly
,fetchSize
,timeout
, andflushMode
15.3.1. executeUpdate方法
16. executeUpdate
16.1. Purpose
Updates the database with DML-style operations
16.2. Examples
Account.executeUpdate("delete Book b where b.pages > 100")
Account.executeUpdate("delete Book b where b.title like ?",
['Groovy In Action'])
Account.executeUpdate("delete Book b where b.author=?",
[Author.load(1)])
Account.executeUpdate("update Book b set b.title='Groovy In Action'" +
"where b.title='GINA'")
Account.executeUpdate("update Book b set b.title=:newTitle " +
"where b.title=:oldTitle",
[newTitle: 'Groovy In Action', oldTitle: 'GINA'])
16.3. Description
GORM does not provide a deleteAll
method as deleting data must be done with caution. To delete data you can use executeUpdate
. The basic syntax is:
Book.executeUpdate(String query)
Book.executeUpdate(String query, List positionalParams)
Book.executeUpdate(String query, Map namedParams)
Parameters:
-
query
- An HQL query with DML-style operations -
positionalParams
- AList
of parameters for a positional parameterized HQL query -
namedParams
- AMap
of parameters for a named parameterized HQL query
16.3.1. exists方法
17. exists
17.1. Purpose
Checks whether an instance exists for the specified id
17.2. Examples
def accountId = ...
if (Account.exists(accountId)) {
// do something
}
17.3. Description
Parameters:
-
id
- The id of the object
17.3.1. fetchMode静态属性
18. fetchMode
18.1. Purpose
Allows the configuration of an associations fetch strategy (eager or lazy)
18.2. Examples
class Author {
String name
static hasMany = [books: Book]
static fetchMode = [books: 'eager']
}
In this example the fetchMode
static property specifies that the book
association should be fetching eagerly
18.3. Description
By default associations in Grails are fetched lazily (each record is read from the database only when it is first accessed from the collection). This makes sense for most cases, however in the case where you have a small number of records to fetch and/or are repeatedly required to load lazy associations (resulting in N+1 queries) it makes sense to use eager fetching.
In the case of eager fetching and a one-to-many association, the instance as well as the association will be initialized when they are loaded (eagerly). However, caution should be observed when using eager fetching, since being too eager can result in your entire database being loaded into memory!
18.3.1. find方法
19. find
19.1. Purpose
Finds the first matching result for the given query or null
if no instance is found
19.2. Examples
// Dan brown's first book
Book.find("from Book as b where b.author='Dan Brown'")
// with a positional parameter
Book.find("from Book as b where b.author=?", ['Dan Brown'])
// with a named parameter
Book.find("from Book as b where b.author=:author", [author: 'Dan Brown'])
// use the query cache
Book.find("from Book as b where b.author='Dan Brown'", [cache: true])
Book.find("from Book as b where b.author=:author",
[author: 'Dan Brown'],
[cache: true])
// query by example
def example = new Book(author: "Dan Brown")
Book.find(example)
// Using where criteria (since Grails 2.0)
Person p = Person.find { firstName == "Bart" }
19.3. Description
The find
method allows querying with Hibernate’s query language HQL and querying by example. The basic syntax is:
Book.find(String query)
Book.find(String query, Collection positionalParams)
Book.find(String query, Collection positionalParams, Map queryParams)
Book.find(String query, Map namedParams)
Book.find(String query, Map namedParams, Map queryParams)
Book.find(Book example)
Book.find(Closure whereCriteria)
Parameters:
-
query
- An HQL query -
positionalParams
- AList
of parameters for a positional parametrized HQL query -
namedParams
- AMap
of named parameters a HQL query -
queryParams
- AMap
of query parameters. Currently, onlycache
is supported -
example
- An instance of the domain class for query by example
19.3.1. findAll方法
20. findAll
20.1. Purpose
Finds all of domain class instances matching the specified query
20.2. Examples
// everything
Book.findAll()
// with a positional parameter
Book.findAll("from Book as b where b.author=?", ['Dan Brown'])
// 10 books from Dan Brown staring from 5th book ordered by release date
Book.findAll("from Book as b where b.author=? order by b.releaseDate",
['Dan Brown'], [max: 10, offset: 5])
// examples with max/offset usage
def query = "from Book as b where b.author='Dan Brown' order by b.releaseDate"
// first 10 books
Book.findAll(query, [max: 10])
// 10 books starting from 5th
Book.findAll(query, [max: 10, offset: 5])
// examples with named parameters
Book.findAll("from Book as b where b.author=:author",
[author: 'Dan Brown'])
Book.findAll("from Book as b where b.author=:author",
[author: 'Dan Brown'], [max: 10, offset: 5])
Book.findAll("from Book as b where b.author in (:authors)",
[authors: ['Dan Brown', 'Jack London']])
// examples with tables join
Book.findAll("from Book as b where not exists " +
"(from Borrow as br where br.book = b)")
// query by example
def example = new Book(author: "Dan Brown")
Book.findAll(example)
// Use where criteria (since Grails 2.0)
def results = Person.findAll {
lastName == "Simpson"
}
def results = Person.findAll(sort:"firstName") {
lastName == "Simpson"
}
20.3. Description
The findAll
method allows querying with Hibernate’s query language HQL and querying by example, returning all matching instances. Pagination can be controlled using the max
and offset
parameters:
Book.findAll("from Book as b where b.author=:author",
[author: 'Dan Brown'], [max: 10, offset: 5])
The findAll
method supports the 2nd level cache:
Book.findAll("from Book as b where b.author=:author",
[author:'Dan Brown'], [max: 10, offset: 5, cache: true])
The basic syntax for the method is:
Book.findAll()
Book.findAll(String query)
Book.findAll(String query, Collection positionalParams)
Book.findAll(String query, Collection positionalParams, Map queryParams)
Book.findAll(String query, Map namedParams)
Book.findAll(String query, Map namedParams, Map queryParams)
Book.findAll(Book example)
Book.findAll(Closure whereCriteria)
Book.findAll(Map queryParams, Closure whereCriteria)
If you have a join in your HQL, the method will return a List of `Array`s containing domain class instances. You may wish to use executeQuery in this case so you have more flexibility in specifying what gets returned.
|
Parameters:
-
query
- An HQL query -
positionalParams
- AList
of parameters for a positional parametrized HQL query -
namedParams
- AMap
of named parameters a HQL query -
queryParams
- AMap
containing parameters max, and/or offset and/or cache -
example
- An instance of the domain class for query by example -
readOnly
-true
if returned objects should not be automatically dirty-checked (simlar toread()
) -
fetchSize
- number of rows fetched by the underlying JDBC driver per round trip -
flushMode
- HibernateFlushMode
override, defaults toFlushMode.AUTO
-
timeout
- query timeout in seconds
20.3.1. findAllBy方法
21. findAllBy*
21.1. Purpose
Dynamic method that uses the properties of the domain class to create query method expressions that return all matching instances of the domain class
21.2. Examples
Given this domain class:
class Book {
String title
Date releaseDate
String author
Boolean paperback
}
The following are all possible:
def results = Book.findAllByTitle("The Shining",
[max: 10, sort: "title", order: "desc", offset: 100])
results = Book.findAllByTitleAndAuthor("The Sum of All Fears", "Tom Clancy")
results = Book.findAllByReleaseDateBetween(firstDate, new Date())
results = Book.findAllByReleaseDateGreaterThanEquals(firstDate)
results = Book.findAllByTitleLike("%Hobbit%")
results = Book.findAllByTitleIlike("%Hobbit%") // ignore case
results = Book.findAllByTitleNotEqual("Harry Potter")
results = Book.findAllByReleaseDateIsNull()
results = Book.findAllByReleaseDateIsNotNull()
results = Book.findAllPaperbackByAuthor("Douglas Adams")
results = Book.findAllNotPaperbackByAuthor("Douglas Adams")
results = Book.findAllByAuthorInList(["Douglas Adams", "Hunter S. Thompson"])
21.3. Description
GORM supports the notion of Dynamic Finders. The findAllBy*
method finds all the results for the given method expression.
Parameters:
-
metaParams
- AM`ap containing pagination parameters `max
,order
,offset
andsort
and metaParametersreadOnly
,timeout
,fetchSize
, andflushMode
Pagination and sorting parameters can be used as the last argument to a dynamic method:
def results = Book.findAllByTitle("The Shining",
[max: 10, sort: "title", order: "desc", offset: 100])
If no items meet the supplied criteria an empty list is returned.
The following operator names can be used within the respective dynamic methods:
-
LessThan
-
LessThanEquals
-
GreaterThan
-
GreaterThanEquals
-
Between
-
Like
-
Ilike
(i.e. ignorecase like) -
IsNotNull
-
IsNull
-
Not
-
NotEqual
-
And
-
Or
-
InList
These operator names can be considered keywords, and you will run into problems when querying domain classes that have one of these names as property names. For more information on dynamic finders refer to the user guide.
21.3.1. findAllWhere方法
22. findAllWhere
22.1. Purpose
Uses named arguments corresponding to property names of the domain class to execute a query returning all matching results.
22.2. Examples
Given the domain class:
class Book {
String title
Date releaseDate
String author
static constraints = {
releaseDate nullable: true
}
}
You can query in the form:
def books = Book.findAllWhere(author: "Stephen King", title: "The Stand")
def unreleasedBooks = Book.findAllWhere(releaseDate: null)
22.3. Description
Parameters:
-
queryParams
- A Map of key/value pairs to be used in the query
22.3.1. findBy方法
23. findBy*
23.1. Purpose
Dynamic method that uses the properties of the domain class to execute a query returning the first matching result.
23.2. Examples
Given the domain class Book
:
class Book {
String title
Date releaseDate
String author
Boolean paperback
}
The following are all possible:
def b = Book.findByTitle("The Shining")
b = Book.findByTitleAndAuthor("The Sum of All Fears", "Tom Clancy")
b = Book.findByReleaseDateBetween(firstDate, new Date())
b = Book.findByReleaseDateGreaterThanEquals(firstDate)
b = Book.findByReleaseDateLessThanEquals(firstDate)
b = Book.findByTitleLike("%Hobbit%")
b = Book.findByTitleIlike("%Hobbit%") // ignores case
b = Book.findByTitleNotEqual("Harry Potter")
b = Book.findByReleaseDateIsNull()
b = Book.findByReleaseDateIsNotNull()
b = Book.findPaperbackByAuthor("Douglas Adams")
b = Book.findNotPaperbackByAuthor("Douglas Adams")
b = Book.findByAuthorInList(["Douglas Adams", "Hunter S. Thompson"])
23.3. Description
GORM supports the notion of Dynamic Finders. The findBy*
method finds the first result for the given method expression.
The following operator names can be used within the respective dynamic methods:
-
LessThan
-
LessThanEquals
-
GreaterThan
-
GreaterThanEquals
-
Between
-
Like
-
Ilike
(i.e. ignorecase like) -
IsNotNull
-
IsNull
-
Not
-
NotEqual
-
And
-
Or
-
InList
The above operator names can be considered keywords, and you will run into problems when querying domain classes that have one of these names used as property names. For more information on dynamic finders refer to the user guide.
23.3.1. findOrCreateBy方法
24. findOrCreateBy*
24.1. Purpose
Dynamic method that uses the properties of the domain class to create query method expressions that return the first result of the query. This method behaves like findBy except that it will never return null
. If a matching instance cannot be found then a new instance will be created and returned, populated with values represented in the query parameters. The difference between this method and findOrSaveBy is that this method will not save a newly created instance where findOrSaveBy does.
24.2. Examples
Given the domain class Book
:
class Book {
String title
String author
}
The following are all possible:
def b = Book.findOrCreateByTitle("The Shining")
b = Book.findOrCreateByTitleAndAuthor("The Sum of All Fears", "Tom Clancy")
b = Book.findByAuthorInList(["Douglas Adams", "Hunter S. Thompson"])
The following are roughly equivalent:
def b = Book.findOrCreateByTitle("The Shining")
def b = Book.findByTitle("The Shining")
if (!b) {
b = new Book(title: "The Shining")
}
24.3. Description
GORM supports the notion of Dynamic Finders. The findOrCreateBy*
method finds the first result for the given method expression.
Because this method potentially creates a new instance and populates properties on that instance, only exact match criteria are allowed. For example, Book.findOrCreateByDate(dateValue) is valid but Book.findOrCreateByDateGreaterThan(dateValue) is not.
|
24.3.1. findOrCreateWhere方法
25. findOrCreateWhere
25.1. Purpose
Uses named arguments that match the property names of the domain class to produce a query that returns the first result. This method behaves just like findWhere except that it will never return null
. If a matching instance cannot be found then a new instance will be created, populated with values from the query parameters and returned. The difference between this method and findOrSaveWhere is that this method will not save a newly created instance where findOrSaveWhere does.
25.2. Examples
Given the domain class:
class Book {
String title
Date releaseDate
String author
static constraints = {
releaseDate nullable: true
}
}
You can query in the form:
def book = Book.findOrCreateWhere(author: "Stephen King", title: "The Stand")
25.3. Description
Parameters:
-
queryParams
- AMap
of key/value pairs to be used in the query. If no matching instance is found then this data is used to initialize a new instance.
25.3.1. findOrSaveBy方法
26. findOrSaveBy*
26.1. Purpose
Dynamic method that uses the properties of the domain class to create query method expressions that return the first result of the query. This method behaves like findBy except that it will never return null
. If a matching instance cannot be found then a new instance will be created, populated with values represented in the query parameters, saved and returned. The difference between this method and findOrCreateBy is that this method will save any newly created instance where findOrCreateBy does not.
26.2. Examples
Given the domain class Book
:
class Book {
String title
String author
}
The following are all possible:
def b = Book.findOrSaveByTitleAndAuthor("The Sum of All Fears", "Tom Clancy")
The following are roughly equivalent:
def b = Book.findOrSaveByTitleAndAuthor("The Sum of All Fears", "Tom Clancy")
def b = Book.findByTitleAndAuthor("The Sum of All Fears", "Tom Clancy")
if (!b) {
b = new Book(title: "The Sum of All Fears", author: "Tom Clancy")
b.save()
}
26.3. Description
GORM supports the notion of Dynamic Finders. The findOrSaveBy*
method finds the first result for the given method expression.
Because this method potentially creates a new instance and populates properties on that instance, only exact match criteria are allowed. For example, Book.findOrSaveByTitle(authorValue) is valid but Book.findOrSaveByAuthorInList(listOfNames) is not.
|
26.3.1. findOrSaveWhere方法
27. findOrSaveWhere
27.1. Purpose
Uses named arguments corresponding to property names of the domain class to execute a query returning the first matching result. This method behaves just like findWhere except that it will never return null
. If a matching instance cannot be found in the database then a new instance is created, populated with values from the query parameters, saved and returned. The difference between this method and findOrCreateWhere is that this method will save a newly created instance where findOrCreateWhere does not.
27.2. Examples
Given the domain class:
class Book {
String title
Date releaseDate
String author
static constraints = {
releaseDate nullable: true
}
}
You can query in the form:
def book = Book.findOrSaveWhere(author: "Stephen King", title: "The Stand")
27.3. Description
Parameters:
-
queryParams
- A Map of key/value pairs to be used in the query. If no matching instance is found then this data is used to initialize a new instance.
27.3.1. findWhere方法
28. findWhere
28.1. Purpose
Uses named arguments corresponding to domain class property names to execute a query returning the first matching result.
28.2. Examples
Given the domain class:
class Book {
String title
Date releaseDate
String author
static constraints = {
releaseDate nullable: true
}
}
You can query in the form:
def book = Book.findWhere(author: "Stephen King", title: "The Stand")
boolean isReleased = Book.findWhere(author: "Stephen King",
title: "The Stand",
releaseDate: null) != null
28.3. Description
Parameters:
-
queryParams
- AMap
of key/value pairs to be used in the query
28.3.1. first方法
29. first
29.1. Purpose
Retrieves the first instance of the domain class.
29.2. Examples
Given the domain class:
class Person {
String firstName
String lastName
Integer age
}
// retrieve the first person ordered by the identifier
def p = Person.first()
// retrieve the first person ordered by the lastName property
p = Person.first(sort: 'lastName')
// retrieve the first person ordered by the lastName property
p = Person.first('lastName')
Parameters:
-
sort
(optional) - The name of the property to sort by
See also:
Note that the first() and last() methods are not supported on domain classes which use a composite primary key. |
29.2.1. get方法
30. get
30.1. Purpose
Retrieves an instance of the domain class for the specified id. null
is returned if the row with the specified id doesn’t exist.
30.2. Examples
def b = Book.get(1)
30.3. Description
Parameters:
-
id
- The id of the object to retrieve
30.3.1. getAll方法
31. getAll
31.1. Purpose
Retrieves a List
of instances of the domain class for the specified ids, ordered by the original ids list. If some of the provided ids are null
or there are no instances with these ids, the resulting List
will have null
values in those positions.
31.2. Examples
// get a list which contains Book instances with ids 2, 1, 3 respectively
def bookList = Book.getAll(2, 1, 3)
// can take a list of ids as only argument, very
// useful when the ids list is calculated in code
def bookList = Book.getAll([1, 2, 3])
// when called without arguments returns list of all objects
def bookList = Book.getAll()
31.3. Description
Parameters:
-
varargs
* - A variable argument list of ids -
ids
* - A list of ids
31.3.1. getDirtyPropertyNames方法
32. getDirtyPropertyNames
32.1. Purpose
Retrieve the names of modified fields in a domain class instance.
32.2. Examples
def b = Book.get(1)
someMethodThatMightModifyTheInstance(b)
def names = b.dirtyPropertyNames
for (name in names) {
def originalValue = b.getPersistentValue(name)
...
}
32.3. Description
This method is useful mostly for audit logging or other work done in a beforeUpdate event callback. Hibernate caches the original state of all loaded instances for dirty checking during a flush and this method exposes the names of modified fields so you can compare them with the current state.
32.3.1. getPersistentValue方法
33. getPersistentValue
33.1. Purpose
Retrieve the original value of a field for a domain class instance.
33.2. Examples
def b = Book.get(1)
someMethodThatMightModifyTheInstance(b)
if (b.isDirty('name')) {
def currentName = b.name
def originalName = b.getPersistentValue('name')
if (currentName != originalName) {
...
}
}
33.3. Description
This method is useful mostly for audit logging or other work done in a beforeUpdate event callback. Hibernate caches the original state of all loaded instances for dirty checking during a flush and this method exposes that data so you can compare it with the current state.
33.3.1. hasErrors方法
34. hasErrors
34.1. Purpose
Check if a domain class instance has validation errors following a call to validate or save, or following data binding
34.2. Examples
def b = new Book(title: "The Shining")
b.validate()
if (b.hasErrors()) {
b.errors.allErrors.each {
println it
}
}
34.2.1. hasMany静态属性
35. hasMany
35.1. Purpose
Defines a one-to-many association between two classes.
35.2. Examples
class Author {
String name
static hasMany = [books: Book]
}
In this example we define a one-to-many relationship between the Author
and Book
classes (one Author
has many `Book`s)
35.3. Description
By default GORM will create a property of type java.util.Set
using the key inside the definition of the hasMany
map. For example consider this definition:
static hasMany = [books: Book]
Here a property of type java.util.Set
called books
will be created within the defining class. These can then be iterated over and manipulated:
def a = Author.get(1)
for (book in a.books) { println book.title }
35.3.1. hasOne静态属性
36. hasOne
36.1. Purpose
Defines a bidirectional one-to-one association between two classes where the foreign key is in the child.
36.2. Examples
class Face {
..
static hasOne = [nose: Nose]
}
class Nose {
Face face
}
In this example we define a one-to-one relationship between the Face
class and the Nose
class
36.3. Description
Use a hasOne
association to store the foreign key reference in child table instead of the parent in a bidirectional one-to-one. The example presented above will generate the following table structure:
create table face (id bigint generated by default as identity (start with 1),
version bigint not null,
primary key (id))
create table nose (id bigint generated by default as identity (start with 1),
version bigint not null,
face_id bigint not null,
primary key (id))
Notice that the foreign key face_id
is stored in the nose
table instead of the face
table as with a normal one-to-one definition without belongsTo
.
36.3.1. ident方法
37. ident
37.1. Purpose
Returns the value of the identity property of the domain class regardless of the name of the identity property itself
37.2. Examples
def b = new Book(title: "The Shining")
b.save()
println b.ident()
37.2.1. instanceOf方法
38. instanceOf
38.1. Purpose
Determines if a domain class instance is an instance of the specified class, resolving the actual class if the instance is a proxy.
38.2. Examples
Given the domain classes:
class Container {
static hasMany = [children: Child]
}
class Child {
String name
static belongsTo = [container: Container]
}
class Thing extends Child {}
class Other extends Child {}
Then you can determine the type of the elements in a Container
's children
collection using
def container = Container.get(id)
for (child in container.children) {
if (child.instanceOf(Thing)) {
// process Thing
}
else if (child.instanceOf(Other)) {
// process Other
}
else {
// handle unexpected type
}
}
38.3. Description
Parameters:
-
clazz
- the type to check
38.3.1. isAttached方法
39. isAttached
39.1. Purpose
39.2. Examples
Book b = session.book
...
if (!b.isAttached()) {
b.attach()
}
39.3. Description
Persistent instances are associated with a persistence Session. A new Session is created for each request and is closed at the end of the request. If an object is read from the session and placed into a web scope such as the HttpSession it is seen as detached, since the persistence session has been closed. You can use the attach method to re-attach an existing persistent instance to the persistence session of the current request.
39.3.1. isDirty方法
40. isDirty
40.1. Purpose
Checks to see if a domain class instance has been modified.
40.2. Examples
def b = Book.get(1)
someMethodThatMightModifyTheInstance(b)
// when called without arguments returns true if any field was changed
if (b.isDirty()) {
// can also check if one field has changed
if (b.isDirty('title')) {
...
}
}
40.3. Description
Parameters:
-
fieldName
- The name of a field to check
40.3.1. last方法
41. last
41.1. Purpose
Retrieves the last instance of the domain class.
41.2. Examples
Given the domain class:
class Person {
String firstName
String lastName
Integer age
}
// retrieve the last person ordered by the identifier
def p = Person.last()
// retrieve the last person ordered by the lastName property
p = Person.last(sort: 'lastName')
// retrieve the last person ordered by the lastName property
p = Person.last('lastName')
Parameters:
-
sort
(optional) - The name of the property to sort by
See also:
Note that the first() and last() methods are not supported on domain classes which use a composite primary key. |
41.2.1. list方法
42. list
42.1. Purpose
Lists instances of the domain class.
42.2. Examples
// list everything
def results = Book.list()
// list 10 results
def results = Book.list(max: 10)
// list 10 results, offset by 100
def results = Book.list(max: 10, offset: 100)
// list 10 results, offset by 100, orderd by title in descending order
def results = Book.list(max: 10, offset: 100, sort: "title", order: "desc")
// list all books, eagerly fetching the authors association
def results = Book.list(fetch: [authors: "eager"])
When max
is specified as a named argument this will return a PagedResultList which has a getTotalCount() method to return the total number of matching records for pagination. Two queries are still run, but they’re run for you and the results and total count are combined in the PagedResultList
.
42.3. Description
Parameters:
-
max
- The maximum number to list -
offset
- The offset from the first result to list from -
order
- How to order the list, either"desc"
or"asc"
-
sort
- The property name to sort by -
ignoreCase
- Whether to ignore the case when sorting. Default istrue
. -
fetch
- The fetch policy for the object’s associations as aMap
-
readOnly
- true if returned objects should not be automatically dirty-checked (simlar toread()
) -
fetchSize
- number of rows fetched by the underlying JDBC driver per round trip -
flushMode
- HibernateFlushMode
override, defaults toFlushMode.AUTO
-
timeout
- query timeout in seconds
42.3.1. listOrderBy方法
43. listOrderBy*
43.1. Purpose
Lists all of the instances of the domain class ordered by the property in the method expression
43.2. Examples
// everything
def results = Book.listOrderByAuthor()
// 10 results
def results = Book.listOrderByTitle(max: 10)
// 10 results, offset from 100
def results = Book.listOrderByTitle(max: 10, offset: 100, order: "desc")
43.3. Description
Parameters:
-
max
- The maximum number to list -
offset
- The offset from the first result to list from -
order
- The order to list by, either"desc"
or"asc"
43.3.1. load方法
44. load
44.1. Purpose
Returns a proxy instance of the domain class for the given identifier.
44.2. Examples
// load a single instance
def b = Book.load(1)
String title = b.title
...
// delete an instance without retrieving it
Book.load(1).delete()
44.3. Description
load
usually returns a proxy for the instance which is initialized on-demand, when a method other than getId()
is invoked. load()
only returns null
if the provided id is null
, so you cannot use it to test for existence. If you provide an id for an instance that doesn’t exist, a proxy will be returned and an exception will be thrown only when you call any instance method other than getId()
.
If there is an existing instance with the same id in the Hibernate session or 2nd-level cache, load() will return that non-proxy instance instead of a proxy.
Parameters:
-
id
- The id of the object to retrieve
44.3.1. lock方法
45. lock
45.1. Purpose
The lock
method obtains a pessimistic lock using an SQL select ... for update
.
45.2. Examples
def book = Book.get(1)
book.lock()
45.3. Description
The lock
method obtains a pessimistic lock on an instance, locking the row in the database with select ... for update
. The lock
method is equivalent to using Hibernate’s LockMode.UPGRADE in combination with the http://docs.jboss.org/hibernate/orm/current/javadocs/org/hibernate/Session#lock(java/lang/Object, org/hibernate/LockMode).html[lock] method.
The lock is automatically released when the transaction commits. In Grails this is typically after an action has finished executing.
Refer to the section on Optimistic and Pessimistic locking in the user guide for info.
45.3.1. mappedBy静态属性
46. mappedBy
46.1. Purpose
The mappedBy
static property allows you to control whether an association is mapped as unidirectional or bidirectional, and which properties form the reverse direction in the case of bidirectional associations.
46.2. Examples
In this example the Airport
class defines two bidirectional one-to-many associations. Without defining mappedBy
this is impossible as GORM cannot differentiate which of the two properties on the other end of the association (either departureAirport
or destinationAirport
in the Route
class) each one-to-many should be associated with.
The solution is to define mappedBy
which tells the Airport
class how each association relates to the other side.
class Airport {
static mappedBy = [outgoingFlights: 'departureAirport',
incomingFlights: 'destinationAirport']
static hasMany = [outgoingFlights: Route,
incomingFlights: Route]
}
class Route {
Airport departureAirport
Airport destinationAirport
}
Separate example for many-to-one relationships:
class Person {
String name
Person parent
static belongsTo = [ supervisor: Person ]
static mappedBy = [ supervisor: "none", parent: "none" ]
static constraints = { supervisor nullable: true }
}
46.3. Description
Grails needs to know whether associations are unidirectional or bidirectional, and in the case of the latter, which properties are involved from both sides. Normally it can infer this information, but when there are multiple properties with the same type its guess can sometimes be wrong. The mappedBy
property is a simple map of property name pairs, where the key is the name of an association property in the current domain class and the value is the name of an association property in the domain class on the other side of the association (which may be itself). The examples above should clarify this.
You can also specify a value of "none" in the map, which indicates that the association is unidirectional. However, this won’t work if you actually have a domain property called "none" in the target class!
46.3.1. mapping静态属性
47. mapping
47.1. Purpose
The mapping
static property configures how GORM maps the domain class to the database. See the section on the ORM DSL in the user guide for more information.
47.2. Examples
class Person {
String firstName
static hasMany = [addresses: Address]
static mapping = {
table 'people'
version false
id column: 'person_id'
firstName column: 'First_Name'
addresses lazy: false
}
}
This example uses the ORM DSL to map the Person
class onto a table called people
47.2.1. mapWith静态属性
48. mapWith
48.1. Purpose
The mapWith
static property adds the ability to control if a domain class is being persisted.
48.2. Examples
class Airport {
static mapWith = "none"
}
In this example the Airport
class will not be persisted to the database.
48.2.1. merge方法
49. merge
49.1. Purpose
Merges a domain class instance back into the current persistence context and returns a new merged instance.
49.2. Examples
def b = new Book(title: "The Shining")
b = b.merge()
49.3. Description
The merge
method is similar in function to the save method, but not in behaviour. merge
allows the merging of "detached" instances such as those stored in the HTTP session. Each persistent instance is associated with a persistence context. A new persistence context is created for each request. The result is that objects stored in the HTTP session lose their persistence context on subsequent requests. In this case you can’t simply call save as the domain class is not associated with a current context.
The merge
method on the other hand lets you merge a detached object’s state back into the current Hibernate session. Unlike the save
method this method returns a new instance of the class representing the re-attached object. In other words you must write code like this:
book = book.merge()
If you don’t use the return value of the merge
method then you still have access to the original unmodified detached instance and you will get errors such as lazy initialization exceptions.
The merge
method is defined in the Hibernate documentation as follows:
Copy the state of the given object onto the persistent object with the same identifier. If there is no persistent instance currently associated with the session, it will be loaded. If the given instance is unsaved, save a copy of and return it as a newly persistent instance.
The merge
method is equivalent to the Hibernate merge method.
Parameters:
-
validate
(optional) - Set tofalse
if validation should be skipped -
flush
(optional) - When set totrue
flushes the persistence context, hence persisting the object immediately
49.3.1. namedQueries静态属性
50. namedQueries
50.1. Purpose
The namedQueries
static property defines named queries. Named queries support the criteria builder syntax. See the section on the Criteria Builder in the user guide for more information.
50.2. Examples
class Publication {
String title
String author
Date datePublished
Integer numberOfPages
static namedQueries = {
recentPublications {
def now = new Date()
gt 'datePublished', now - 365
}
oldPublicationsLargerThan { pageCount ->
def now = new Date()
lt 'datePublished', now - 365
gt 'numberOfPages', pageCount
}
publicationsWithBookInTitle {
like 'title', '%Book%'
}
recentPublicationsWithBookInTitle {
// calls to other named queries...
recentPublications()
publicationsWithBookInTitle()
}
}
}
// get all recent publications...
def recentPubs = Publication.recentPublications.list()
// get up to 10 recent publications, skip the first 5...
def recentPubs = Publication.recentPublications.list(max: 10, offset: 5)
// get a single recent publication
def recentPub = Publication.recentPublications.get()
// get the number of recent publications...
def numberOfRecentPubs = Publication.recentPublications.count()
// get a recent publication with a specific id...
def pub = Publication.recentPublications.get(42)
// get all recent publications where title = 'Some Title'
def pubs = Publication.recentPublications.findAllWhere(title: 'Some Title')
// get a recent publication where title = 'Some Title'
def pub = Publication.recentPublications.findWhere(title: 'Some Title')
// dynamic finders are supported
def pubs = Publication.recentPublications.findAllByTitle('Some Title')
// get all old publications with more than 350 pages
def pubs = Publication.oldPublicationsLargerThan(350).list()
// get all old publications with more than 350 pages
// and the word 'Grails' in the title
def pubs = Publication.oldPublicationsLargerThan(350).findAllByTitleLike('%Grails%')
// get all recent publications with 'Book' in their title
def pubs = Publication.recentPublicationsWithBookInTitle().list()
The list
method on named queries supports the same attributes as the static list
method added to domain classes (sort, order, ignoreCase, fetch etc…). See the list docs for details.
Note that calling something like Publication.recentPublications.get(42) is not the same as calling something like Publication.get(42) . The former will only return a Publication if the Publication with id 42 meets all the criteria defined in the recentPublications named query.
|
Named criteria support listDistinct()
:
class PlantCategory {
Set plants
String name
static hasMany = [plants: Plant]
static namedQueries = {
withPlantsInPatch {
plants {
eq 'goesInPatch', true
}
}
}
}
class Plant {
boolean goesInPatch
String name
}
PlantCategory.withPlantsInPatch.listDistinct()
Named criteria support additional criteria being supplied at invocation time in the form of a criteria closure:
// get all recent publications with author names beginning with Tony or Phil...
def books = Publication.recentPublications {
or {
like 'author', 'Tony%'
like 'author', 'Phil%'
}
}
// get the number of recent publications with
// author names beginning with Tony or Phil...
def numberOfBooks = Publication.recentPublications.count {
or {
like 'author', 'Tony%'
like 'author', 'Phil%'
}
}
Named criteria may be chained together. When criteria are chained together, the query will be generated as if all of the chained criteria had been combined in a single criteria closure.
// recent publications with 'Book' in the title
def books = Publication.recentPublications.publicationsWithBookInTitle.list()
// old publications with more than 500 pages and with 'Book' in the title
def books = Publication.oldPublicationsLargerThan(500)
.publicationsWithBookInTitle
.list()
When a named query involves a domain class relationship and the relationship class defines a named query, that named query may be accessed directly as a method call. An example:
class Author {
static hasMany = [publications: Publication]
static namedQueries = {
authorsWithRecentPublications {
publications {
// invoking a named query defined in the Publication class...
recentPublications()
}
}
}
}
class Publication {
Author author
static namedQueries = {
recentPublications {
def now = new Date()
gt 'datePublished', now - 10
}
}
}
50.2.1. properties属性
51. properties
51.1. Purpose
Allows access to the domain class properties as a Map and is typically used for data binding to perform type conversions allowing properties to be set from request parameters or other Maps.
51.2. Examples
def b = new Book(title: "The Shining")
b.properties = params
b.save()
51.2.1. read方法
52. read
52.1. Purpose
Retrieves an instance of the domain class for the specified id in a read-only state. null
is returned if the row with the specified id doesn’t exist.
52.2. Examples
def b = Book.read(1)
52.3. Description
The read
method is similar to the get method except that automatic dirty detection is disabled. The instance isn’t truly read-only - you can modify it - but if it isn’t explicitly saved but has been modified, it won’t be updated in the database during a flush. But you can explicitly call save()
and it will be updated. There is one exception to this though - any associated collections, for example an Author
's books
collection, will participate in automatic flushing and dirty detection. This is because mapped collections are treated differently than regular properties and they manage their own dirty checking indepenent of the containing domain class.
Parameters:
-
id
- The id of the object to retrieve
52.3.1. refresh方法
53. refresh
53.1. Purpose
Refreshes a domain classes state from the database
53.2. Examples
def b = Book.get(1)
...
b.refresh()
53.3. Description
Equivalent to the Hibernate refresh method.
Re-reads the state of the given instance from the underlying database. It is inadvisable to use this to implement long-running sessions that span many business tasks. However this method is useful in certain special circumstances. For example
-
where a database trigger alters the object state upon insert or update
-
after executing direct SQL (e.g. a bulk update) in the same Session
-
after inserting a Blob or Clob
53.3.1. removeFrom方法
54. removeFrom*
54.1. Purpose
Opposite of the addTo method in that it removes instances from an association.
54.2. Examples
def author = Author.findByName("Stephen King")
def book = author.books.find { it.title == 'The Stand' }
author.removeFromBooks(book)
54.2.1. save方法
55. save
55.1. Purpose
Saves a new domain class instance or updates a modified persistent instance in the database, cascading updates to child instances if required.
55.2. Examples
def b = new Book(title: "The Shining")
b.save()
55.3. Description
The save
method informs the persistence context that an instance should be saved or updated. The object will not be persisted immediately unless the flush
argument is used:
b.save(flush: true)
The save
method returns null
if validation failed and the instance was not persisted, or the instance itself if successful. This lets you use "Groovy truth" (null
is considered false
) to write code like the following:
if (!b.save()) {
b.errors.allErrors.each {
println it
}
}
Parameters:
-
validate
(optional) - Set tofalse
if validation should be skipped -
flush
(optional) - When set totrue
flushes the persistence context, persisting the object immediately and updating theversion
column for optimistic locking -
insert
(optional) - When set totrue
will force Hibernate to do a SQL INSERT; this is useful in certain situations (for example when using assigned ids) and Hibernate cannot detect whether to do an INSERT or an UPDATE -
failOnError
(optional) - When set totrue
thesave
method with throw agrails.validation.ValidationException
if validation fails. This behavior may also be triggered by setting thegrails.gorm.failOnError
property ingrails-app/conf/application.groovy
. If the Config property is set and the argument is passed to the method, the method argument will always take precedence. For more details about the config property and other GORM config options, see the GORM Configuration Options section of The User Guide. -
deepValidate
(optional) - Determines whether associations of the domain instance should also be validated, i.e. whether validation cascades. This istrue
by default - set tofalse
to disable cascading validation.
By default GORM classes are configured for optimistic locking, which is a feature of Hibernate that involves storing an incrementing version in the table. This value is only updated in the database when the Hibernate session is flushed. |
55.3.1. transients静态属性
56. transients
56.1. Purpose
Defines a list of property names that should not be persisted to the database. This is often useful if you have read-only accessor methods ("getters") that are helper methods but get confused as being persistence-related.
56.2. Examples
class Author {
String name
String getUpperCaseName() { name.toUpperCase() }
static transients = ['upperCaseName']
}
Here we have an accessor that takes the name
and converts it to uppercase. It doesn’t make sense to persist this derived value, so we mark it as transient adding its JavaBean property name to the transients
list.
As of Grails 2.0 if there is only a getter or only a setter method, you don’t need to declare the property name of the method in the transients list. Only typed fields and get/set pairs that form a property but shouldn’t be persisted need to go in the transients list. |
56.2.1. validate方法
57. validate
57.1. Purpose
Validates a domain class against the applied constraints (see validation)
57.2. Description
The validate
method validates a domain class based on its defined Constraints. The errors are stored in the errors property of the domain class instance.
The validate
method accepts an optional List
argument which contains the names of the properties to be validated. When a List
of names is specified, only those properties will be validated.
57.3. Examples
def b = new Book(title: "The Shining")
if (!b.validate()) {
b.errors.allErrors.each {
println it
}
}
def a = new Album(artist: "Genesis", title: "Nursery Cryme", releaseDate: 1971)
// only validate title and releaseDate
if (!a.validate(["title", "releaseDate"])) {
a.errors.allErrors.each {
println it
}
}
Parameters:
-
deepValidate
(optional) - Whether associations of the domain instance should also be validated, i.e. whether validation cascades. This istrue
by default; set it tofalse
to disable cascading validation.
57.3.1. where方法
58. where
58.1. Purpose
Defines a new grails.gorm.DetachedCriteria
instance.
58.2. Examples
Basic query:
def query = Person.where {
firstName == "Bart"
}
Person bart = query.find()
Conjunctions/Disjunctions:
def query = Person.where {
(lastName != "Simpson" && firstName != "Fred") || (firstName == "Bart" && age > 9)
}
def results = query.list(sort:"firstName")
Property comparison:
def query = Person.where {
firstName == lastName
}
Querying Associations:
def query = Pet.where {
owner.firstName == "Joe" || owner.firstName == "Fred"
}
Subqueries:
final query = Person.where {
age > avg(age)
}
58.3. Description
The where
method is a powerful new type-safe querying option introduced in Grails 2.0. For more information on using the where
method see the dedicated section on Where Queries and Detached Criteria in the user guide.
58.3.1. whereAny方法
59. where
59.1. Purpose
Defines a new grails.gorm.DetachedCriteria
instance that uses a disjunction (logical OR).
59.2. Examples
Basic query:
def query = Person.whereAny {
firstName == "Bart"
firstName == "Lisa"
}
Person bart = query.find()
59.3. Description
The where
method defaults to a conjunction (logical AND) for the created query. The whereAny
compliments the where
method by allowing the creation of DetachedCriteria
using a disjunction (logical OR).
The where
method is a powerful new type-safe querying option introduced in Grails 2.0. For more information on using the where
method see the dedicated section on Where Queries and Detached Criteria in the user guide.
59.3.1. withCriteria方法
60. withCriteria
60.1. Purpose
Allows inline execution of Criteria queries. See the createCriteria method for reference.
60.2. Return value
If no matching records are found, an empty List is returned.
If a projection is specified:
-
returns a single value if it only contains one field
-
a List in case there are multiple fields in the projection
Otherwise, it will return a List of matched instances of the class calling withCriteria.
60.3. Examples
def results = Book.withCriteria {
def now = new Date()
between('releaseDate', now-7, now)
like('title', '%Groovy%')
}
60.4. Description
The withCriteria
method allows the inline definition of Criteria. Arguments to the [http://grails.github.io/grails-data-mapping/latest/api/grails/orm/HibernateCriteriaBuilder.html] can be passed as the first parameter:
def book = Book.withCriteria(uniqueResult: true) {
def now = new Date()
between('releaseDate', now-7, now)
like('title', '%Groovy%')
}
Parameters:
-
arguments
(optional) - A map of named arguments to be set on the Criteria instance -
closure
- A closure that defines the criteria
60.4.1. withNewSession方法
61. withNewSession
61.1. Purpose
Provides a way to execute code within the context of a new Hibernate session which shares the same transactional (JDBC Connection) resource as the currently bound session.
61.2. Examples
Book.withNewSession { session ->
// do work
}
61.3. Description
Parameters:
-
closure
- A closure which accepts aSession
argument
61.3.1. withSession方法
62. withSession方法
62.1. 目的
提供 Hibernate Session
对象的底层权限
62.2. 示例
Book.withSession { session ->
session.clear()
}
62.3. 描述
参数:
-
closure
- 接受Session
参数的闭包
62.3.1. withTransaction方法
63. withTransaction方法
63.1. 目的
允许编程式事务使用Spring的事务抽象。
63.2. 示例
Account.withTransaction { status ->
def source = Account.get(params.from)
def dest = Account.get(params.to)
int amount = params.amount.toInteger()
if (source.active) {
source.balance -= amount
if (dest.active) {
dest.amount += amount
}
else {
status.setRollbackOnly()
}
}
}
命名参数可以根据需要作为参数传递,以控制事务的属性。
//Map的key必须与 org.springframework.transaction.support.DefaultTransactionDefinition 中的属性一致。
Account.withTransaction([propagationBehavior: TransactionDefinition.PROPAGATION_REQUIRES_NEW,
isolationLevel: TransactionDefinition.ISOLATION_REPEATABLE_READ]) {
// ...
}
63.3. 描述
withTransaction
方法接受一个含有 TransactionStatus 参数的闭包。
TransactionStatus
对象可以被用来以编程方式控制事务回滚。
详情请参见 Programmatic Transactions 。