Grails: a quick tutorial (1)
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share

Grails: a quick tutorial (1)

  • 12,276 views
Uploaded on

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

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

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
12,276
On Slideshare
12,139
From Embeds
137
Number of Embeds
8

Actions

Shares
Downloads
180
Comments
0
Likes
6

Embeds 137

http://daviderossi.blogspot.com 69
http://www.slideshare.net 50
http://daviderossi.blogspot.it 9
http://www.slideee.com 5
http://www.naymz.com 1
http://daviderossi.blogspot.in 1
http://www.linkedin.com 1
http://daviderossi.blogspot.ch 1

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Grails: a quick tutorial (1) Davide Rossi
    • Jug Milano, 22/01/2009
  • 2. Contents
    • - Grails: Groovy on Rails ?
    • - Create a new application
    • - Create domain classes
        • - Datasource
        • - Constraints
        • - Associations
    • - Create controllers / views
    • - Run the application
  • 3. Contents (2)
    • - Searching
        • - Dynamic finders
        • - Hql (Hibernate query language)
        • - Hibernate Criteria
    • - Pagination
  • 4. Grails: Groovy on Rails ?
    • - Web MVC framework based on Groovy
    • - Inspired by Ruby on Rails
    • - Built on top of Spring, Spring MVC, Hibernate
    • - Convention over Configuration
    • - Integrated Jetty Server and HSQL DB for rapid development
    • - Produces a WAR
  • 5. Creating a new application
    • A simple CRUD application, a teams / players database
    > grails create-app JugDemo
  • 6. Domain classes and GORM
    • - Domain classes are the model layer of the application
    • - Every domain class is automatically persisted by GORM (Grails' Object Relational Mapping)
    • - GORM is based on Hibernate
    • - GORM can persist Java classes
    • - GORM adds id and version properties
    • - GORM creates DB schema from Domain classes
    • - GORM allows to configure table mapping (useful for legacy DB)
  • 7. 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' }
  • 8. 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" } } }
  • 9. Constraints
    • - Grails validation (from Spring)
    • - Used by GORM to generate DB schema
    • - To validate a class instance call validate()
    • - save() calls validate() before calling the DB
    • - blank, creditCard, email, inList, matches, max, maxSize, min, notEqual, nullable, range, scale, size, unique, url, validator (custom closure)
  • 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. GORM Associations – Many to many
    • Update / delete cascading
    • - class Author {
    • static hasMany = [books: Book]
    • }
    • class Book {
    • static belongsTo = Author
    • static hasMany = [authors: Author]
    • }
      • - GORM creates a join table with the entitiy names (author_book)
  • 16. GORM notes
    • - Default fetching strategy is lazy but can be changed
    • - Default inheritance strategy is table per hierarchy, but can be changed to table per class
    • - It’s possible to prevent some properties from being persisted: static transients = [ "myProperty" ]
    • - Provides events hooks: beforeInsert, beforeUpdate, onLoad...
  • 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 = "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
  • 20. 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)
  • 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 == "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() } }
  • 24. Search types
    • - Dynamic finders
    • - Hql
    • - Criteria
  • 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. Conclusions
    • - Convention over configuration speeds up development by reducing glue and framework code
    • - GORM is a powerful tool that leverage Hibernate strengths but lowering its complexity
    • - You can reuse your previous knowledge (HQL, Criteria, Spring Validation...)