注:本文档只针对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 "图书表"
}

全部方法: addTo方法 , attach方法 …​..

参看 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

between("balance", 500, 1000)

eq

Where a property equals a particular value.

eq("branch", "London")

eq (case-insensitive)

A version of eq that supports an optional 3rd Map parameter to specify that the query be case-insensitive.

eq("branch", "london", [ignoreCase: true])

eqProperty

Where one property must equal another

eqProperty("lastTx", "firstTx")

gt

Where a property is greater than a particular value

gt("balance",1000)

gtProperty

Where one property must be greater than another

gtProperty("balance", "overdraft")

ge

Where a property is greater than or equal to a particular value

ge("balance", 1000)

geProperty

Where one property must be greater than or equal to another

geProperty("balance", "overdraft")

idEq

Where an objects id equals the specified value

idEq(1)

ilike

A case-insensitive like expression

ilike("holderFirstName", "Steph%")

in

Where a property is contained within the specified list of values. Can also be chained with the not method where a property is not contained within the specified list of values. Note: in is a Groovy reserve word, so it must be escaped by quotes.

'in'("age",[18..65]) or not {'in'("age",[18..65])}

isEmpty

Where a collection property is empty

isEmpty("transactions")

isNotEmpty

Where a collection property is not empty

isNotEmpty("transactions")

isNull

Where a property is null

isNull("holderGender")

isNotNull

Where a property is not null

isNotNull("holderGender")

lt

Where a property is less than a particular value

lt("balance", 1000)

ltProperty

Where one property must be less than another

ltProperty("balance", "overdraft")

le

Where a property is less than or equal to a particular value

le("balance", 1000)

leProperty

Where one property must be less than or equal to another

leProperty("balance", "overdraft")

like

Equivalent to SQL like expression

like("holderFirstName", "Steph%")

ne

Where a property does not equal a particular value

ne("branch", "London")

neProperty

Where one property does not equal another

neProperty("lastTx", "firstTx")

order

Order the results by a particular property

order("holderLastName", "desc")

rlike

Similar to like, but uses a regex. Only supported on Oracle and MySQL.

rlike("holderFirstName", /Steph.+/)

sizeEq

Where a collection property’s size equals a particular value

sizeEq("transactions", 10)

sizeGt

Where a collection property’s size is greater than a particular value

sizeGt("transactions", 10)

sizeGe

Where a collection property’s size is greater than or equal to a particular value

sizeGe("transactions", 10)

sizeLt

Where a collection property’s size is less than a particular value

sizeLt("transactions", 10)

sizeLe

Where a collection property’s size is less than or equal to a particular value

sizeLe("transactions", 10)

sizeNe

Where a collection property’s size is not equal to a particular value

sizeNe("transactions", 10)

sqlRestriction

Use arbitrary SQL to modify the resultset

sqlRestriction "char_length(first_name) = 4"

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).

order "age", "desc"

firstResult(int)

Specifies the offset for the results. A value of 0 will return all records up to the maximum specified.

firstResult 20

maxResults(int)

Specifies the maximum number of records to return.

maxResults 10

cache(boolean)

Indicates if the query should be cached (if the query cache is enabled).

cache 'true'

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

property("firstName")

distinct

Returns results using a single or collection of distinct property names

distinct("fn") or distinct(['fn', 'ln'])

avg

Returns the average value of the given property

avg("age")

count

Returns the count of the given property name

count("branch")

countDistinct

Returns the count of the given property name for distinct rows

countDistinct("branch")

groupProperty

Groups the results by the given property

groupProperty("lastName")

max

Returns the maximum value of the given property

max("age")

min

Returns the minimum value of the given property

min("age")

sum

Returns the sum of the given property

sum("balance")

rowCount

Returns count of the number of rows returned

rowCount()

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 to true 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 - A List of parameters for a positional parameterized query

  • namedParams - A Map of named parameters for a named parameterized query

  • metaParams - A Map of pagination parameters max or/and offset, as well as Hibernate query parameters readOnly, fetchSize, timeout, and flushMode

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 - A List of parameters for a positional parameterized HQL query

  • namedParams - A Map 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 - A List of parameters for a positional parametrized HQL query

  • namedParams - A Map of named parameters a HQL query

  • queryParams - A Map of query parameters. Currently, only cache 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 - A List of parameters for a positional parametrized HQL query

  • namedParams - A Map of named parameters a HQL query

  • queryParams - A Map 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 to read())

  • fetchSize - number of rows fetched by the underlying JDBC driver per round trip

  • flushMode - Hibernate FlushMode override, defaults to FlushMode.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 - A M`ap containing pagination parameters `max, order, offset and sort and metaParameters readOnly, timeout, fetchSize, and flushMode

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 - 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.

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 - A Map 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

Returns whether the domain instance is attached to a currently active Hibernate session. This method is often used on conjunction with the merge or attach methods.

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 is true.

  • fetch - The fetch policy for the object’s associations as a Map

  • readOnly - true if returned objects should not be automatically dirty-checked (simlar to read())

  • fetchSize - number of rows fetched by the underlying JDBC driver per round trip

  • flushMode - Hibernate FlushMode override, defaults to FlushMode.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 to false if validation should be skipped

  • flush (optional) - When set to true 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 to false if validation should be skipped

  • flush (optional) - When set to true flushes the persistence context, persisting the object immediately and updating the version column for optimistic locking

  • insert (optional) - When set to true 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 to true the save method with throw a grails.validation.ValidationException if validation fails. This behavior may also be triggered by setting the grails.gorm.failOnError property in grails-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 is true by default - set to false 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 is true by default; set it to false 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 a Session 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