GRAILS GORM 
Practical basics of how GORM can query for you 
when SQL is all you know. 
Dec 2014 - Ted Vinke
Overview 
• Introduction 
• Querying 
• Basic GORM 
• Dynamic Finders 
• Where Queries 
• Criteria 
• HQL 
• Native SQL 
• How to GORM your existing SQL? 
• Summary
We already know how to 
do SQL in Grails, right?
Just use Groovy! (jeej) 
class AnimalService { 
def dataSource 
def findWithGroovySql(String animalName) { 
def sql = new Sql(dataSource) 
try { 
return sql.rows("select * from animal where name = ?", [animalName]) 
} finally { 
sql.close() 
} 
} 
}
Is SQL your Grails application’s core 
business?
In a nutshell 
GORM stands for Grails Object Relational Mapping 
Grails 2.4.3 uses Hibernate 4 under the hood 
Evolves around domain classes
Domain classes? 
Database likes SQL We – the developers – like 
select * from animals 
where name = “Belle” 
to talk in domain terms 
Animal.findByName(“Belle”) 
select * from animals 
where id = 5 
Animal.get(5)
QUERYING 
What Grails can do for you
Settings 
dataSource { 
driverClassName = "org.h2.Driver" 
url = "jdbc:h2:mem:devDb;..." 
... 
} 
hibernate { 
... 
format_sql = true 
use_sql_comments = true 
}
We have a domain class 
// grails-app/domain/Animal.groovy 
class Animal { 
String name 
int age 
static mapping = { 
id column: "ani_id" 
version false 
} 
}
And some animals 
class BootStrap { 
def init = { servletContext -> 
environments { 
development { 
new Animal(name: "Belle", age: 1).save() 
new Animal(name: "Cinnamon", age: 5).save() 
} 
} 
} 
}
Basic GORM 
def animal = Animal.get(1) 
select animal0_.id as id1_0_0_, animal0_.version as version2_0_0_, 
animal0_.age as age3_0_0_, animal0_.name as name4_0_0_ from animal 
animal0_ where animal0_.id=? 
Hibernate uses unique table and column aliases, e.g. alias(column 
name)_(column unique integer)_(table unique integer)_(some 
suffix) 
int total = Animal.count() 
select count(*) as y0_ from animal this_ 
new Animal(name: "Belle", age: 1).save() 
insert into animal (id, version, age, name) values (null, ?, ?, ?) 
def animal = Animal.first(sort: "age") 
select ... from animal this_ order by this_.age asc limit ? 
• addTo 
• count 
• countBy 
• delete 
• exists 
• first 
• get 
• getAll 
• indent 
• last 
• list 
• listOrderBy 
• …
AnimalController 
// grails-app/controllers/AnimalController.groovy 
class AnimalController { 
def animalService 
def show(String name) { 
def animal = animalService.find...(name) 
log.info "Found " + animal 
... 
} 
} 
http://localhost:8080/query/animal/show?name=Belle
AnimalService 
class AnimalService { 
.... 
}
Dynamic 
finders 
Animal findWithDynamicFinder(String animalName) { 
Animal.findByName(animalName) 
} 
• findBy, countBy 
• findAllBy 
select this_.id as id1_0_0_, this_.version as 
version2_0_0_, this_.age as age3_0_0_, this_.name as 
name4_0_0_ from animal this_ where this_.name=? limit ?
Dynamic 
finders 
Animal findWithDynamicFinder(String animalName) { 
Animal.findByNameAndAgeLessThan(animalName, 3) 
} 
• findBy, countBy 
• findAllBy 
 Combines properties with 
all kinds of operators 
• LessThan 
• LessThanEquals 
• Between 
• Like 
• Not 
• Or 
select this_.id as id1_0_0_, this_.version as 
version2_0_0_, this_.age as age3_0_0_, this_.name as 
name4_0_0_ from animal this_ where this_.name=? and 
this_.age<? limit ?
Dynamic 
finders 
Animal findWithDynamicFinder(String animalName) { 
Animal.findByName(animalName, [sort: "age"]) 
} 
• findBy, countBy 
• findAllBy 
 Combines properties with 
all kinds of operators 
• LessThan 
• LessThanEquals 
• Between 
• Like 
• Not 
• Or 
 Pagination (sort, max, 
etc) and meta params 
(readOnly, timeout, 
etc.) 
select this_.id as id1_0_0_, this_.version as 
version2_0_0_, this_.age as age3_0_0_, this_.name as 
name4_0_0_ from animal this_ where this_.name=? order by 
this_.age asc limit ?
Where 
Animal findWithWhereQuery(String animalName) { 
def query = Animal.where { 
name == animalName 
} 
return query.find() 
} 
Defines a new 
grails.gorm.DetachedCriteria 
select this_.id as id1_0_0_, this_.version as 
version2_0_0_, this_.age as age3_0_0_, this_.name as 
name4_0_0_ from animal this_ where this_.name=?
Where 
Animal findWithWhereQuery(String animalName) { 
def query = Animal.where { 
name == animalName && (age < 3) 
} 
return query.find([sort: "age"]) 
} 
Defines a new 
grails.gorm.DetachedCriteria 
 Enhanced, compile-time 
