Grails: a quick tutorial (1) Davide Rossi <ul><li>Jug Milano, 22/01/2009 </li></ul>
Contents <ul><li>- Grails: Groovy on Rails ? </li></ul><ul><li>- Create a new application </li></ul><ul><li>- Create domai...
Contents (2) <ul><li>- Searching </li></ul><ul><ul><ul><li>- Dynamic finders </li></ul></ul></ul><ul><ul><ul><li>- Hql (Hi...
Grails: Groovy on Rails ? <ul><li>- Web MVC framework based on Groovy </li></ul><ul><li>- Inspired by Ruby on Rails </li><...
Creating a new application <ul><li>A simple CRUD application, a teams / players database </li></ul>> grails create-app Jug...
Domain classes and GORM <ul><li>- Domain classes are the model layer of the    application </li></ul><ul><li>- Every domai...
Datasource dataSource { pooled = true driverClassName = &quot;org.hsqldb.jdbcDriver&quot; username = &quot;sa&quot; passwo...
Datasource environments { development { dataSource { dbCreate = &quot;create-drop&quot; // one of 'create', 'create-drop',...
Constraints <ul><li>- Grails validation (from Spring) </li></ul><ul><li>- Used by GORM to generate DB schema </li></ul><ul...
Team domain class > grails create-domain-class team  class Team { String name String championship String city Long support...
Player domain class >  grails create-domain-class player class Player { String name String lastName String city Date birth...
GORM Associations – One to one Unidirectional -  class Person { BillingAddress address } class BillingAddress { ... } Bidi...
GORM Associations – One to one Bidirectional (update / delete cascading) -  class Person { BillingAddress address } class ...
GORM Associations – One to many Update cascading, no delete -  class Team { static hasMany = [players: Player] } class Pla...
GORM Associations – Many to many <ul><li>Update / delete cascading </li></ul><ul><li>-  class Author { </li></ul><ul><li>s...
GORM notes <ul><li>- Default fetching strategy is lazy but can be changed </li></ul><ul><li>- Default inheritance strategy...
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...
Team controller - Show def show = { def team = Team.get( params.id ) if (!team) { flash.message = &quot;Team not found wit...
Team controller - Save def save = { def team = new Team(params) if(!team.hasErrors() && team.save()) { flash.message = &qu...
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 == &quot;development&quot;) {  def team1 =...
Search types <ul><li>- Dynamic finders </li></ul><ul><li>- Hql </li></ul><ul><li>- Criteria </li></ul>
Adding a search form team/list.gsp: <g:form name=”searchForm” action=”search”> Championship: <g:select name=”championship”...
Dynamic finders teamController: def search = { def teamList =  Team.findAllByChampionship(params.championship) render(view...
Dynamic finders - findBy and findAllBy - def team = Team.findByName(‘Juventus’) - def teamList = Team.findAllByCity(‘Milan...
Multiaction search form <g:form name=&quot;searchForm&quot;> <input type=&quot;text&quot; name=&quot;searchQuery&quot; /> ...
Hql queries def teamSearch = { def playerList = Player.findAll(&quot;from Player as p where  p.team.name = ?&quot;, [&quot...
Hibernate Criteria def citySearch = { def c = Player.createCriteria() def playerList = c.list { ilike(&quot;city&quot;, &q...
Hibernate Criteria A more complex example: def c = Account.createCriteria() def results = c { like(&quot;holderFirstName&q...
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 ...
Pagination def teamSearch = { def playerList = Player.findAll(&quot;from Player as p where p.team.name = ?&quot;,  [&quot;...
Conclusions <ul><li>- Convention over configuration speeds up development  by reducing glue and framework code </li></ul><...
Upcoming SlideShare
Loading in …5
×

Grails: a quick tutorial (1)

10,019 views

Published on

The first part of a Grails tutorial (mainly about Domain Classes and Gorm) presented to the Milano JUG.

Published in: Technology
0 Comments
8 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
10,019
On SlideShare
0
From Embeds
0
Number of Embeds
161
Actions
Shares
0
Downloads
224
Comments
0
Likes
8
Embeds 0
No embeds

No notes for slide

Grails: a quick tutorial (1)

  1. 1. Grails: a quick tutorial (1) Davide Rossi <ul><li>Jug Milano, 22/01/2009 </li></ul>
  2. 2. Contents <ul><li>- Grails: Groovy on Rails ? </li></ul><ul><li>- Create a new application </li></ul><ul><li>- Create domain classes </li></ul><ul><ul><ul><li>- Datasource </li></ul></ul></ul><ul><ul><ul><li>- Constraints </li></ul></ul></ul><ul><ul><ul><li>- Associations </li></ul></ul></ul><ul><li>- Create controllers / views </li></ul><ul><li>- Run the application </li></ul>
  3. 3. Contents (2) <ul><li>- Searching </li></ul><ul><ul><ul><li>- Dynamic finders </li></ul></ul></ul><ul><ul><ul><li>- Hql (Hibernate query language) </li></ul></ul></ul><ul><ul><ul><li>- Hibernate Criteria </li></ul></ul></ul><ul><li>- Pagination </li></ul>
  4. 4. Grails: Groovy on Rails ? <ul><li>- Web MVC framework based on Groovy </li></ul><ul><li>- Inspired by Ruby on Rails </li></ul><ul><li>- Built on top of Spring, Spring MVC, Hibernate </li></ul><ul><li>- Convention over Configuration </li></ul><ul><li>- Integrated Jetty Server and HSQL DB for rapid development </li></ul><ul><li>- Produces a WAR </li></ul>
  5. 5. Creating a new application <ul><li>A simple CRUD application, a teams / players database </li></ul>> grails create-app JugDemo
  6. 6. Domain classes and GORM <ul><li>- Domain classes are the model layer of the application </li></ul><ul><li>- Every domain class is automatically persisted by GORM (Grails' Object Relational Mapping) </li></ul><ul><li>- GORM is based on Hibernate </li></ul><ul><li>- GORM can persist Java classes </li></ul><ul><li>- GORM adds id and version properties </li></ul><ul><li>- GORM creates DB schema from Domain classes </li></ul><ul><li>- GORM allows to configure table mapping (useful for legacy DB) </li></ul>
  7. 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. 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. 9. Constraints <ul><li>- Grails validation (from Spring) </li></ul><ul><li>- Used by GORM to generate DB schema </li></ul><ul><li>- To validate a class instance call validate() </li></ul><ul><li>- save() calls validate() before calling the DB </li></ul><ul><li>- blank, creditCard, email, inList, matches, max, maxSize, min, notEqual, nullable, range, scale, size, unique, url, validator (custom closure) </li></ul>
  10. 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. 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. 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. 13. GORM Associations – One to one Bidirectional (update / delete cascading) - class Person { BillingAddress address } class BillingAddress { static belongsTo = [person: Person] }
  14. 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. 15. GORM Associations – Many to many <ul><li>Update / delete cascading </li></ul><ul><li>- class Author { </li></ul><ul><li>static hasMany = [books: Book] </li></ul><ul><li>} </li></ul><ul><li>class Book { </li></ul><ul><li>static belongsTo = Author </li></ul><ul><li>static hasMany = [authors: Author] </li></ul><ul><li>} </li></ul><ul><ul><li>- GORM creates a join table with the entitiy names (author_book) </li></ul></ul>
  16. 16. GORM notes <ul><li>- Default fetching strategy is lazy but can be changed </li></ul><ul><li>- Default inheritance strategy is table per hierarchy, but can be changed to table per class </li></ul><ul><li>- It’s possible to prevent some properties from being persisted: static transients = [ &quot;myProperty&quot; ] </li></ul><ul><li>- Provides events hooks: beforeInsert, beforeUpdate, onLoad... </li></ul>
  17. 17. Team controller > grails generate-controller team Every action is implemented as a closure
  18. 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. 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. 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. 21. Generate controllers and view > grails generate-views team > grails generate-all player
  22. 22. Running the app > grails run-app
  23. 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. 24. Search types <ul><li>- Dynamic finders </li></ul><ul><li>- Hql </li></ul><ul><li>- Criteria </li></ul>
  25. 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. 26. Dynamic finders teamController: def search = { def teamList = Team.findAllByChampionship(params.championship) render(view:'list', model: [teamList: teamList] ) }
  27. 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. 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. 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. 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. 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. 32. Pagination problem After the search we see a pagination error: we must modify the generated action and gsp:
  33. 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. 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. 35. Conclusions <ul><li>- Convention over configuration speeds up development by reducing glue and framework code </li></ul><ul><li>- GORM is a powerful tool that leverage Hibernate strengths but lowering its complexity </li></ul><ul><li>- You can reuse your previous knowledge (HQL, Criteria, Spring Validation...) </li></ul>

×