Reference
Domain Classes
addTo
addTo* 方法
目的
为一对多或多对多关系添加域类关系,其中以该方法的后缀的属性名称表示关系的映射属性
示例
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()
描述
`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`构造函数来创建实例.
attach
attach方法
目的
将“detached”状态的domain类实例与当前Hibernate会话关联在一起
示例
def b = Book.get(1)
b.title = "Blah"
b.save(flush:true)
b.discard()
...
if (!b.isAttached()) {
b.attach()
}
描述
Hibernate在持久会话session中管理持久实例。每个请求创建新会话,并在请求结束时关闭。如果一个对象是从会话中获取并放置到一个Web范围如HttpSession,状态将变为“detached”,一旦Hibernate会话session关闭和废弃。你可以使用` attach() `方法重新附加现有的持久化实例对当前请求的Hibernate session。
belongsTo
belongsTo 静态属性
目的
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.
示例
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.
描述
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.
clearErrors
clearErrors方法
目的
重装domain类的errors列表 . 如果domain类包含绑定或验证的错误时,将非常有用。这些错误可以通过程序纠正。如果不清除错误,验证将继续失败。
示例
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()
}
constraints
constraints
Purpose
Allows the definition of declarative validation constraints. See validation in the user guide.
Examples
class Book {
String title
Author author
static constraints = {
title blank: false, size: 5..150
author nullable: true
}
}
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
// ...
}
count
count方法
目的
返回domain类对应的表的记录总数
示例
int bookCount = Book.count()
countBy
countBy*方法
目的
使用domain类中的属性来查询相应记录的数目的动态方法
示例
这是`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()
描述
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 的用户向导.
createCriteria
createCriteria方法
目的
创建并返回一个 Grails' HibernateCriteriaBuilder 的实例, 用作构建标准化查询.
单元测试时在内存中实现的并不是GORM Hibernate, 如`createCriteria()` 返回的是 CriteriaBuilder 的实例,而不是HibernateCriteriaBuilder的实例. |
示例
用作构建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}"
描述
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 |
|
delete
delete
Purpose
Deletes a persistent instance.
Examples
def book = Book.get(1)
book.delete()
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)
discard
discard
Purpose
Discards any changes that have been made to a persistent instance.
Examples
def b = Book.get(1)
b.title = "Blah"
b.discard() // changes won't be applied now
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. |
embedded
embedded
Purpose
Supports embedding domain components into domain classes - otherwise known as composition.
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) |
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
.
errors
errors
Purpose
An instance of the Spring {springapi}org/springframework/validation/Errors.html[Errors] interface containing data binding and/or validation errors.
Examples
def user = new User(params)
if (user.validate()) {
// do something with user
}
else {
user.errors.allErrors.each {
println it
}
}
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.
executeQuery
executeQuery
Purpose
Executes HQL queries
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])
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
executeUpdate
executeUpdate
Purpose
Updates the database with DML-style operations
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'])
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
exists
exists
Purpose
Checks whether an instance exists for the specified id
Examples
def accountId = ...
if (Account.exists(accountId)) {
// do something
}
Description
Parameters:
-
id
- The id of the object
fetchMode
fetchMode
Purpose
Allows the configuration of an associations fetch strategy (eager or lazy)
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
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!
find
find
Purpose
Finds the first matching result for the given query or null
if no instance is found
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" }
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
findAll
findAll
Purpose
Finds all of domain class instances matching the specified query
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"
}
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
findAllBy
findAllBy*
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
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"])
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.
findAllWhere
findAllWhere
Purpose
Uses named arguments corresponding to property names of the domain class to execute a query returning all matching results.
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)
Description
Parameters:
-
queryParams
- A Map of key/value pairs to be used in the query
findBy
findBy*
Purpose
Dynamic method that uses the properties of the domain class to execute a query returning the first matching result.
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"])
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.
findOrCreateBy
findOrCreateBy*
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.
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")
}
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.
|
findOrCreateWhere
findOrCreateWhere
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.
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")
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.
findOrSaveBy
findOrSaveBy*
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.
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()
}
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.
|
findOrSaveWhere
findOrSaveWhere
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.
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")
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.
findWhere
findWhere
Purpose
Uses named arguments corresponding to domain class property names to execute a query returning the first matching result.
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
Description
Parameters:
-
queryParams
- AMap
of key/value pairs to be used in the query
first
first
Purpose
Retrieves the first instance of the domain class.
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. |
get
get
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.
Examples
def b = Book.get(1)
Description
Parameters:
-
id
- The id of the object to retrieve
getAll
getAll
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.
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()
Description
Parameters:
-
varargs
* - A variable argument list of ids -
ids
* - A list of ids
getDirtyPropertyNames
getDirtyPropertyNames
Purpose
Retrieve the names of modified fields in a domain class instance.
Examples
def b = Book.get(1)
someMethodThatMightModifyTheInstance(b)
def names = b.dirtyPropertyNames
for (name in names) {
def originalValue = b.getPersistentValue(name)
...
}
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.
getPersistentValue
getPersistentValue
Purpose
Retrieve the original value of a field for a domain class instance.
Examples
def b = Book.get(1)
someMethodThatMightModifyTheInstance(b)
if (b.isDirty('name')) {
def currentName = b.name
def originalName = b.getPersistentValue('name')
if (currentName != originalName) {
...
}
}
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.
hasErrors
hasErrors
Purpose
Check if a domain class instance has validation errors following a call to validate or save, or following data binding
Examples
def b = new Book(title: "The Shining")
b.validate()
if (b.hasErrors()) {
b.errors.allErrors.each {
println it
}
}
hasMany
hasMany
Purpose
Defines a one-to-many association between two classes.
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)
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 }
hasOne
hasOne
Purpose
Defines a bidirectional one-to-one association between two classes where the foreign key is in the child.
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
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
.
ident
ident
Purpose
Returns the value of the identity property of the domain class regardless of the name of the identity property itself
Examples
def b = new Book(title: "The Shining")
b.save()
println b.ident()
instanceOf
instanceOf
Purpose
Determines if a domain class instance is an instance of the specified class, resolving the actual class if the instance is a proxy.
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
}
}
Description
Parameters:
-
clazz
- the type to check
isAttached
isAttached
Purpose
Examples
Book b = session.book
...
if (!b.isAttached()) {
b.attach()
}
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.
isDirty
isDirty
Purpose
Checks to see if a domain class instance has been modified.
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')) {
...
}
}
Description
Parameters:
-
fieldName
- The name of a field to check
last
last
Purpose
Retrieves the last instance of the domain class.
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. |
list
list
Purpose
Lists instances of the domain class.
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
.
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
listOrderBy
listOrderBy*
Purpose
Lists all of the instances of the domain class ordered by the property in the method expression
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")
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"
load
load
Purpose
Returns a proxy instance of the domain class for the given identifier.
Examples
// load a single instance
def b = Book.load(1)
String title = b.title
...
// delete an instance without retrieving it
Book.load(1).delete()
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
lock
lock
Purpose
The lock
method obtains a pessimistic lock using an SQL select ... for update
.
Examples
def book = Book.get(1)
book.lock()
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.
mappedBy
mappedBy
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.
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 }
}
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!
mapping
mapping
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.
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
mapWith
mapWith
Purpose
The mapWith
static property adds the ability to control if a domain class is being persisted.
Examples
class Airport {
static mapWith = "none"
}
In this example the Airport
class will not be persisted to the database.
merge
merge
Purpose
Merges a domain class instance back into the current persistence context and returns a new merged instance.
Examples
def b = new Book(title: "The Shining")
b = b.merge()
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
namedQueries
namedQueries
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.
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
}
}
}
properties
properties
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.
Examples
def b = new Book(title: "The Shining")
b.properties = params
b.save()
read
read
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.
Examples
def b = Book.read(1)
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
refresh
refresh
Purpose
Refreshes a domain classes state from the database
Examples
def b = Book.get(1)
...
b.refresh()
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
removeFrom
removeFrom*
Purpose
Opposite of the addTo method in that it removes instances from an association.
Examples
def author = Author.findByName("Stephen King")
def book = author.books.find { it.title == 'The Stand' }
author.removeFromBooks(book)
save
save
Purpose
Saves a new domain class instance or updates a modified persistent instance in the database, cascading updates to child instances if required.
Examples
def b = new Book(title: "The Shining")
b.save()
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. |
transients
transients
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.
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. |
validate
validate
Purpose
Validates a domain class against the applied constraints (see validation)
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.
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.
where
where
Purpose
Defines a new grails.gorm.DetachedCriteria
instance.
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)
}
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.
whereAny
where
Purpose
Defines a new grails.gorm.DetachedCriteria
instance that uses a disjunction (logical OR).
Examples
Basic query:
def query = Person.whereAny {
firstName == "Bart"
firstName == "Lisa"
}
Person bart = query.find()
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.
withCriteria
withCriteria
Purpose
Allows inline execution of Criteria queries. See the createCriteria method for reference.
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.
Examples
def results = Book.withCriteria {
def now = new Date()
between('releaseDate', now-7, now)
like('title', '%Groovy%')
}
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
withNewSession
withNewSession
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.
Examples
Book.withNewSession { session ->
// do work
}
Description
Parameters:
-
closure
- A closure which accepts aSession
argument
withSession
withSession方法
目的
提供 Hibernate Session
对象的底层权限
示例
Book.withSession { session ->
session.clear()
}
描述
参数:
-
closure
- 接受Session
参数的闭包
withTransaction
withTransaction方法
目的
允许编程式事务使用Spring的事务抽象。
示例
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]) {
// ...
}
描述
withTransaction
方法接受一个含有 TransactionStatus 参数的闭包。
TransactionStatus
对象可以被用来以编程方式控制事务回滚。
详情请参见 Programmatic Transactions 。
Constraints
attributes
Unresolved directive in reference.adoc - include::ref/Constraints/attributes.adoc[]
bindable
Unresolved directive in reference.adoc - include::ref/Constraints/bindable.adoc[]
blank
blank 约束
目的
验证字符串值是否为空白字符串值''.
示例
login blank: false
描述
如果字符串值不能为空字符串值'',则设置为“false”.
错误代码 Code: className.propertyName.blank
注意:如果字符串为“null”,它将不能通过验证“blank: true”。在这种情况下,设置 nullable约束为`true`。
creditCard
creditCard约束
目的
验证字符串值是有效的信用卡号
示例
cardNumber creditCard: true
描述
如果字符串必须是信用卡号码,则设置为“true”。内部使用`org.apache.commons.validator.CreditCardValidator`类。
错误代码 Code: className.propertyName.creditCard.invalid
email约束
目的
验证字符串值是有效的电子邮件地址.
示例
homeEmail email: true
描述
如果字符串值必须是电子邮件地址,则设置为“true”。内部使用`org.apache.commons.validator.EmailValidator`类。
错误代码 Code: className.propertyName.email.invalid
inList
inList约束
目的
验证值是否在约束值的范围或集合中.
示例
name inList: ["Joe", "Fred", "Bob"]
描述
约束一个值,使其必须包含在给定的列表中.
这种约束的影响参见 schema generation.
错误代码 Code: className.propertyName.not.inList
matches
matches约束
目的
验证字符串值是否与给定的正则表达式匹配.
例子
login matches: "[a-zA-Z]+"
例子
对字符串值应用正则表达式.
错误代码 Code: className.propertyName.matches.invalid
max
max约束
目的
确保值不超过最大值.
示例
age max: new Date()
price max: 999F
描述
可以设置最大值为任意实现接口implements `java.lang.Comparable`的类对象.值的类型必须和属性的类型相同。
注意: 如果值为`java.util.Date`类型,约束的值只在评估执行时创建一次。
class User {
...
static constraints = {
//此日期对象在评估约束执行时创建,而不是每次验证用户类的实例时创建
birthDate max: new Date()
}
}
这种约束的影响参见 schema generation.
错误代码 Code: className.propertyName.max.exceeded
maxSize
maxSize约束
目的
确保值的大小不超过最大值.
示例
children maxSize: 25
min约束
目的
确保值不低于最小值
示例
age min: new Date()
price min: 0F
描述
可以设置最小值为任意实现接口implements `java.lang.Comparable`的类对象.值的类型必须和属性的类型相同。
注意: 如果值为`java.util.Date`类型,约束的值只在评估执行时创建一次。
class User {
...
static constraints = {
//此日期对象在评估约束执行时创建,而不是每次验证用户类的实例时创建
age min: new Date()
}
}
这种约束的影响参见 schema generation.
错误代码 Code: className.propertyName.min.notmet
minSize
minSize约束
目的
确保值的大小不低于最小值.
示例
children minSize: 25
描述
设置集合或数字属性的最小大小.
这种约束的影响参见 schema generation.
错误代码 Code: className.propertyName.minSize.notmet
notEqual
notEqual约束
目的
确保属性不等于指定值
示例
username notEqual: "Bob"
描述
错误代码 Code: className.propertyName.notEqual
nullable
nullable约束
目的
允许属性设置为“null”,默认设置为`false`
示例
age nullable: true
描述
如果属性允许设置为`null`值,设置为`true`.对应数据库会设置为allow null.
这种约束的影响参见 schema generation.
错误代码 Code: className.propertyName.nullable
注意:web请求表单提交,对于没有输入值的输入框, 属性的值为空白字符串,不是`null`.
range
range约束
目的
使用Groovy的Range类型以确保属性值在指定范围内
示例
age range: 18..65
描述
Groovy的Range类型可以包含数字类型`IntRange`或日期类型,或者任意类型(实现了implements Comparable
,并且提供`next` 和 previous
两个方法)
这种约束的影响参见 schema generation.
错误代码 Code: className.propertyName.range.toosmall
或者 className.propertyName.range.toobig
scale
scale约束
目的
设置为浮点数字所需的尺度(即小数点右边的位数)
示例
salary scale: 2
描述
设置为浮点数字所需的尺度(即小数点右边的位数)。这个限制适用于以下类型的属性: java.lang.Float
, java.lang.Double
, and java.math.BigDecimal
(及其子类)。当验证被调用时,此约束确定该数字是否包含比刻度允许更多的非零小数位置。如果是这样,它将数字循环到由刻度允许的最大小数位数。此约束不会生成验证错误消息。
这种约束的影响参见 schema generation.
无错误代码 Code: N/A
size
size约束
目的
使用Groovy的Range类型限制集合、数字或字符串长度的大小
示例
children size: 5..15
描述
设置集合的大小、数字属性或字符串长度.
这种约束的影响参见 schema generation.
错误代码 Code: className.propertyName.size.toosmall
或 className.propertyName.size.toobig
unique
unique约束
目的
在数据库级别将属性限制为唯一,会在数据库表上建立唯一性约束
示例
username unique: true
描述
如果属性必须是唯一的,则设置为`true`。这是一个持久的调用,将查询数据库。
警告:有可能会发生(虽然不可能在实践中出现)唯一性验证通过,但随后的保存失败。场景为:如果同时有其他的进程修改数据库在GORM检查的同时,那么相关调用将失败。为了防止这一点的唯一方法就是使用`SERIALIZABLE`隔离级别的事务,但对性能影响很大。 还可以通过定义将字段包含为参数值来定义多列的“唯一”约束。如果有另一个字段,请指定它的名称,但如果有多于一个字段,请使用列表List,例如:
示例:
group unique: 'department'
这个例子中,每个`department`部门内`group`名是唯一的,不同的部门可以存在重名的组。组名本身不是唯一的.
示例2:
username(unique: ['group', 'department'])
这个例子要求在一个部门和组内的 `username`用户名是唯一的。不同的部门或不同的组内可以存在重名的情况
这种约束的影响参见 schema generation.
错误代码 Code: className.propertyName.unique
url
url约束
Purpose
验证字符串值是有效URL.
例子
homePage url: true
描述
如果一个字符串是 URL设置为 true
.
错误代码 Code: className.propertyName.url.invalid
validator
validator约束
目的
为字段添加自定义约束.
例子
even validator: {
return (it % 2) == 0
}
// is equivalent to
even validator: { val ->
return (val % 2) == 0
}
// 闭包的两个参数,第一个是属性值,第二个是实例对象
password1 validator: { val, obj ->
obj.password2 == val
}
//闭包的三个参数,第一个是属性值,第二个是实例对象 ,第三个是errors错误对象
password1 validator: { val, obj, errors ->
if (!(obj.password2 == val)) errors.rejectValue('password1', 'noMatch')
}
// Examples 传递参数到i18n的消息中 message.properties
// Example 1: 使用隐式的参数 0 (属性名称)
class Person {
String name
static constraints = {
name validator: {
if (!it) return ['entryMissing']
}
}
// 上述例子将调用如下消息:
// person.name.entryMissing=Please enter a name in the field {0}
// Example 2: 使用隐式的参数 0 (属性名称) and 参数2 (属性值)
class Person {
Integer yearOfBirth
static constraints = {
yearOfBirth validator: {
if (yearOfBirth>2013) return ['yearTooBig']
}
}
// 上述例子将调用如下消息:
// person.yearOfBirth.yearTooBig=The value {2} entered in the field {0} is not valid because it lies in the future.
// Example 3: 复杂
class Astronaut {
Integer yearOfBirth
Integer yearOfFirstSpaceTravel
yearOfFirstSpaceTravel validator: { val, obj ->
if (val < obj.yearOfBirth) ['datePriorTo', val.toString(), obj.yearOfBirth]
else if (val < (obj.yearOfBirth+18)) ['maybeABitTooYoung', val-obj.yearOfBirth]
}
}
// 各自的消息
// 注意,参数3是属性值转换toString方法,以避免不必要的格式,如前所述.
astronaut.yearOfFirstSpaceTravel.datePriorTo=The value {3} entered for the year of the first space travel is prior to the year of birth ({4}). Please correct the value.
astronaut.yearOfFirstSpaceTravel.maybeABitTooYoung={3} years seems a bit young for travelling to space, dude!
描述
自定义验证通过闭包来实现,并接受3个参数。
如果闭包接受0个或1个参数,参数值将被验证("it" 是闭包的第0个参数).
如果接受2个参数,第一个是值,第二个是domain类的实例.
如果接受3个参数,第一个是值,第二个是domain类的实例,第3个是Spring Errors
对象。
闭包可以返回:
-
null
或者true
(或者没有返回值) 说明值是有效的。 -
false
说明值是无效的并使用默认的消息代码 -
string 指明错误代码增加到 "classname.propertyName."字符串后,从而获取错误消息.如果无法解决特定字段消息,错误代码本身将使用全局错误消息.
-
一个包含字符串的list列表,后面是参数. 将使用 i18n/message.properties 中的定义组织消息。 参数的映射如下:参数0到2将自动映射到0:属性名称,1:类名,2:属性值。在参数3开始映射附加参数。
请注意,在最后的错误信息,该属性的标签将使用在message.properties文件中的定义。否则,将使用类中定义的属性名称。
当显式传递的错误代码,通常不需要使用"return"关键词返回的错误代码,因为如果从闭包返回时,它将检查错误是否已连接到错误的对象。
这可以看作特例,闭包中传递了第3个参数,预计错误的对象`Errors`将直接更新。
widget
Unresolved directive in reference.adoc - include::ref/Constraints/widget.adoc[]
Database Mapping
autoImport
autoImport映射
目的
在当前domain类的 HQL 查询中 Enable/disable auto import .
示例
class Book {
...
static mapping = {
autoImport false
}
}
描述
Usage: autoImport(boolean)
默认情况下,domain类在HQL查询是auto-imported,因此,您不需要指定整个类名(包括包),但是,如果在不同的包中有重复的domain类名,则名称不再是唯一的,会造成`org.hibernate.DuplicateMappingException`错误。禁用其中一个或两个domain类的自动导入来修复此错误,设置`autoImport` 为 false
。记住,你需要在HQL的查询中使用完全的domain类名引用。
static mapping = {
autoImport false
}
autoTimestamp
autoTimestamp映射
目的
设置为false时,关闭自动时间戳功能
示例
class Book {
...
static mapping = {
autoTimestamp false
}
}
描述
Usage: autoTimestamp(boolean)
缺省当domain类中的属性名为 dateCreated
和 lastUpdated
, GORM会在数据库自定维护这两个字段。可以通过设置为false关闭此功能:
static mapping = {
autoTimestamp false
}
batchSize
batchSize映射
目的
定制多少结果获取在懒加载执行时.
示例
class Book {
...
static mapping = {
batchSize 10
}
}
描述
Usage: batchSize(integer)
针对`Author` 类拥有多个 Book`实例的延迟加载关联。GORM将执行一条查询`Author`和附加查询多条关联`Book`实例。
这就是著名的N+1问题,通常使用表连接处理,然而,关联表会消耗性能。
批量加载是针对延迟加载的一个优化方案,例如设置`batchSize`为10,并且当前作者拥有30本书.
系统将执行四次获取(一次`Author
,3次批量10对象的获取),替换之前的31此获取:
static mapping = {
batchSize 10
}
也可以在关联端设置 batchSize
:
class Author {
static hasMany = [books: Book]
static mapping = {
books batchSize: 10
}
}
cache
cache映射
目的
配置domain类支持Hibernate二级缓存.
示例
class Book {
...
static mapping = {
cache true
}
}
示例
Usage: cache(boolean/string/map)
参数:
-
cache
- 缓存使用. 可被设置为read-only
,read-write
,nonstrict-read-write
或transactional
-
include
(optional) - 是否包含非延迟加载关联项。可被设置为all
或non-lazy
示例:
static mapping = {
cache true
}
这将配置domain类使用’read-write' 缓存,需要提前确认配置的缓存政策被允许使用(缓存引擎提供支持):
static mapping = {
cache 'transactional'
}
or
static mapping = {
cache usage: 'read-only', include: 'non-lazy'
}
也可以在关联端设置缓存:
class Author {
static hasMany = [books: Book]
static mapping = {
books cache: true // or 'read-write' etc.
}
}
参照文档获取更多信息 Caching .
cascade
cascade映射
目的
配置关联项的级联操作
示例
class Author {
static hasMany = [books: Book]
static mapping = {
books cascade: 'all-delete-orphan'
}
}
描述
Usage: association_name(cascade:string)
参数:
-
cascade
- 定义级联行为. 可被设置为一个或多个 (comma-separated逗号分隔) ofall
,merge
,save-update
,delete
,lock
,refresh
,evict
,replicate
orall-delete-orphan
(one-to-many只支持一对多关系).
默认GORM会配置"belongs to"属性对象的级联操作为"all",示例如下:
class Book {
static belongsTo = [author: Author]
}
class Author {
static hasMany = [books: Book]
}
所以的持久化操作都会从`Author` 类传递到 Book
类。作者对象删除将导致关联的图书全部删除。
如果关联没有设置所有者("belongs to" 关联关系):
class Book {
}
class Author {
static hasMany = [books: Book]
}
GORM默认使用"save-update"级联策略. 作者对象删除时,不会级联删除关联的图书。 使用`cascade`参数自定义级联行为:
class Author {
static hasMany = [books: Book]
static mapping = {
books cascade: 'all-delete-orphan'
}
}
以上配置,`Book`当成为孤儿(orphaned) 时会被删除。.
参看文档获取更多信息 transitive persistence .
column
column映射
目的
自定义数据列的设置
示例
static mapping = {
currency column: "currency", sqlType: "char", length: 3
}
描述
Usage: property_name(map)
Arguments:
-
column
- The name of the column as a String -
sqlType
(optional) - The underlying SQL type -
enumType
(optional) - The enum type in for type-safe Enum properties. Eitherordinal
orstring
. -
index
(optional) - The index name -
unique
(optional) - Whether it is unique -
length
(optional) - The length of the column -
precision
(optional) - The precision of the column -
scale
(optional) - The scale of the column -
comment
(optional) - The comment for the column (used to create the table DDL) -
defaultValue
(optional) - The database default value
By default GORM uses the property name and type to determine how to map a property in the database. For example a String
property is typically mapped as a varchar(255)
column. You can customize these with column configuration arguments:
static mapping = {
currency column: "currency", sqlType: "char", length: 3
}
If you use a Hibernate type that requires multiple column definitions you can use the column
method to define each column:
static mapping = {
amount type: MonetaryUserType, {
column name: "value"
column name: "currency", sqlType: "char", length: 3
}
}
Note that if you have a static method that is the same name as one of your properties you are trying to configure or you use the same property name as one of the static GORM methods this can lead to conflicts. To avoid this scenario scope the mapping configuration using the delegate
property:
static mapping = {
delegate.currency column: "currency", sqlType: "char", length: 3
}
comment
comment映射
目的
设置生成表创建sql(DDL)时使用的注释.
示例
class Book {
static mapping = {
comment "your comment here"
}
}
描述
Usage: comment(string)
参数:
-
comment
- 具体注释
discriminator
discriminator
Purpose
Customizes the discriminator column used in table-per-hierarchy inheritance mapping. Has no effect when using table-per-subclass inheritance mapping.
Examples
class Content {
...
}
class PodCast extends Content {
...
static mapping = {
discriminator "audio"
}
}
Description
Usage: discriminator(string/map)
Arguments:
-
column
(optional) - The column name to store the discriminator -
value
- The value to use for the discriminator -
formula
(optional) - an SQL expression that is executed to evaluate the type of class. Use this orcolumn
but not both -
type
(optional defaults to string) - the Hibernate type, used for the where clause condition to know if it needs to wrap it with '
By default when mapping inheritance Grails uses a single-table model where all classes share the same table. A discriminator column is used to determine the type for each row, by default the full class name. You can use the discriminator
method to customize what’s stored:
class Content {
...
}
class PodCast extends Content {
...
static mapping = {
discriminator "audio"
}
}
You can also customize the discriminator column name:
class Content {
...
static mapping = {
discriminator column: "content_type"
}
}
class PodCast extends Content {
...
static mapping = {
discriminator value: "audio"
}
}
Or you can use a formula:
class Content {
...
static mapping = {
discriminator value: "1", type: "integer",
formula: "case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end",
}
}
class PodCast extends Content {
...
static mapping = {
discriminator value: "0"
}
}
dynamicInsert
dynamicInsert
Purpose
Whether to dynamically build INSERT
queries
Examples
class Book {
...
static mapping = {
dynamicInsert true
}
}
Description
Usage: dynamicInsert(boolean)
By default Hibernate generates all queries at startup and caches them. This helps performance since insert, update, and delete queries don’t have to be dynamically generated at runtime. However, there are certain circumstances where dynamic queries are useful.
For example if you were using custom UserType
to hash passwords, every time an UPDATE occurred, the password would get re-hashed. The dynamicInsert
method lets you turn off the dynamic creation of queries that uses only the properties that are needed to perform the insert.
dynamicUpdate
dynamicUpdate
Purpose
Whether to dynamically build UPDATE
queries
Examples
class Book {
...
static mapping = {
dynamicUpdate true
}
}
Description
Usage: dynamicUpdate(boolean)
By default Hibernate generates all queries at startup and caches them. This helps performance since insert, update, and delete queries don’t have to be dynamically generated at runtime. However, there are certain circumstances where dynamic queries are useful.
For example if you were using custom UserType
to hash passwords, every time an UPDATE occurred, the password would get re-hashed. The dynamicUpdate
method lets you turn off the dynamic creation of queries that uses only the properties that are needed to perform the update.
fetch
fetch
Purpose
Configures the fetching behavior of an association.
Examples
class Author {
static hasMany = [books: Book]
static mapping = {
books fetch: 'join'
}
}
Description
Usage: association_name(fetch:string)
Arguments:
-
fetchStrategy
- The fetching strategy to use. Eitherjoin
orselect
.
By default GORM assumes fetching of associations is done using a SELECT
when the association is accessed. If you prefer that the association be fetched eagerly at the same time then you can override the behavior:
class Author {
static hasMany = [books: Book]
static mapping = {
books fetch: 'join'
}
}
Here the books
association will be fetched using a join at the same time the author is retrieved, for example:
def author = Author.get(1)
// the books collection is pre-initialized - no risk of lazy loading exceptions
Note that excessive use of joins can be a performance bottleneck. See the section on Eager vs Lazing Fetching in the user guide.
id
id
Purpose
Customizes the way the identifier for a domain class is generated
Examples
class Book {
static mapping = {
id generator: 'hilo',
params: [table: 'hi_value', column: 'next_value', max_lo: 100]
}
}
Description
Usage: id(map)
Arguments:
-
generator
(optional) - The name of the generator to use. Can beincrement
,identity
,sequence
,hilo
,seqhilo
,uuid
,guid
,native
,assigned
,select
,foreign
orsequence-identity
. See Hibernate reference documentation for more information. -
composite
(optional) - Takes a list of property names to use that form the composite identifier -
name
(optional) - The property name to use as the identifier -
params
(optional) - Any parameters to pass to the defined identity generator -
column
(optional) - The column name to map the identifier to. The remaining column definition properties are also available.
By default GORM uses the native strategy to generate a database identifier for each entity (typically an auto-incrementing column or a sequence). You can alter this with the id
methods generator
argument:
static mapping = {
id generator: 'hilo',
params: [table: 'hi_value', column: 'next_value', max_lo: 100]
}
You can also use the method to define a composite identifier:
static mapping = {
id composite: ['title', 'author']
}
or change the name of the property that defines the identifier:
static mapping = {
id name: 'title'
}
You can also alter the column definition:
static mapping = {
id column: 'book_id', type: 'integer'
}
See the section on Custom Database Identity in the user guide for more information.
ignoreNotFound
ignoreNotFound
Purpose
Specifies how foreign keys that reference missing rows are handled in many-to-one relationships.
Examples
class LegacyCdDomain {
String title
Thumbnail thumbnail
static mapping = {
thumbnail ignoreNotFound: true
}
}
class Thumbnail {
byte[] data
}
Description
Usage: association_name(ignoreNotFound: boolean)
When the data in the database is corrupt and a foreign key references a non existent record, Hibernate will complain with a "No row with the given identifier exists" message. For example, a LegacyCdDomain
record may have a reference to a Thumbnail
record with an ID of 1234, but the database may no longer contain a Thumbnail
with that ID.
One possible way to load such corrupt data is to use the ignoreNotFound
mapping option. If you set it to true
, Hibernate will treat a missing row as a null
association. So in the above example, our LegacyCdDomain
instance would load but its thumbnail
property would be null
. Specifying a value of false
for ignoreNotFound
will result in Hibernate throwing an org.hibernate.ObjectNotFoundException
.
A dataset loaded with ignoreNotFound: true may throw an exception during save() because of the missing reference!
|
The default value for this setting is false
. It maps to Hibernate’s not-found
property.
This settings can be useful if you have a legacy database with unusual behaviour when it comes to referential integrity.
indexColumn
indexColumn
Purpose
Customizes the index column definition of an indexed collection such as a List
or Map
Examples
class Neo {
static hasMany = [matrix: Integer]
static mapping = {
matrix indexColumn: [name: "the_matrix", type: Integer]
}
}
def neo = new Neo()
neo.matrix = [(1): 30, (2): 42, (3): 23]
neo.save(flush: true)
Description
Usage: association_name(indexColumn:map)
Arguments:
-
name
- The name of the column as a String -
type
(optional) - The Hibernate type. -
sqlType
(optional) - The underlying SQL type -
enumType
(optional) - The enum type in for type-safe Enum properties. Eitherordinal
orstring
. -
index
(optional) - The index name -
unique
(optional) - Whether it is unique -
length
(optional) - The length of the column -
precision
(optional) - The precision of the column -
scale
(optional) - The scale of the column
By default when mapping an indexed collection such as a Map
or List
the index is stored in a column called association_name_idx
which is an integer
type in the case of lists and a String in the case of maps. You can alter how the index column is mapped using the indexColumn
argument:
static mapping = {
matrix indexColumn: [name: "the_matrix", type: Integer]
}
insertable
insertable
Purpose
Determines whether a property’s database column is set when a new instance is persisted.
Examples
class Book {
String title
static belongsTo = [author: Author]
static mapping = {
author insertable: false
author updateable: false
}
}
Description
Usage: association_name(insertable: boolean)
Useful in general where you don’t want to set an initial value (or include the column in the generated SQL) during an initial save()
.
In particular this is useful for one-to-many relationships. For example when you store the foreign key in the child table, it’s often efficient to save the child using only the foreign key of the parent. You do this by setting the parent object (and the parent foreign key) in the child entity. Setting the attributes insertable:false and updateable:false for the belongsTo parent object lets you insert and update using only the foreign key.
static mapping = {
author insertable: false
}
joinTable
joinTable
Purpose
Customizes the join table used for undirectional one-to-many, many-to-many and primitive collection types.
Examples
Basic collection type:
class Book {
static hasMany = [chapterPageCounts: Integer]
static mapping = {
chapterPageCounts indexColumn: [name: "chapter_number", type: Integer],
joinTable: [column: "page_count"]
}
Enum collection types:
enum VehicleStatus { OFF, IDLING, ACCELERATING, DECELERATING }
class Truck {
static hasMany = [states: VehicleStatus]
static mapping = {
states joinTable: [name: 'VEHICLE_STATUS_LOG',
key: 'TRUCK_ID', column: 'STATUS']
}
}
Many-to-many:
class Book {
String title
static belongsTo = Author
static hasMany = [authors: Author]
static mapping = {
authors joinTable: [name: "mm_author_books", key: 'mm_book_id' ]
}
}
class Author {
String name
static hasMany = [books: Book]
static mapping = {
books joinTable: [name: "mm_author_books", key: 'mm_author_id']
}
}
Unidirectional One-to-many:
class Employee {
static hasMany = [projects: Project]
static mapping = {
projects joinTable: [name: 'EMP_PROJ',
column: 'PROJECT_ID',
key: 'EMPLOYEE_ID']
}
}
class Project { }
Description
Usage: association_name(joinTable:map)
Arguments:
-
name
- The table name -
key
(optional) - The foreign key -
column
(optional) - The inverse column
GORM has various strategies for mapping association types. Some of them, such as basic collection types and many-to-many associations, use a join table. You can customize how this join table is created using the joinTable
argument.
lazy
lazy
Purpose
Configures whether to use proxies and lazy loading for one-to-one and many-to-one associations.
Examples
class Book {
static belongsTo = [author: Author]
static mapping = {
author lazy: false
}
}
Description
Usage: association_name(lazy: boolean)
By default GORM single-ended associations are lazy in that when you load a domain instance, an associated domain is not loaded until accessed. Hibernate creates a dynamic proxy by subclassing the domain class and proxying all methods and property access.
This can have some strange side effects (see this page on proxies at the Hibernate site) particularly in relation to inheritance. You can tell Hibernate not use proxies for single-ended associations by using the lazy
argument:
static mapping = {
author lazy: false
}
See the section on Eager vs Lazing Fetching in the user guide.
order
order
Purpose
Customizes the default sort order of query results.
Examples
class Book {
...
static mapping = {
order "desc"
}
}
Description
Usage: order(string)
Arguments:
-
direction
- Either "desc" or "asc" for descending and ascending respectively.
By defaults results are sorted in ascending order. With the order
method you can alter results to be sorted in descending order. See also the sort method.
sort
sort
Purpose
Customizes the default property to sort by for query results.
Examples
class Book {
...
static mapping = {
sort "releaseDate"
}
}
Description
Usage: sort(string/map)
By default results are sorted by creation date, and you can specify the column to order by in a query:
def results = Book.list(sort:"title")
However, you can also configure the default sort property:
static mapping = {
sort "title"
}
in which case the sort
argument is no longer needed. You can also control the sort order:
static mapping = {
sort title: "desc" // or "asc"
}
table
table映射
目的
自定义的domain类关联的数据库表的名称
示例
class Book {
static mapping = {
table "book_catalog"
}
}
描述
Usage: table(string/map)
Arguments:
-
name
- 表的名称 -
schema
(可选) - 表的schema -
catalog
(可选) - 表的catalog
默认情况下,domain类的表的映射名字是基于类的名字。GORM将类名的java风格驼峰格式映射为表名时,使用下划线隔离。例如` productreview 成为
product_review `。你可以用`table`方法重写表名:
static mapping = {
table "book_catalog"
}
也可以指定 schema 和 catalog名称:
static mapping = {
table name: "book_catalog", schema: "dbo", catalog: "CRM"
}
type
type
Purpose
Configures the Hibernate type for a particular property.
Examples
Changing to a text type (CLOB or TEXT depending on database dialect):
class Book {
String title
static mapping = {
title type: "text"
}
}
User types with multiple columns:
class Book {
...
MonetaryAmount amount
static mapping = {
amount type: MonetaryUserType, {
column name: "value"
column name: "currency", sqlType: "char", length: 3
}
}
}
Description
Usage: association_name(type:string/class)
Hibernate will attempt to automatically select the appropriate database type from the field typed based on configuration in the Dialect
class that is being used. But you can override the defaults if necessary. For example String
values are mapped by default to varchar(255)
columns. To store larger String
values you can use a text
type instead:
static mapping = {
title type: "text"
}
Hibernate also has the concept of custom UserType
implementations. In this case you specify the UserType
class. If the UserType
maps to multiple columns you may need to specify a mapping for each column:
static mapping = {
amount type: MonetaryUserType, {
column name: "value"
column name: "currency", sqlType: "char", length: 3
}
}
updateable
updateable映射
Purpose
Determines whether a property’s database column is updated when persistent instances are updated.
Examples
class Book {
String title
static belongsTo = [author: Author]
static mapping = {
author insertable: false
author updateable: false
}
}
Description
Usage: association_name(updateable: boolean)
Useful in general where you don’t want to update a value (or include the column in the generated SQL) during a save()
.
In particular this is useful for one-to-many relationships. For example when you store the foreign key in the child table, it’s often efficient to save the child using only the foreign key of the parent. You do this by setting the parent object (and the parent foreign key) in the child entity. Setting the attributes insertable:false and updateable:false for the belongsTo parent object lets you insert and update using only the foreign key.
static mapping = {
author updateable: false
}
version
version映射
目的
用于禁用乐观锁定或更改保持版本的列.
示例
class Book {
...
static mapping = {
version false
}
}
描述
Usage: version(string/boolean)
默认情况下,GORM配置 optimistic locking 启用. 可以通过设置 version
方法的 `false`参数来禁用:
static mapping = {
version false
}
可以将名称传递到 `version`方法以更改数据库列的名称:
static mapping = {
version 'revision_number'
}