checked query DSL. 
 More flexible than 
dynamic finders 
select this_.id as id1_0_0_, this_.version as 
version2_0_0_, this_.age as age3_0_0_, this_.name as 
name4_0_0_ from animal this_ where (this_.name=? and 
this_.age<?) order by this_.age asc
Where 
Animal findWithWhereQuery(String animalName) { 
def query = Animal.where { 
name == animalName && (age < 3) 
} 
return query.find() 
} 
The DetachedCriteria 
defined for where can also 
be used for find 
Animal findWithWhereQuery(String animalName) { 
Animal.find { 
name == animalName && (age < 3) 
} 
} 
Tip! 
If your query 
can return 
multiple rows, 
use findAll 
instead!
Criteria 
Animal findWithCriteria(String animalName) { 
// Criteria 
def c = Animal.createCriteria() 
return c.get { 
eq "name", animalName 
} 
} 
 Type-safe Groovy way of 
building criteria queries 
select this_.id as id1_0_0_, this_.version as 
version2_0_0_, this_.age as age3_0_0_, this_.name as 
name4_0_0_ from animal this_ where this_.name=?
Criteria 
Animal findWithCriteria(String animalName) { 
// Criteria 
def c = Animal.createCriteria() 
return c.get { 
eq "name", animalName 
lt "age", 3 
order "age", "desc" 
} 
} 
 Type-safe Groovy way of 
building criteria queries 
• c.list 
• c.get 
• c.scroll 
• c.listDinstinct 
select this_.id as id1_0_0_, this_.version as 
version2_0_0_, this_.age as age3_0_0_, this_.name as 
name4_0_0_ from animal this_ where this_.name=? and 
this_.age<? order by this_.age desc
Criteria and 
projections 
Long calculateTotalAge() { 
// Criteria 
def c = Animal.createCriteria() 
return c.get { 
projections { 
sum("age") 
} 
} 
} 
 Projections change the 
