If you can't read please download the document
Upload
davide-rossi
View
9.976
Download
0
Embed Size (px)
Citation preview
Blu scuro
Grails: a quick tutorial (1)
Davide Rossi
Jug Milano, 22/01/2009
Contents
- Grails: Groovy on Rails ?
- Create a new application
- Create domain classes
- Datasource
- Constraints
- Associations
- Create controllers / views
- Run the application
Contents (2)
- Searching
- Dynamic finders
- Hql (Hibernate query language)
- Hibernate Criteria
- Pagination
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
Creating a new application
A simple CRUD application, a teams / players database
> grails create-app JugDemo
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)
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
- 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)
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
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)
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
- Its possible to prevent some properties from being persisted: static transients = [ "myProperty" ]
- Provides events hooks: beforeInsert, beforeUpdate, onLoad...
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: '[email protected]', team: team1).save() ... new Player(name: 'player11', lastName: 'eleven', city: 'Palermo', birth: new Date(), email: '[email protected]', team: team4).save() }}
Search types
- Dynamic finders
- Hql
- Criteria
Adding a search form
team/list.gsp:
Championship:
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: "name", order: "desc"])
Multiaction search form
- Each button sends the form to a different action
Hql queries
def teamSearch = { def playerList = Player.findAll("from Player as p where p.team.name = ?", ["${params.searchQuery}"]) render(view: 'list', model: [playerList: playerList])}
- You can use HQL queries with named and pagination parameters:
es:Player.findAll("from Player as p where p.team.name = :teamName order by p.lastName asc", [teamName: "Juventus"], [max:10, offset:20])
Hibernate Criteria
def citySearch = { def c = Player.createCriteria() def playerList = c.list { ilike("city", "%${params.searchQuery}%") }
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("holderFirstName", "Fred%")and {between("balance", 500, 1000)eq("branch", "London")}maxResults(10)order("holderLastName", "desc")}
Pagination problem
After the search we see a pagination error: we must modify the generated action and gsp:
Pagination
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("from Player as p where p.team.name = ?", ["${params.searchQuery}"]) render(view: 'list', model: [playerList: playerList, playerCount: playerList.size()])}
def citySearch = { def c = Player.createCriteria() def playerList = c.list { ilike("city", "%${params.searchQuery}%") }
render(view: 'list', model: [playerList: playerList, playerCount: playerList.size()])}
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...)
Click to edit the title text format
Click to edit the outline text format
Second Outline Level
Third Outline Level
Fourth Outline Level
Fifth Outline Level
Sixth Outline Level
Seventh Outline Level
Eighth Outline Level
Ninth Outline Level
Click to edit the title text format
Click to edit the outline text format