SlideShare a Scribd company logo
1 of 35
Grails: a quick tutorial (1) Davide Rossi ,[object Object]
Contents ,[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]
Contents (2) ,[object Object],[object Object],[object Object],[object Object],[object Object]
Grails: Groovy on Rails ? ,[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]
Creating a new application ,[object Object],> grails create-app JugDemo
Domain classes and GORM ,[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]
Datasource dataSource { pooled = true driverClassName = "org.hsqldb.jdbcDriver" username = "sa" password = "" } hibernate { cache.use_second_level_cache=true cache.use_query_cache=true cache.provider_class='com.opensymphony.oscache.hibernate.OSCacheProvider' }
Datasource environments { development { dataSource { dbCreate = "create-drop" // one of 'create', 'create-drop','update' url = "jdbc:hsqldb:mem:devDB" } } test { dataSource { dbCreate = "update" url = "jdbc:hsqldb:mem:testDb" } } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } } }
Constraints ,[object Object],[object Object],[object Object],[object Object],[object Object]
Team domain class > grails create-domain-class team  class Team { String name String championship String city Long supporters Integer foundationYear static hasMany = [players: Player] static constraints = { name (unique: true, blank: false, size: 2..30) championship (blank: false, inList: ['Serie A', 'Serie B', 'Serie C1', 'Serie C2']) city (blank: true, size: 2..25) supporters (nullable: true) foundationYear (nullable: true) } }
Player domain class >  grails create-domain-class player class Player { String name String lastName String city Date birth String email static belongsTo = [team: Team] static constraints = { name (blank: false, size: 2..30) lastName (blank: false, size: 2..30) city (blank: true, size: 2..25) birth (nullable:true) email (blank: true, email: true) } }
GORM Associations – One to one Unidirectional -  class Person { BillingAddress address } class BillingAddress { ... } Bidirectional (no update / delete cascading) -  class Person { BillingAddress address } class BillingAddress { Person person }
GORM Associations – One to one Bidirectional (update / delete cascading) -  class Person { BillingAddress address } class BillingAddress { static belongsTo = [person: Person]  }
GORM Associations – One to many Update cascading, no delete -  class Team { static hasMany = [players: Player] } class Player { ...  } Update / delete cascading -  class Team { static hasMany = [players: Player] } class Player { static belongsTo = [team: Team] }
GORM Associations – Many to many ,[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]
GORM notes ,[object Object],[object Object],[object Object],[object Object]
Team controller >  grails generate-controller team Every action is implemented as a closure
Team controller - List def list = { if (!params.max) params.max = 10 [ teamList: Team.list( params ) ] } - params is a map containing the request parameters - list is a GORM method, can take parameters: - max, offset, order, sort, ignoreCase, fetch - No explicit return statement - No explicit view
Team controller - Show def show = { def team = Team.get( params.id ) if (!team) { flash.message = "Team not found with id ${params.id}" redirect(action: list) } else {  return [ team : team ]  } } - get is a GORM method - flash is a new scope introduces by Grails - explicit return statement
Team controller - Save def save = { def team = new Team(params) if(!team.hasErrors() && team.save()) { flash.message = "Team ${team.id} created" redirect(action: show, id: team.id) } else { render(view: 'create', model: [team: team]) } } - save performs validation and return null if errors are found - explicit view declarations (render)
Generate controllers and view >  grails generate-views team >  grails generate-all player
Running the app >  grails run-app
Application bootstrap def init = { servletContext -> if (GrailsUtil.environment == "development") {  def team1 = new Team(name: 'Varese', championship: 'Serie C2', city:  'Varese', supporters: 1000, foundationYear: 1910).save() def team2 = ... def team3 = ... def team4 = ... new Player(name: 'player1', lastName: 'one', city: 'Varese', birth: new Date(),  email: 'player1@team.it', team: team1).save() ... new Player(name: 'player11', lastName: 'eleven', city: 'Palermo', birth: new  Date(), email: 'player11@team.it', team: team4).save() } }
Search types ,[object Object],[object Object],[object Object]
Adding a search form team/list.gsp: <g:form name=”searchForm” action=”search”> Championship: <g:select name=”championship”  from=”${Team.constraints.championship.inList}” /> <input type=”submit” value=”Search” /> </g:form>
Dynamic finders teamController: def search = { def teamList =  Team.findAllByChampionship(params.championship) render(view:'list', model: [teamList: teamList] ) }
Dynamic finders - findBy and findAllBy - def team = Team.findByName(‘Juventus’) - def teamList = Team.findAllByCity(‘Milano’) - Composition of properties, boolean logic and comparators: - and, or - LessThan, LessThanEquals, GreaterThan,  GreaterThanEquals, Like, Ilike, NotEqual, Between,  IsNotNull, IsNull es:  Team.findAllByCityLikeAndSupportersGreaterThan(‘Milano’, 1000, [max: 3, offset: 2, sort: &quot;name&quot;, order: &quot;desc&quot;])
Multiaction search form <g:form name=&quot;searchForm&quot;> <input type=&quot;text&quot; name=&quot;searchQuery&quot; /> <g:actionSubmit value=&quot;Search by Team&quot;  action=&quot;teamSearch&quot; /> <g:actionSubmit value=&quot;Search by City&quot;  action=&quot;citySearch&quot; /> </g:form> - Each button sends the form to a different action
Hql queries def teamSearch = { def playerList = Player.findAll(&quot;from Player as p where  p.team.name = ?&quot;, [&quot;${params.searchQuery}&quot;]) render(view: 'list', model: [playerList: playerList]) } -  You can use HQL queries with named and pagination parameters: es: Player.findAll(&quot;from Player as p where p.team.name  = :teamName order by p.lastName asc&quot;,  [teamName: &quot;Juventus&quot;], [max:10, offset:20])
Hibernate Criteria def citySearch = { def c = Player.createCriteria() def playerList = c.list { ilike(&quot;city&quot;, &quot;%${params.searchQuery}%&quot;) } render(view: 'list', model: [playerList: playerList]) } - Uses a Groovy builder to hide Hibernate's Criteria API complexity
Hibernate Criteria A more complex example: def c = Account.createCriteria() def results = c { like(&quot;holderFirstName&quot;, &quot;Fred%&quot;) and { between(&quot;balance&quot;, 500, 1000) eq(&quot;branch&quot;, &quot;London&quot;) } maxResults(10) order(&quot;holderLastName&quot;, &quot;desc&quot;) }
Pagination problem After the search we see a pagination error: we must  modify the generated action and gsp:
Pagination <div class=&quot;paginateButtons&quot;> <g:paginate total=&quot;${playerCount}&quot; /> </div> def list = { if (!params.max) params.max = 10 def playerList = Player.list( params ) [ playerList:  playerList, playerCount: Player.count()] }
Pagination def teamSearch = { def playerList = Player.findAll(&quot;from Player as p where p.team.name = ?&quot;,  [&quot;${params.searchQuery}&quot;]) render(view: 'list', model: [playerList: playerList,  playerCount: playerList.size()]) } def citySearch = { def c = Player.createCriteria() def playerList = c.list { ilike(&quot;city&quot;, &quot;%${params.searchQuery}%&quot;) } render(view: 'list', model: [playerList: playerList,  playerCount: playerList.size()]) }
Conclusions ,[object Object],[object Object],[object Object]

More Related Content

What's hot

GR8Conf 2011: Effective Groovy
GR8Conf 2011: Effective GroovyGR8Conf 2011: Effective Groovy
GR8Conf 2011: Effective Groovy
GR8Conf
 
Micro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicateMicro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicate
Kiev ALT.NET
 
Introduction to Gremlin
Introduction to GremlinIntroduction to Gremlin
Introduction to Gremlin
Max De Marzi
 

What's hot (20)

JavaScript Arrays
JavaScript Arrays JavaScript Arrays
JavaScript Arrays
 
GR8Conf 2011: Effective Groovy
GR8Conf 2011: Effective GroovyGR8Conf 2011: Effective Groovy
GR8Conf 2011: Effective Groovy
 
(Greach 2015) Dsl'ing your Groovy
(Greach 2015) Dsl'ing your Groovy(Greach 2015) Dsl'ing your Groovy
(Greach 2015) Dsl'ing your Groovy
 
Invertible-syntax 入門
Invertible-syntax 入門Invertible-syntax 入門
Invertible-syntax 入門
 
Painless Persistence with Realm
Painless Persistence with RealmPainless Persistence with Realm
Painless Persistence with Realm
 
The Ring programming language version 1.9 book - Part 39 of 210
The Ring programming language version 1.9 book - Part 39 of 210The Ring programming language version 1.9 book - Part 39 of 210
The Ring programming language version 1.9 book - Part 39 of 210
 
Groovy ネタ NGK 忘年会2009 ライトニングトーク
Groovy ネタ NGK 忘年会2009 ライトニングトークGroovy ネタ NGK 忘年会2009 ライトニングトーク
Groovy ネタ NGK 忘年会2009 ライトニングトーク
 
Implementing a many-to-many Relationship with Slick
Implementing a many-to-many Relationship with SlickImplementing a many-to-many Relationship with Slick
Implementing a many-to-many Relationship with Slick
 
Micro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicateMicro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicate
 
Introduction to Gremlin
Introduction to GremlinIntroduction to Gremlin
Introduction to Gremlin
 
Big Data Everywhere Chicago: Unleash the Power of HBase Shell (Conversant)
Big Data Everywhere Chicago: Unleash the Power of HBase Shell (Conversant) Big Data Everywhere Chicago: Unleash the Power of HBase Shell (Conversant)
Big Data Everywhere Chicago: Unleash the Power of HBase Shell (Conversant)
 
Martin Fowler's Refactoring Techniques Quick Reference
Martin Fowler's Refactoring Techniques Quick ReferenceMartin Fowler's Refactoring Techniques Quick Reference
Martin Fowler's Refactoring Techniques Quick Reference
 
Introduction To Groovy 2005
Introduction To Groovy 2005Introduction To Groovy 2005
Introduction To Groovy 2005
 
Template Haskell とか
Template Haskell とかTemplate Haskell とか
Template Haskell とか
 
Pragmatic Real-World Scala (short version)
Pragmatic Real-World Scala (short version)Pragmatic Real-World Scala (short version)
Pragmatic Real-World Scala (short version)
 
groovy & grails - lecture 3
groovy & grails - lecture 3groovy & grails - lecture 3
groovy & grails - lecture 3
 
Why realm?
Why realm?Why realm?
Why realm?
 
Tips and Tricks of Developing .NET Application
Tips and Tricks of Developing .NET ApplicationTips and Tricks of Developing .NET Application
Tips and Tricks of Developing .NET Application
 
Apache Spark - Key Value RDD - Transformations | Big Data Hadoop Spark Tutori...
Apache Spark - Key Value RDD - Transformations | Big Data Hadoop Spark Tutori...Apache Spark - Key Value RDD - Transformations | Big Data Hadoop Spark Tutori...
Apache Spark - Key Value RDD - Transformations | Big Data Hadoop Spark Tutori...
 
The Ring programming language version 1.5.2 book - Part 44 of 181
The Ring programming language version 1.5.2 book - Part 44 of 181The Ring programming language version 1.5.2 book - Part 44 of 181
The Ring programming language version 1.5.2 book - Part 44 of 181
 

Similar to Grails: a quick tutorial (1)

GHC Participant Training
GHC Participant TrainingGHC Participant Training
GHC Participant Training
AidIQ
 
Curscatalyst
CurscatalystCurscatalyst
Curscatalyst
Kar Juan
 
Perl web frameworks
Perl web frameworksPerl web frameworks
Perl web frameworks
diego_k
 
Agile web development Groovy Grails with Netbeans
Agile web development Groovy Grails with NetbeansAgile web development Groovy Grails with Netbeans
Agile web development Groovy Grails with Netbeans
Carol McDonald
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overview
Yehuda Katz
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Paulo Ragonha
 

Similar to Grails: a quick tutorial (1) (20)

Bag Of Tricks From Iusethis
Bag Of Tricks From IusethisBag Of Tricks From Iusethis
Bag Of Tricks From Iusethis
 
DBIx-DataModel v2.0 in detail
DBIx-DataModel v2.0 in detail DBIx-DataModel v2.0 in detail
DBIx-DataModel v2.0 in detail
 
Groovy On Trading Desk (2010)
Groovy On Trading Desk (2010)Groovy On Trading Desk (2010)
Groovy On Trading Desk (2010)
 
Framework
FrameworkFramework
Framework
 
What's New in ZF 1.10
What's New in ZF 1.10What's New in ZF 1.10
What's New in ZF 1.10
 
GHC Participant Training
GHC Participant TrainingGHC Participant Training
GHC Participant Training
 
DataMapper
DataMapperDataMapper
DataMapper
 
Pxb For Yapc2008
Pxb For Yapc2008Pxb For Yapc2008
Pxb For Yapc2008
 
Curscatalyst
CurscatalystCurscatalyst
Curscatalyst
 
Perl web frameworks
Perl web frameworksPerl web frameworks
Perl web frameworks
 
Agile web development Groovy Grails with Netbeans
Agile web development Groovy Grails with NetbeansAgile web development Groovy Grails with Netbeans
Agile web development Groovy Grails with Netbeans
 
Simple Ways To Be A Better Programmer (OSCON 2007)
Simple Ways To Be A Better Programmer (OSCON 2007)Simple Ways To Be A Better Programmer (OSCON 2007)
Simple Ways To Be A Better Programmer (OSCON 2007)
 
B03-GenomeContent-Intermine
B03-GenomeContent-IntermineB03-GenomeContent-Intermine
B03-GenomeContent-Intermine
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overview
 
Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2
 
Grails 0.3-SNAPSHOT Presentation WJAX 2006 English
Grails 0.3-SNAPSHOT Presentation WJAX 2006 EnglishGrails 0.3-SNAPSHOT Presentation WJAX 2006 English
Grails 0.3-SNAPSHOT Presentation WJAX 2006 English
 
vfsStream - a better approach for file system dependent tests
vfsStream - a better approach for file system dependent testsvfsStream - a better approach for file system dependent tests
vfsStream - a better approach for file system dependent tests
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
 
Ruby on Rails Intro
Ruby on Rails IntroRuby on Rails Intro
Ruby on Rails Intro
 
Rust ⇋ JavaScript
Rust ⇋ JavaScriptRust ⇋ JavaScript
Rust ⇋ JavaScript
 

Recently uploaded

Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Victor Rentea
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 

Recently uploaded (20)

Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024
 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistan
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 

Grails: a quick tutorial (1)

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7. Datasource dataSource { pooled = true driverClassName = &quot;org.hsqldb.jdbcDriver&quot; username = &quot;sa&quot; password = &quot;&quot; } hibernate { cache.use_second_level_cache=true cache.use_query_cache=true cache.provider_class='com.opensymphony.oscache.hibernate.OSCacheProvider' }
  • 8. Datasource environments { development { dataSource { dbCreate = &quot;create-drop&quot; // one of 'create', 'create-drop','update' url = &quot;jdbc:hsqldb:mem:devDB&quot; } } test { dataSource { dbCreate = &quot;update&quot; url = &quot;jdbc:hsqldb:mem:testDb&quot; } } production { dataSource { dbCreate = &quot;update&quot; url = &quot;jdbc:hsqldb:file:prodDb;shutdown=true&quot; } } }
  • 9.
  • 10. Team domain class > grails create-domain-class team class Team { String name String championship String city Long supporters Integer foundationYear static hasMany = [players: Player] static constraints = { name (unique: true, blank: false, size: 2..30) championship (blank: false, inList: ['Serie A', 'Serie B', 'Serie C1', 'Serie C2']) city (blank: true, size: 2..25) supporters (nullable: true) foundationYear (nullable: true) } }
  • 11. Player domain class > grails create-domain-class player class Player { String name String lastName String city Date birth String email static belongsTo = [team: Team] static constraints = { name (blank: false, size: 2..30) lastName (blank: false, size: 2..30) city (blank: true, size: 2..25) birth (nullable:true) email (blank: true, email: true) } }
  • 12. GORM Associations – One to one Unidirectional - class Person { BillingAddress address } class BillingAddress { ... } Bidirectional (no update / delete cascading) - class Person { BillingAddress address } class BillingAddress { Person person }
  • 13. GORM Associations – One to one Bidirectional (update / delete cascading) - class Person { BillingAddress address } class BillingAddress { static belongsTo = [person: Person] }
  • 14. GORM Associations – One to many Update cascading, no delete - class Team { static hasMany = [players: Player] } class Player { ... } Update / delete cascading - class Team { static hasMany = [players: Player] } class Player { static belongsTo = [team: Team] }
  • 15.
  • 16.
  • 17. Team controller > grails generate-controller team Every action is implemented as a closure
  • 18. Team controller - List def list = { if (!params.max) params.max = 10 [ teamList: Team.list( params ) ] } - params is a map containing the request parameters - list is a GORM method, can take parameters: - max, offset, order, sort, ignoreCase, fetch - No explicit return statement - No explicit view
  • 19. Team controller - Show def show = { def team = Team.get( params.id ) if (!team) { flash.message = &quot;Team not found with id ${params.id}&quot; redirect(action: list) } else { return [ team : team ] } } - get is a GORM method - flash is a new scope introduces by Grails - explicit return statement
  • 20. Team controller - Save def save = { def team = new Team(params) if(!team.hasErrors() && team.save()) { flash.message = &quot;Team ${team.id} created&quot; redirect(action: show, id: team.id) } else { render(view: 'create', model: [team: team]) } } - save performs validation and return null if errors are found - explicit view declarations (render)
  • 21. Generate controllers and view > grails generate-views team > grails generate-all player
  • 22. Running the app > grails run-app
  • 23. Application bootstrap def init = { servletContext -> if (GrailsUtil.environment == &quot;development&quot;) { def team1 = new Team(name: 'Varese', championship: 'Serie C2', city: 'Varese', supporters: 1000, foundationYear: 1910).save() def team2 = ... def team3 = ... def team4 = ... new Player(name: 'player1', lastName: 'one', city: 'Varese', birth: new Date(), email: 'player1@team.it', team: team1).save() ... new Player(name: 'player11', lastName: 'eleven', city: 'Palermo', birth: new Date(), email: 'player11@team.it', team: team4).save() } }
  • 24.
  • 25. Adding a search form team/list.gsp: <g:form name=”searchForm” action=”search”> Championship: <g:select name=”championship” from=”${Team.constraints.championship.inList}” /> <input type=”submit” value=”Search” /> </g:form>
  • 26. Dynamic finders teamController: def search = { def teamList = Team.findAllByChampionship(params.championship) render(view:'list', model: [teamList: teamList] ) }
  • 27. Dynamic finders - findBy and findAllBy - def team = Team.findByName(‘Juventus’) - def teamList = Team.findAllByCity(‘Milano’) - Composition of properties, boolean logic and comparators: - and, or - LessThan, LessThanEquals, GreaterThan, GreaterThanEquals, Like, Ilike, NotEqual, Between, IsNotNull, IsNull es: Team.findAllByCityLikeAndSupportersGreaterThan(‘Milano’, 1000, [max: 3, offset: 2, sort: &quot;name&quot;, order: &quot;desc&quot;])
  • 28. Multiaction search form <g:form name=&quot;searchForm&quot;> <input type=&quot;text&quot; name=&quot;searchQuery&quot; /> <g:actionSubmit value=&quot;Search by Team&quot; action=&quot;teamSearch&quot; /> <g:actionSubmit value=&quot;Search by City&quot; action=&quot;citySearch&quot; /> </g:form> - Each button sends the form to a different action
  • 29. Hql queries def teamSearch = { def playerList = Player.findAll(&quot;from Player as p where p.team.name = ?&quot;, [&quot;${params.searchQuery}&quot;]) render(view: 'list', model: [playerList: playerList]) } - You can use HQL queries with named and pagination parameters: es: Player.findAll(&quot;from Player as p where p.team.name = :teamName order by p.lastName asc&quot;, [teamName: &quot;Juventus&quot;], [max:10, offset:20])
  • 30. Hibernate Criteria def citySearch = { def c = Player.createCriteria() def playerList = c.list { ilike(&quot;city&quot;, &quot;%${params.searchQuery}%&quot;) } render(view: 'list', model: [playerList: playerList]) } - Uses a Groovy builder to hide Hibernate's Criteria API complexity
  • 31. Hibernate Criteria A more complex example: def c = Account.createCriteria() def results = c { like(&quot;holderFirstName&quot;, &quot;Fred%&quot;) and { between(&quot;balance&quot;, 500, 1000) eq(&quot;branch&quot;, &quot;London&quot;) } maxResults(10) order(&quot;holderLastName&quot;, &quot;desc&quot;) }
  • 32. Pagination problem After the search we see a pagination error: we must modify the generated action and gsp:
  • 33. Pagination <div class=&quot;paginateButtons&quot;> <g:paginate total=&quot;${playerCount}&quot; /> </div> def list = { if (!params.max) params.max = 10 def playerList = Player.list( params ) [ playerList: playerList, playerCount: Player.count()] }
  • 34. Pagination def teamSearch = { def playerList = Player.findAll(&quot;from Player as p where p.team.name = ?&quot;, [&quot;${params.searchQuery}&quot;]) render(view: 'list', model: [playerList: playerList, playerCount: playerList.size()]) } def citySearch = { def c = Player.createCriteria() def playerList = c.list { ilike(&quot;city&quot;, &quot;%${params.searchQuery}%&quot;) } render(view: 'list', model: [playerList: playerList, playerCount: playerList.size()]) }
  • 35.