nature of the results 
select sum(this_.age) as y0_ from animal this_
How to test?
Dynamic finders, Where and Criteria queries 
can be unit tested! 
create-unit-test AnimalService 
test/unit/AnimalServiceSpec.groovy 
test-app –unit AnimalServiceSpec
AnimalServiceSpec 
• Uses DomainClassUnitTestMixin, simple in-memory ConcurrentHashMap 
@Mock(Animal) 
@TestFor(AnimalService) 
class AnimalServiceSpec extends Specification { 
void "test finding animals with various queries"() { 
given: 
new Animal(name: "Belle", age: 1).save() 
new Animal(name: "Cinnamon", age: 5).save() 
expect: 
"Belle" == service.findWithDynamicFinder("Belle").name 
"Belle" == service.findWithWhereQuery("Belle").name 
"Belle" == service.findWithCriteria("Belle").name 
} 
} 
Just @Mock the domain class 
and insert and verify your test 
data through the GORM API
Unit test with DomainClassUnitTestMixin 
uses 
in-memory ConcurrentHashMap 
which allows mocking of large part of GORM 
• Simple persistence methods like list(), save() 
• Dynamic Finders 
• Named Queries 
• Query By Example 
• GORM Events
HQL 
Animal findWithHQL(String animalName) { 
Animal.find("from Animal as a where a.name = :name", 
["name" : animalName]) 
} 
Hibernate Query Language 
Animal findWithHQL(String animalName) { 
Animal.executeQuery("from Animal a where a.name = 
:name", ["name" : animalName, "max" : 1]).first() 
} 
select animal0_.id as id1_0_, animal0_.version as 
version2_0_, animal0_.age as age3_0_, animal0_.name as 
name4_0_ from animal animal0_ where animal0_.name=? 
limit ?
HQL 
HQL almost looks like SQL… 
Animal.executeQuery("select distinct a.name 
from Animal a order by a.name") 
select distinct animal0_.name as col_0_0_ from 
animal animal0_ order by animal0_.name
class Animal { 
String name 
int age 
static mapping = { 
name column: "ani_name" 
age column: "ani_age" 
} 
} 
select animal0_.id as id1_0_, 
animal0_.version as version2_0_, 
animal0_.ani_age as ani_age3_0_, 
animal0_.ani_name as ani_name4_0_ from 
animal animal0_ where 
animal0_.ani_name=? limit ? 
HQL 
…but uses domain classes and properties instead of tables and columns 
Animal.executeQuery("select distinct a.name from Animal a order by a.name") 
class Animal { 
String name 
int age 
static mapping = {} 
} 
select animal0_.id as id1_0_, 
animal0_.version as version2_0_, 
animal0_.age as age3_0_, animal0_.name 
as name4_0_ from animal animal0_ where 
animal0_.name=? limit ?
How to test?
HQL can be unit tested!
AnimalServiceHibernateSpec 
• Uses HibernateTestMixin, Hibernate 4 and in-memory H2 
@Domain(Animal) 
@TestFor(AnimalService) 
@TestMixin(HibernateTestMixin) 
class AnimalServiceHibernateSpec extends Specification { 
def cleanup() { 
// unit test does not clear db between tests 
Animal.list()*.delete(flush: true) 
} 
void "test finding animal with HQL"() { 
given: 
new Animal(name: "Belle").save() 
new Animal(name: "Cinnamon").save() 
expect: 
"Belle" == service.findWithHQL("Belle").name
Unit test with HibernateTestMixin 
uses 
in-memory H2 database 
which allows testing all of GORM, including 
• String-based HQL queries 
• composite identifiers 
• dirty checking methods 
• other direct interaction with Hibernate 
• Hibernate needs to know 
about all domain classes, 
more than you would like to 
annotate with @Domain, so 
the Hibernate mixin is not 
really useful in practice
HQL can be integration tested! 
create-integration-test AnimalService 
test/integration/AnimalServiceSpec.groovy 
test-app –integration AnimalServiceSpec
AnimalServiceIntegrationSpec 
 Full Grails container is started in test-environment 
 Uses H2 in-memory database. Each test runs in transaction, which is rolled back at end of the test 
// no annotations whatsoever 
class AnimalServiceIntegrationSpec extends Specification { 
def animalService 
void "test finding animal with HQL"() { 
given: 
new Animal(name: "Belle").save() 
new Animal(name: "Cinnamon").save() 
expect: 
"Belle" == animalService.findWithHQL("Belle").name 
}
Integration test 
uses 
in-memory H2 database by default 
which allows testing all of GORM 
• Each test runs in its own transaction, 
which is rolled back at the end of the test
NATIVE SQL 
We have a lot of stored procedures and functions in our 
Oracle database
Groovy SQL 
and Hibernate 
Nastive SQL 
def findWithGroovySql(String animalName) { 
def sql = new Sql(dataSource) 
try { 
// returns rows of resultset 
// e.g. [[ID:1, VERSION:0, AGE:1, NAME:Belle]] 
String query = "select * from animal where name = ? limit 1" 
return sql.rows(query, [animalName]) 
} finally { 
sql.close() 
} 
} 
def findWithHibernateNativeSql(String animalName) { 
def session = sessionFactory.currentSession 
def query = session.createSQLQuery("select * from animal where name = :name limit 1") 
List results = query.with { 
// map columns to keys 
resultTransformer = AliasToEntityMapResultTransformer.INSTANCE 
setString("name", animalName) 
list() 
} 
// results are [[AGE:1, VERSION:0, ID:1, NAME:Belle]] 
results 
}
Just a Groovy call 
A breeze with Groovy SQL - no need to register all kinds of parameters. Perform a direct call with all 
the parameters. The closure is called once. Some more examples here. 
FUNCTION par_id ( 
i_participant_code_type IN participant.participant_code_type%TYPE 
, i_participant_code IN participant.participant_code%TYPE 
) RETURN participant.par_id%TYPE 
IS 
return_waarde participant.par_id%TYPE := NULL; 
Long getParticipantId(String participantCodeType, String participantCode) { 
Sql sql = new groovy.sql.Sql(dataSource) 
Long participantId 
sql.call("{? = call rxpa_general.par_id(?, ?)}", [Sql.BIGINT, participantCodeType, participantCode]) { 
result -> participantId = result 
} 
return participantId 
}
Sometimes trouble 
Has a lot of input parameters, some optional. This might sometimes cause some ORA issues when using the 
direct call method…. 
PROCEDURE set_dry_off_date ( 
o_message_code OUT VARCHAR2 
, i_ani_id IN lactation_period.ani_id_cow%TYPE 
, i_lactation_end_date IN lactation_period.lactation_end_date%TYPE 
, i_jou_id IN oxop_general.t_jou_id%TYPE 
DEFAULT NULL 
, i_par_id_last_change IN lactation_period.par_id_last_change%TYPE 
, i_prc_code_last_change IN lactation_period.prc_code_last_change%TYPE 
) 
IS 
...
Old-fashioned CallableStatement 
String messageCode 
try { 
Connection c = sql.createConnection() 
CallableStatement cs = c.prepareCall("{call axmi_general_mut.set_dry_off_date(?,?,?,?,?,?)}"); 
cs.setLong('i_ani_id', aniId) 
cs.setDate('i_lactation_end_date', new java.sql.Date(lactationEndDate.time)) 
if (journalId) { 
cs.setLong('i_jou_id', journalId) 
} else { 
cs.setNull('i_jou_id', Types.NUMERIC) 
} 
cs.setLong('i_par_id_last_change', parIdLastChange) 
cs.setString('i_prc_code_last_change', prcCodeLastChange) 
cs.registerOutParameter("o_message_code", Types.VARCHAR) 
cs.execute() 
messageCode = cs.getString("o_message_code") 
} catch (java.sql.SQLException e) { 
throw new RuntimeException("axmi_general_mut.set_dry_off_date failed", e) 
} finally { 
sql.close() 
}
How to test? 
But wait! What to test?
From a Grails perspective 
• I’m not interested in the workings of the existing db 
procedures/functions – those should have their own tests 
• I want to test that my service is 
1. calling them properly 
2. return any results properly 
• Unfortunately, there’s currently no easy way to do that 
• Options for CI: 
• Docker container with Oracle XE 
• ?
So, what if you can dream the SQL, 
and want to GORM it?
STEP-BY-STEP STRATEGY 
How to GORMinize Your SQL
Step-by-step 
• Let’s say, 
• you have an existing database with animals 
• in Grails you needs to show their accumulated age 
• you already know the SQL which does the trick: 
select sum(age) from animal
On a high-level 
1. have a method execute your SQL directly by Groovy 
SQL or Hibernate Native SQL 
• have a Grails test verify the logic in that original form 
• in order to do that, create domain class(es) to init test data 
2. refactor the Groovy SQL into GORM 
• your test informs you behaviour is still correct
1. Create an integration test 
• create-integration-test AnimalServiceIntegration 
• create a skeleton test and discover 
class AnimalServiceIntegrationSpec extends Specification { 
def animalService 
void "test calculating the total age"() { 
given: 
new Animal(name: "Belle", age: 1).save() 
new Animal(name: "Cinnamon", age: 5).save() 
expect: 
6 == animalService.calculateTotalAgeWithGroovySql() 
6 == animalService.calculateTotalAge() 
} 
}
1. Create an integration test 
• create-integration-test AnimalServiceIntegration 
• create a skeleton test and discover what you need... 
class AnimalServiceIntegrationSpec extends Specification { 
def animalService 
void "test calculating the total age"() { 
given: 
// some animals with each an age 
expect: 
// call calculation method, verify total age 
} 
} 
Some Animal 
domain classes for 
test data 
The actual business 
method 
2. Create domain class(es) 
• Either create a domain class from scratch, or 
• use the Grails Database Reverse Engineering Plugin 
• use them to initialize your test with 
class AnimalServiceIntegrationSpec extends Specification { 
def animalService 
void "test calculating the total age"() { 
given: 
new Animal(name: "Belle", age: 1).save() 
new Animal(name: "Cinnamon", age: 5).save() 
expect: 
// call calculation method, verify total age 
} 
} 
class Animal { 
String name 
int age 
static mapping = { 
id column: "ani_id" 
version false 
} 
}
2. Implement with Groovy SQL 
• Implement your business method taking the original query. See the various Groovy SQL examples 
Long calculateTotalAgeWithGroovySql() { 
new Sql(dataSource).firstRow("select sum(age) as total from animal").total 
} 
• Invoke it from the test. Verify with enough testcases that it does what it’s supposed to do. 
void "test calculating the total age"() { 
given: 
new Animal(name: "Belle", age: 1).save() 
new Animal(name: "Cinnamon", age: 5).save() 
expect: 
6 == animalService.calculateTotalAgeWithGroovySql() 
} 
}
3. Refactor into GORM 
• Now that you have (enough) test coverage, you can safely refactor into a version which doesn’t use 
Groovy SQL, but GORM or Hibernate features instead 
Long calculateTotalAgeWithGroovySql() { 
new Sql(dataSource).firstRow("select sum(age) as total from animal").total 
} 
• can become e.g. 
Long calculateTotalAge() { 
Animal.executeQuery("select sum(age) as total from animal").total 
} 
• and verify with your tests everything still works as expected
A few tips 
• Use the Grails Build Test Data plugin to refactor your tests to only include the relevant test data, and 
still have valid domain classes 
class AnimalServiceIntegrationSpec extends 
Specification { 
def animalService 
void "test calculating the total age"() { 
given: 
new Animal(name: "Belle", age: 1).save() 
new Animal(name: "Cinnamon", age: 5).save() 
expect: 
6 == animalService.calculateTotalAge() 
} 
} 
@Build(Animal) 
class AnimalServiceIntegrationSpec extends 
Specification { 
def animalService 
void "test calculating the total age"() { 
given: 
Animal.build(age: 1).save() 
Animal.build(age: 5).save() 
expect: 
6 == animalService.calculateTotalAge() 
} 
}
SUMMARY
Where 
Queries 
Less verbose than criteria 
More flexible than Dynamic Finders 
Dynamic 
Finders 
Simple queries with few 
properties 
Criteria 
Hibernate projections & 
restrictions 
Hibernate 
HQL 
Fully object-oriented SQL 
Hibernate 
Native SQL 
Native SQL through Hibernate 
Groovy 
SQL 
Native SQL through Groovy 
My opinionated ranking of 
query options, when 
considering readability, 
writability and testability for 
90% of my use cases.
Summary 
Try simplest possible query option first: easy to write, read & test 
GORM 
• allows you to think in domains rather than SQL 
• is easy to test with unit and integration tests 
• gives you more we haven’t covered yet: caching, named queries, GORM 
events, etc. 
Know the pros and cons of your SQL approach and choose 
accordingly. 
GORM has its strong suits, but native SQL too, e.g. performance tuning or db-specific 
SQL
More info 
• GORM 
• Querying with GORM 
• Dynamic Finders 
• Where Queries 
• Criteria 
• HQL 
• Groovy SQL 
• groovy.sql.Sql 
• database features 
• Hibernate Query Language (HQL) 
• Further reading: 
• How and When to Use Various GORM Querying Options 
• Recipes for using GORM with Grails

Grails GORM - You Know SQL. You Know Queries. Here's GORM.

  • 1.
    GRAILS GORM Practicalbasics of how GORM can query for you when SQL is all you know. Dec 2014 - Ted Vinke
  • 2.
    Overview • Introduction • Querying • Basic GORM • Dynamic Finders • Where Queries • Criteria • HQL • Native SQL • How to GORM your existing SQL? • Summary
  • 3.
    We already knowhow to do SQL in Grails, right?
  • 4.
    Just use Groovy!(jeej) class AnimalService { def dataSource def findWithGroovySql(String animalName) { def sql = new Sql(dataSource) try { return sql.rows("select * from animal where name = ?", [animalName]) } finally { sql.close() } } }
  • 5.
    Is SQL yourGrails application’s core business?
  • 6.
    In a nutshell GORM stands for Grails Object Relational Mapping Grails 2.4.3 uses Hibernate 4 under the hood Evolves around domain classes
  • 7.
    Domain classes? Databaselikes SQL We – the developers – like select * from animals where name = “Belle” to talk in domain terms Animal.findByName(“Belle”) select * from animals where id = 5 Animal.get(5)
  • 8.
    QUERYING What Grailscan do for you
  • 9.
    Settings dataSource { driverClassName = "org.h2.Driver" url = "jdbc:h2:mem:devDb;..." ... } hibernate { ... format_sql = true use_sql_comments = true }
  • 10.
    We have adomain class // grails-app/domain/Animal.groovy class Animal { String name int age static mapping = { id column: "ani_id" version false } }
  • 11.
    And some animals class BootStrap { def init = { servletContext -> environments { development { new Animal(name: "Belle", age: 1).save() new Animal(name: "Cinnamon", age: 5).save() } } } }
  • 12.
    Basic GORM defanimal = Animal.get(1) select animal0_.id as id1_0_0_, animal0_.version as version2_0_0_, animal0_.age as age3_0_0_, animal0_.name as name4_0_0_ from animal animal0_ where animal0_.id=? Hibernate uses unique table and column aliases, e.g. alias(column name)_(column unique integer)_(table unique integer)_(some suffix) int total = Animal.count() select count(*) as y0_ from animal this_ new Animal(name: "Belle", age: 1).save() insert into animal (id, version, age, name) values (null, ?, ?, ?) def animal = Animal.first(sort: "age") select ... from animal this_ order by this_.age asc limit ? • addTo • count • countBy • delete • exists • first • get • getAll • indent • last • list • listOrderBy • …
  • 13.
    AnimalController // grails-app/controllers/AnimalController.groovy class AnimalController { def animalService def show(String name) { def animal = animalService.find...(name) log.info "Found " + animal ... } } http://localhost:8080/query/animal/show?name=Belle
  • 14.
  • 15.
    Dynamic finders AnimalfindWithDynamicFinder(String animalName) { Animal.findByName(animalName) } • findBy, countBy • findAllBy select this_.id as id1_0_0_, this_.version as version2_0_0_, this_.age as age3_0_0_, this_.name as name4_0_0_ from animal this_ where this_.name=? limit ?
  • 16.
    Dynamic finders AnimalfindWithDynamicFinder(String animalName) { Animal.findByNameAndAgeLessThan(animalName, 3) } • findBy, countBy • findAllBy  Combines properties with all kinds of operators • LessThan • LessThanEquals • Between • Like • Not • Or select this_.id as id1_0_0_, this_.version as version2_0_0_, this_.age as age3_0_0_, this_.name as name4_0_0_ from animal this_ where this_.name=? and this_.age<? limit ?
  • 17.
    Dynamic finders AnimalfindWithDynamicFinder(String animalName) { Animal.findByName(animalName, [sort: "age"]) } • findBy, countBy • findAllBy  Combines properties with all kinds of operators • LessThan • LessThanEquals • Between • Like • Not • Or  Pagination (sort, max, etc) and meta params (readOnly, timeout, etc.) select this_.id as id1_0_0_, this_.version as version2_0_0_, this_.age as age3_0_0_, this_.name as name4_0_0_ from animal this_ where this_.name=? order by this_.age asc limit ?
  • 18.
    Where Animal findWithWhereQuery(StringanimalName) { def query = Animal.where { name == animalName } return query.find() } Defines a new grails.gorm.DetachedCriteria select this_.id as id1_0_0_, this_.version as version2_0_0_, this_.age as age3_0_0_, this_.name as name4_0_0_ from animal this_ where this_.name=?
  • 19.
    Where Animal findWithWhereQuery(StringanimalName) { def query = Animal.where { name == animalName && (age < 3) } return query.find([sort: "age"]) } Defines a new grails.gorm.DetachedCriteria  Enhanced, compile-time checked query DSL.  More flexible than dynamic finders select this_.id as id1_0_0_, this_.version as version2_0_0_, this_.age as age3_0_0_, this_.name as name4_0_0_ from animal this_ where (this_.name=? and this_.age<?) order by this_.age asc
  • 20.
    Where Animal findWithWhereQuery(StringanimalName) { def query = Animal.where { name == animalName && (age < 3) } return query.find() } The DetachedCriteria defined for where can also be used for find Animal findWithWhereQuery(String animalName) { Animal.find { name == animalName && (age < 3) } } Tip! If your query can return multiple rows, use findAll instead!
  • 21.
    Criteria Animal findWithCriteria(StringanimalName) { // Criteria def c = Animal.createCriteria() return c.get { eq "name", animalName } }  Type-safe Groovy way of building criteria queries select this_.id as id1_0_0_, this_.version as version2_0_0_, this_.age as age3_0_0_, this_.name as name4_0_0_ from animal this_ where this_.name=?
  • 22.
    Criteria Animal findWithCriteria(StringanimalName) { // Criteria def c = Animal.createCriteria() return c.get { eq "name", animalName lt "age", 3 order "age", "desc" } }  Type-safe Groovy way of building criteria queries • c.list • c.get • c.scroll • c.listDinstinct select this_.id as id1_0_0_, this_.version as version2_0_0_, this_.age as age3_0_0_, this_.name as name4_0_0_ from animal this_ where this_.name=? and this_.age<? order by this_.age desc
  • 23.
    Criteria and projections Long calculateTotalAge() { // Criteria def c = Animal.createCriteria() return c.get { projections { sum("age") } } }  Projections change the nature of the results select sum(this_.age) as y0_ from animal this_
  • 24.
  • 25.
    Dynamic finders, Whereand Criteria queries can be unit tested! create-unit-test AnimalService test/unit/AnimalServiceSpec.groovy test-app –unit AnimalServiceSpec
  • 26.
    AnimalServiceSpec • UsesDomainClassUnitTestMixin, simple in-memory ConcurrentHashMap @Mock(Animal) @TestFor(AnimalService) class AnimalServiceSpec extends Specification { void "test finding animals with various queries"() { given: new Animal(name: "Belle", age: 1).save() new Animal(name: "Cinnamon", age: 5).save() expect: "Belle" == service.findWithDynamicFinder("Belle").name "Belle" == service.findWithWhereQuery("Belle").name "Belle" == service.findWithCriteria("Belle").name } } Just @Mock the domain class and insert and verify your test data through the GORM API
  • 27.
    Unit test withDomainClassUnitTestMixin uses in-memory ConcurrentHashMap which allows mocking of large part of GORM • Simple persistence methods like list(), save() • Dynamic Finders • Named Queries • Query By Example • GORM Events
  • 28.
    HQL Animal findWithHQL(StringanimalName) { Animal.find("from Animal as a where a.name = :name", ["name" : animalName]) } Hibernate Query Language Animal findWithHQL(String animalName) { Animal.executeQuery("from Animal a where a.name = :name", ["name" : animalName, "max" : 1]).first() } select animal0_.id as id1_0_, animal0_.version as version2_0_, animal0_.age as age3_0_, animal0_.name as name4_0_ from animal animal0_ where animal0_.name=? limit ?
  • 29.
    HQL HQL almostlooks like SQL… Animal.executeQuery("select distinct a.name from Animal a order by a.name") select distinct animal0_.name as col_0_0_ from animal animal0_ order by animal0_.name
  • 30.
    class Animal { String name int age static mapping = { name column: "ani_name" age column: "ani_age" } } select animal0_.id as id1_0_, animal0_.version as version2_0_, animal0_.ani_age as ani_age3_0_, animal0_.ani_name as ani_name4_0_ from animal animal0_ where animal0_.ani_name=? limit ? HQL …but uses domain classes and properties instead of tables and columns Animal.executeQuery("select distinct a.name from Animal a order by a.name") class Animal { String name int age static mapping = {} } select animal0_.id as id1_0_, animal0_.version as version2_0_, animal0_.age as age3_0_, animal0_.name as name4_0_ from animal animal0_ where animal0_.name=? limit ?
  • 31.
  • 32.
    HQL can beunit tested!
  • 33.
    AnimalServiceHibernateSpec • UsesHibernateTestMixin, Hibernate 4 and in-memory H2 @Domain(Animal) @TestFor(AnimalService) @TestMixin(HibernateTestMixin) class AnimalServiceHibernateSpec extends Specification { def cleanup() { // unit test does not clear db between tests Animal.list()*.delete(flush: true) } void "test finding animal with HQL"() { given: new Animal(name: "Belle").save() new Animal(name: "Cinnamon").save() expect: "Belle" == service.findWithHQL("Belle").name
  • 34.
    Unit test withHibernateTestMixin uses in-memory H2 database which allows testing all of GORM, including • String-based HQL queries • composite identifiers • dirty checking methods • other direct interaction with Hibernate • Hibernate needs to know about all domain classes, more than you would like to annotate with @Domain, so the Hibernate mixin is not really useful in practice
  • 35.
    HQL can beintegration tested! create-integration-test AnimalService test/integration/AnimalServiceSpec.groovy test-app –integration AnimalServiceSpec
  • 36.
    AnimalServiceIntegrationSpec  FullGrails container is started in test-environment  Uses H2 in-memory database. Each test runs in transaction, which is rolled back at end of the test // no annotations whatsoever class AnimalServiceIntegrationSpec extends Specification { def animalService void "test finding animal with HQL"() { given: new Animal(name: "Belle").save() new Animal(name: "Cinnamon").save() expect: "Belle" == animalService.findWithHQL("Belle").name }
  • 37.
    Integration test uses in-memory H2 database by default which allows testing all of GORM • Each test runs in its own transaction, which is rolled back at the end of the test
  • 38.
    NATIVE SQL Wehave a lot of stored procedures and functions in our Oracle database
  • 39.
    Groovy SQL andHibernate Nastive SQL def findWithGroovySql(String animalName) { def sql = new Sql(dataSource) try { // returns rows of resultset // e.g. [[ID:1, VERSION:0, AGE:1, NAME:Belle]] String query = "select * from animal where name = ? limit 1" return sql.rows(query, [animalName]) } finally { sql.close() } } def findWithHibernateNativeSql(String animalName) { def session = sessionFactory.currentSession def query = session.createSQLQuery("select * from animal where name = :name limit 1") List results = query.with { // map columns to keys resultTransformer = AliasToEntityMapResultTransformer.INSTANCE setString("name", animalName) list() } // results are [[AGE:1, VERSION:0, ID:1, NAME:Belle]] results }
  • 40.
    Just a Groovycall A breeze with Groovy SQL - no need to register all kinds of parameters. Perform a direct call with all the parameters. The closure is called once. Some more examples here. FUNCTION par_id ( i_participant_code_type IN participant.participant_code_type%TYPE , i_participant_code IN participant.participant_code%TYPE ) RETURN participant.par_id%TYPE IS return_waarde participant.par_id%TYPE := NULL; Long getParticipantId(String participantCodeType, String participantCode) { Sql sql = new groovy.sql.Sql(dataSource) Long participantId sql.call("{? = call rxpa_general.par_id(?, ?)}", [Sql.BIGINT, participantCodeType, participantCode]) { result -> participantId = result } return participantId }
  • 41.
    Sometimes trouble Hasa lot of input parameters, some optional. This might sometimes cause some ORA issues when using the direct call method…. PROCEDURE set_dry_off_date ( o_message_code OUT VARCHAR2 , i_ani_id IN lactation_period.ani_id_cow%TYPE , i_lactation_end_date IN lactation_period.lactation_end_date%TYPE , i_jou_id IN oxop_general.t_jou_id%TYPE DEFAULT NULL , i_par_id_last_change IN lactation_period.par_id_last_change%TYPE , i_prc_code_last_change IN lactation_period.prc_code_last_change%TYPE ) IS ...
  • 42.
    Old-fashioned CallableStatement StringmessageCode try { Connection c = sql.createConnection() CallableStatement cs = c.prepareCall("{call axmi_general_mut.set_dry_off_date(?,?,?,?,?,?)}"); cs.setLong('i_ani_id', aniId) cs.setDate('i_lactation_end_date', new java.sql.Date(lactationEndDate.time)) if (journalId) { cs.setLong('i_jou_id', journalId) } else { cs.setNull('i_jou_id', Types.NUMERIC) } cs.setLong('i_par_id_last_change', parIdLastChange) cs.setString('i_prc_code_last_change', prcCodeLastChange) cs.registerOutParameter("o_message_code", Types.VARCHAR) cs.execute() messageCode = cs.getString("o_message_code") } catch (java.sql.SQLException e) { throw new RuntimeException("axmi_general_mut.set_dry_off_date failed", e) } finally { sql.close() }
  • 43.
    How to test? But wait! What to test?
  • 44.
    From a Grailsperspective • I’m not interested in the workings of the existing db procedures/functions – those should have their own tests • I want to test that my service is 1. calling them properly 2. return any results properly • Unfortunately, there’s currently no easy way to do that • Options for CI: • Docker container with Oracle XE • ?
  • 45.
    So, what ifyou can dream the SQL, and want to GORM it?
  • 46.
    STEP-BY-STEP STRATEGY Howto GORMinize Your SQL
  • 47.
    Step-by-step • Let’ssay, • you have an existing database with animals • in Grails you needs to show their accumulated age • you already know the SQL which does the trick: select sum(age) from animal
  • 48.
    On a high-level 1. have a method execute your SQL directly by Groovy SQL or Hibernate Native SQL • have a Grails test verify the logic in that original form • in order to do that, create domain class(es) to init test data 2. refactor the Groovy SQL into GORM • your test informs you behaviour is still correct
  • 49.
    1. Create anintegration test • create-integration-test AnimalServiceIntegration • create a skeleton test and discover class AnimalServiceIntegrationSpec extends Specification { def animalService void "test calculating the total age"() { given: new Animal(name: "Belle", age: 1).save() new Animal(name: "Cinnamon", age: 5).save() expect: 6 == animalService.calculateTotalAgeWithGroovySql() 6 == animalService.calculateTotalAge() } }
  • 50.
    1. Create anintegration test • create-integration-test AnimalServiceIntegration • create a skeleton test and discover what you need... class AnimalServiceIntegrationSpec extends Specification { def animalService void "test calculating the total age"() { given: // some animals with each an age expect: // call calculation method, verify total age } } Some Animal domain classes for test data The actual business method 
  • 51.
    2. Create domainclass(es) • Either create a domain class from scratch, or • use the Grails Database Reverse Engineering Plugin • use them to initialize your test with class AnimalServiceIntegrationSpec extends Specification { def animalService void "test calculating the total age"() { given: new Animal(name: "Belle", age: 1).save() new Animal(name: "Cinnamon", age: 5).save() expect: // call calculation method, verify total age } } class Animal { String name int age static mapping = { id column: "ani_id" version false } }
  • 52.
    2. Implement withGroovy SQL • Implement your business method taking the original query. See the various Groovy SQL examples Long calculateTotalAgeWithGroovySql() { new Sql(dataSource).firstRow("select sum(age) as total from animal").total } • Invoke it from the test. Verify with enough testcases that it does what it’s supposed to do. void "test calculating the total age"() { given: new Animal(name: "Belle", age: 1).save() new Animal(name: "Cinnamon", age: 5).save() expect: 6 == animalService.calculateTotalAgeWithGroovySql() } }
  • 53.
    3. Refactor intoGORM • Now that you have (enough) test coverage, you can safely refactor into a version which doesn’t use Groovy SQL, but GORM or Hibernate features instead Long calculateTotalAgeWithGroovySql() { new Sql(dataSource).firstRow("select sum(age) as total from animal").total } • can become e.g. Long calculateTotalAge() { Animal.executeQuery("select sum(age) as total from animal").total } • and verify with your tests everything still works as expected
  • 54.
    A few tips • Use the Grails Build Test Data plugin to refactor your tests to only include the relevant test data, and still have valid domain classes class AnimalServiceIntegrationSpec extends Specification { def animalService void "test calculating the total age"() { given: new Animal(name: "Belle", age: 1).save() new Animal(name: "Cinnamon", age: 5).save() expect: 6 == animalService.calculateTotalAge() } } @Build(Animal) class AnimalServiceIntegrationSpec extends Specification { def animalService void "test calculating the total age"() { given: Animal.build(age: 1).save() Animal.build(age: 5).save() expect: 6 == animalService.calculateTotalAge() } }
  • 55.
  • 56.
    Where Queries Lessverbose than criteria More flexible than Dynamic Finders Dynamic Finders Simple queries with few properties Criteria Hibernate projections & restrictions Hibernate HQL Fully object-oriented SQL Hibernate Native SQL Native SQL through Hibernate Groovy SQL Native SQL through Groovy My opinionated ranking of query options, when considering readability, writability and testability for 90% of my use cases.
  • 57.
    Summary Try simplestpossible query option first: easy to write, read & test GORM • allows you to think in domains rather than SQL • is easy to test with unit and integration tests • gives you more we haven’t covered yet: caching, named queries, GORM events, etc. Know the pros and cons of your SQL approach and choose accordingly. GORM has its strong suits, but native SQL too, e.g. performance tuning or db-specific SQL
  • 58.
    More info •GORM • Querying with GORM • Dynamic Finders • Where Queries • Criteria • HQL • Groovy SQL • groovy.sql.Sql • database features • Hibernate Query Language (HQL) • Further reading: • How and When to Use Various GORM Querying Options • Recipes for using GORM with Grails