groovy & grails - lecture 12
Upcoming SlideShare
Loading in...5
×
 

groovy & grails - lecture 12

on

  • 953 views

GORM relationships

GORM relationships
databases batches
application configuration

Statistics

Views

Total Views
953
Views on SlideShare
953
Embed Views
0

Actions

Likes
1
Downloads
24
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Apple Keynote

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • \n
  • \n
  • “terminal” shell session\nno export => variable is not passed to forked process\nbash is not the only shell\ncsh tcsh sh etc... with different notation variations\n
  • “terminal” shell session\nno export => variable is not passed to forked process\nbash is not the only shell\ncsh tcsh sh etc... with different notation variations\n
  • “terminal” shell session\nno export => variable is not passed to forked process\nbash is not the only shell\ncsh tcsh sh etc... with different notation variations\n
  • “terminal” shell session\nno export => variable is not passed to forked process\nbash is not the only shell\ncsh tcsh sh etc... with different notation variations\n
  • relationship are the heart of the domain architectures\nthe expression of the logic of how your objet are built and depends on each others\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • approximative....\n
  • approximative....\n
  • approximative....\n
  • Grails Objet Relational Mapping\nhide SQL complexity beyond a domain description\n...well to some extent\nDomain self constraints in static constraints={}\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • instead of belongsTo = [user:Person]\nconstraints user(nullable:true)\n
  • instead of belongsTo = [user:Person]\nconstraints user(nullable:true)\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • cf last time\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Bean list declaration is symmetrical.\nGORM offers the possibility to put a direction of dependency\n
  • Tag only have a name, which is a unique constrained property\n
  • belongs : directions\n
  • belongs : directions\n
  • belongs : directions\n
  • \n
  • see MessageIntegrationTests / testTags() for more tests and examples\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • i.e. stay on disk, survive restart run-app\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • batch= automatic =1/manual\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • i.e. stay on disk, survive restart run-app\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n

groovy & grails - lecture 12 groovy & grails - lecture 12 Presentation Transcript

  • Groovy: Efficiency Oriented ProgrammingLecture 12Master Proteomics & Bioinformatics - University of GenevaAlexandre Masselot - summer 2011
  • Agenda‣ Linux tip-of-the-day‣ GORM relationships‣ Database batches‣ Application configuration
  • Linux tip-of-the-day: bash environment variables‣ Set a variable to be read by further terminal process export GRAILS_HOME=$HOME/local/grails-1.3.7
  • Linux tip-of-the-day: bash environment variables‣ Set a variable to be read by further terminal process export GRAILS_HOME=$HOME/local/grails-1.3.7‣ Prepend the path to give priority: export PATH=$GRAILS_HOME/bin:$PATH
  • Linux tip-of-the-day: bash environment variables‣ Set a variable to be read by further terminal process export GRAILS_HOME=$HOME/local/grails-1.3.7‣ Prepend the path to give priority: export PATH=$GRAILS_HOME/bin:$PATH‣ Variables are not shared among “parallel” consoles
  • Linux tip-of-the-day: bash environment variables‣ Set a variable to be read by further terminal process export GRAILS_HOME=$HOME/local/grails-1.3.7‣ Prepend the path to give priority: export PATH=$GRAILS_HOME/bin:$PATH‣ Variables are not shared among “parallel” consoles‣ To set variable a bash startup, edit either ~/.bashrc ~/.profile
  • Objects dependancies ↔ domain relationships (↔ foreign keys constraints)
  • Different type of relationships
  • Different type of relationships‣ one-to-one
  • Different type of relationships‣ one-to-one‣ one-to-many
  • Different type of relationships‣ one-to-one‣ one-to-many‣ many-to-many
  • The pedestrian one-to-many‣Message.groovyPerson commiter
  • The pedestrian one-to-many‣Message.groovyPerson commiter‣ Person.groovySet<Messages> messages=[]def addToMessages(msg){ msg.commiter=this msg.save() messages.add(msg)}
  • The pedestrian one-to-many‣Message.groovyPerson commiter‣ Person.groovySet<Messages> messages=[]def addToMessages(msg){ msg.commiter=this msg.save() messages.add(msg)}‣ And we do not mention - added messages already has a commiter member - cascade deletion
  • GORM: do not care about SQL/Hibernate
  • Dependency to a single bean: typed member
  • Dependency to a list of beans: hasMany
  • Dependency direction: belongsTo
  • One-to-one relationship
  • Example: a Profile object with details for a user 1 Person ↔ 0:1 Profile
  • one-to-one (cont’d)
  • one-to-one (cont’d)class Profile { String homePage static constraints = { homePage(url:true, blank:false) }}class Person { ... Profile profile static constraints = { ... profile(nullable:true) }}
  • one-to-one (cont’d)class Profile { String homePage static belongsTo = Person static constraints = { homePage(url:true, blank:false) }}class Person { ... Profile profile static constraints = { ... profile(nullable:true) }}
  • one-to-one (cont’d) ‣ Setting a profile joe.profile=new Profile(url=‘http://www.example.com’) joe.save()
  • one-to-one (cont’d) ‣ Setting a profile joe.profile=new Profile(url=‘http://www.example.com’) joe.save() ‣ Deleting cascades: joe.delete()
  • one-to-one (end) void testProfile(){ assert Person.count() == 0 assert Profile.count() == 0 }
  • one-to-one (end) void testProfile(){ assert Person.count() == 0 assert Profile.count() == 0 Person p=new Person(firstName:Lucky, lastName:Luke, email:lucky.luke@morris.com, username:lucky_luke ).save(failOnError:true) assert Person.count() == 1 assert Profile.count() == 0 }
  • one-to-one (end) void testProfile(){ assert Person.count() == 0 assert Profile.count() == 0 Person p=new Person(firstName:Lucky, lastName:Luke, email:lucky.luke@morris.com, username:lucky_luke ).save(failOnError:true) assert Person.count() == 1 assert Profile.count() == 0 p.profile = new Profile(homePage:http://www.morris.com/~lucky-luke) p.save(failOnError:true) assert Person.count() == 1 assert Profile.count() == 1 }
  • one-to-one (end) void testProfile(){ assert Person.count() == 0 assert Profile.count() == 0 Person p=new Person(firstName:Lucky, lastName:Luke, email:lucky.luke@morris.com, username:lucky_luke ).save(failOnError:true) assert Person.count() == 1 assert Profile.count() == 0 p.profile = new Profile(homePage:http://www.morris.com/~lucky-luke) p.save(failOnError:true) assert Person.count() == 1 assert Profile.count() == 1 p.delete() assert Person.count() == 0 assert Profile.count() == 0 }
  • One-to-many relationship
  • Example1 Person ↔ n Message
  • one-to-many‣ Person.groovy static hasMany = [messages: Message]
  • one-to-many‣ Person.groovy static hasMany = [messages: Message]‣ Message.groovy static belongsTo = [commiter:Person]
  • one-to-many‣ Person.groovy static hasMany = [messages: Message]‣ Message.groovy static belongsTo = [commiter:Person]‣ Add one messagejoe.addToMessages(new Message(...)).save()
  • one-to-many‣ Person.groovy static hasMany = [messages: Message]‣ Message.groovy static belongsTo = [commiter:Person]‣ Add one messagejoe.addToMessages(new Message(...)).save()‣ Delete on messagejoe.removeFromMessages(my_msg).save()
  • one-to-many‣ Person.groovy static hasMany = [messages: Message]‣ Message.groovy static belongsTo = [commiter:Person]‣ Add one message joe.addToMessages(new Message(...)).save()‣ Delete on message joe.removeFromMessages(my_msg).save()‣ Delete person + all messages joe.delete()
  • many-to-many relationships
  • Example: a Message contains many Tag and vice-versa n Message ↔ m Tag
  • many-to-many‣ Message.groovy:static hasMany = [tags:Tag]
  • many-to-many‣ Message.groovy:static hasMany = [tags:Tag]‣ Tag.groovy:class Tag{ String name static hasMany = [messages:Message] constraints = { name(unique:true) }}
  • many-to-many‣ Message.groovy:static hasMany = [tags:Tag]‣ Tag.groovy:class Tag{ String name belongsTo = Message static hasMany = [messages:Message] constraints = { name(unique:true) }}
  • belongsTo: who adds who?
  • my_msg.addToTags(my_tag).save()
  • Self referencing domain
  • Twitter example: a Person is following others
  • static hasMany = [ messages: Message, followings: Person ]
  • Application configuration: beyond default
  • Three environments:development (run-app)test (test-app -integration)production (war deployment)
  • Goal: a persistent development database
  • DataSource.groovy‣ All database configuration is stored under grails-app/conf/DataSource.groovy
  • DataSource.groovy‣ All database configuration is stored under grails-app/conf/DataSource.groovy‣ Global vs environment configuration dataSource { pooled = true driverClassName = "org.hsqldb.jdbcDriver" username = "sa" password = "" }
  • DataSource.groovy (cont’d)environments { development { dataSource { // one of create, create-drop,update dbCreate = "create-drop" url = "jdbc:hsqldb:mem:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } }}
  • DataSource.groovy (cont’d)environments { development { dataSource { // one of create, create-drop,update dbCreate = "create-drop" url = "jdbc:hsqldb:mem:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } }}
  • DataSource.groovy (cont’d)environments { development { dataSource { // one of create, create-drop,update dbCreate = "create-drop" url = "jdbc:hsqldb:mem:devDB" "jdbc:hsqldb:file:devDb:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } }}
  • DataSource.groovy (cont’d)environments { development { dataSource { // one of create, create-drop,update dbCreate = "create-drop" url = "jdbc:hsqldb:mem:devDB" "jdbc:hsqldb:file:devDb:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } }}
  • DataSource.groovy (cont’d)environments { development { dataSource { // one of create, create-drop,update dbCreate = "create-drop" "update" url = "jdbc:hsqldb:mem:devDB" "jdbc:hsqldb:file:devDb:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } }}
  • DataSource.groovy (cont’d)environments { development { dataSource { // one of create, create-drop,update dbCreate = "create-drop" "update" url = "jdbc:hsqldb:mem:devDB" "jdbc:hsqldb:file:devDb:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } }}
  • hsqldb on file‣ Database is saved in 3 flat files
  • hsqldb on file‣ Database is saved in 3 flat files‣ e.g. devDb.script CREATE SCHEMA PUBLIC AUTHORIZATION DBA CREATE MEMORY TABLE MESSAGE(ID BIGINT GENERATED ... CREATE MEMORY TABLE PERSON(ID BIGINT GENERATED ... ALTER TABLE MESSAGE ADD CONSTRAINT FK38EB0007D6D0B12 FOREIGN KEY(COMMITER_ID) REFERENCES PERSON(ID) ALTER TABLE MESSAGE ALTER COLUMN ID RESTART WITH 3 ALTER TABLE PERSON ALTER COLUMN ID RESTART WITH 2 CREATE USER SA PASSWORD "" GRANT DBA TO SA SET WRITE_DELAY 10 SET SCHEMA PUBLIC INSERT INTO MESSAGE VALUES(1,1,Im coming,1) INSERT INTO MESSAGE VALUES(2,0,on Jolly Jumper,1) INSERT INTO PERSON VALUES(1,0,Luke,lucky_luke, lucky.luke@morris.com,2010-05-17 01:18:16.607000000,Lucky)
  • More configuration‣ All configuration files reside into grails-app/conf
  • More configuration‣ All configuration files reside into grails-app/conf‣ Config.groovy handles much
  • More configuration‣ All configuration files reside into grails-app/conf‣ Config.groovy handles much‣ For example, to set failOnError:true by default on all .save() callsgrails.gorm.save.failOnError = true
  • Populating the database with batch
  • Inserting elements at application start: BootStrap.groovy
  • grails-app/conf/BootStrap.groovyclass BootStrap { def init = { servletContext -> } def destroy = { }}
  • grails-app/conf/BootStrap.groovyclass BootStrap { def init = { servletContext -> environments { test { } development { } production { } } } def destroy = { }}
  • grails-app/conf/BootStrap.groovyimport eop.lec12.twitter.Personimport eop.lec12.twitter.Profileclass BootStrap { def init = { servletContext -> environments { test { } development { if(!Person.findByUsername(sci_am)){ def p=new Person(firstName:Scientific, lastName:American, email:info@sciam.com, username:sci_am) p.save() p.profile=new Profile(homePage:http://www.sciam.com).save() } } production { } } } def destroy = { }}
  • grails-app/conf/BootStrap.groovyimport eop.lec12.twitter.Personimport eop.lec12.twitter.Profileclass BootStrap { def init = { servletContext -> environments { test { } development { if(!Person.findByUsername(sci_am)){ def p=new Person(firstName:Scientific, lastName:American,email:info@sciam.com, username:sci_am) p.save() p.profile=new Profile(homePage:http://www.sciam.com).save() } } production { } } } def destroy = { }}
  • Another batch: uploading a list of messages
  • Uploading a file
  • Uploading a file1. a local file with a list of contents convertible into messages (rss feed with publication update from Scientific American & Nature)
  • Uploading a file1. a local file with a list of contents convertible into messages (rss feed with publication update from Scientific American & Nature)2. file is uploaded through a web page with a form
  • Uploading a file1. a local file with a list of contents convertible into messages (rss feed with publication update from Scientific American & Nature)2. file is uploaded through a web page with a form3. a controller/action receive data + params
  • Uploading a file1. a local file with a list of contents convertible into messages (rss feed with publication update from Scientific American & Nature)2. file is uploaded through a web page with a form3. a controller/action receive data + params4. launch a service action to parse file & insert into database
  • Uploading a file1. a local file with a list of contents convertible into messages (rss feed with publication update from Scientific American & Nature)2. file is uploaded through a web page with a form3. a controller/action receive data + params4. launch a service action to parse file & insert into database
  • Input file
  • Input file‣ Download Scientific American’s feedhttp://rss.sciam.com/ScientificAmerican-Global
  • Input file‣ Download Scientific American’s feed http://rss.sciam.com/ScientificAmerican-Global‣ Nature’s feed http://feeds.nature.com/nature/rss/current?format=xml
  • Input file‣ Download Scientific American’s feed http://rss.sciam.com/ScientificAmerican-Global‣ Nature’s feed http://feeds.nature.com/nature/rss/current?format=xml‣ Stored locally in test/data/
  • Input file‣ Download Scientific American’s feed http://rss.sciam.com/ScientificAmerican-Global‣ Nature’s feed http://feeds.nature.com/nature/rss/current?format=xml‣ Stored locally in test/data/‣ Two loader classes (with tests) to convert rss item into temporary MessageToLoad class src/groovy
  • http://localhost:8080/eop.lec12.twitter/message/loadListgrails-app/controllers/MessageController.groovy : loadList = {...}grails-app/views/message/loadList.gsp
  • loadList.gspenable the form to upload file <g:form controller="message" action="loadList" method="post” enctype="multipart/form-data"> <input type="file" name="messagefile"/> <g:select name="type" from="${[sciam, nature] }"/> <input type="submit"/> </g:form> form field with file data
  • MessageController.groovy def loadList = { }
  • MessageController.groovy def loadList = { if(!params.type){ // the controller is called without parameter // thus we just display the form return } }
  • MessageController.groovy def loadList = { if(!params.type){ // the controller is called without parameter // thus we just display the form return } def loaders=[ sciam:new ScientificAmericanMessageLoader(), nature:new NatureMessageLoader() ] def loader=loaders[params.type] }
  • MessageController.groovy def loadList = { if(!params.type){ // the controller is called without parameter // thus we just display the form return } def loaders=[ sciam:new ScientificAmericanMessageLoader(), nature:new NatureMessageLoader() ] def loader=loaders[params.type] def input=request.getFile("messagefile").inputStream }
  • MessageController.groovy def loadList = { if(!params.type){ // the controller is called without parameter // thus we just display the form return } def loaders=[ sciam:new ScientificAmericanMessageLoader(), nature:new NatureMessageLoader() ] def loader=loaders[params.type] def input=request.getFile("messagefile").inputStream def n=messageService.loadMessages(loader, input) }
  • MessageController.groovy def loadList = { if(!params.type){ // the controller is called without parameter // thus we just display the form return } def loaders=[ sciam:new ScientificAmericanMessageLoader(), nature:new NatureMessageLoader() ] def loader=loaders[params.type] def input=request.getFile("messagefile").inputStream def n=messageService.loadMessages(loader, input) flash.uploadedMessage= "file was correctly uploaded with $n entries" }
  • MessageService‣ generate grails-app/services/MessageService.groovycreate-service message
  • MessageService‣ generate grails-app/services/MessageService.groovy create-service message‣ In MessageController.groovy, service is referred simply with def messageService
  • MessageService‣ generate grails-app/services/MessageService.groovy create-service message‣ In MessageController.groovy, service is referred simply with def messageService‣ See project source for actual bean insertion
  • Extracting a bean list
  • All messages: Message.list()
  • Exact MatchretList=Message.findAllByCommiter( Person.findByUsername(params.username) )
  • Text with wildcard: %retList=Message.findAllByTextLike("%$params.text%")
  • Match with comparatorretList=Message.findAllByDateGreaterThan(params.date)
  • Multiple constraintsretList=Message.findAllByTextLikeAndDateGreaterThan( "%$params.text%", params.date )
  • PaginationretList=Message.findAllByTextLike("%$params.text%", [max:10, offset:19, sort:‘date’, order:‘desc’] )
  • More power:Hibernate criteria & HQL
  • Application configuration: beyond default
  • Three environments:development (run-app)test (test-app -integration)production (war deployment)
  • Goal: a persistent development database
  • DataSource.groovy‣ All database configuration is stored under grails-app/conf/DataSource.groovy
  • DataSource.groovy‣ All database configuration is stored under grails-app/conf/DataSource.groovy‣ Global vs environment configuration dataSource { pooled = true driverClassName = "org.hsqldb.jdbcDriver" username = "sa" password = "" }
  • DataSource.groovy (cont’d)environments { development { dataSource { // one of create, create-drop,update dbCreate = "create-drop" url = "jdbc:hsqldb:mem:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } }}
  • DataSource.groovy (cont’d)environments { development { dataSource { // one of create, create-drop,update dbCreate = "create-drop" url = "jdbc:hsqldb:mem:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } }}
  • DataSource.groovy (cont’d)environments { development { dataSource { // one of create, create-drop,update dbCreate = "create-drop" url = "jdbc:hsqldb:mem:devDB" "jdbc:hsqldb:file:devDb:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } }}
  • DataSource.groovy (cont’d)environments { development { dataSource { // one of create, create-drop,update dbCreate = "create-drop" url = "jdbc:hsqldb:mem:devDB" "jdbc:hsqldb:file:devDb:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } }}
  • DataSource.groovy (cont’d)environments { development { dataSource { // one of create, create-drop,update dbCreate = "create-drop" "update" url = "jdbc:hsqldb:mem:devDB" "jdbc:hsqldb:file:devDb:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } }}
  • DataSource.groovy (cont’d)environments { development { dataSource { // one of create, create-drop,update dbCreate = "create-drop" "update" url = "jdbc:hsqldb:mem:devDB" "jdbc:hsqldb:file:devDb:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } }}
  • hsqldb on file‣ Database is saved in 3 flat files
  • hsqldb on file‣ Database is saved in 3 flat files‣ e.g. devDb.script CREATE SCHEMA PUBLIC AUTHORIZATION DBA CREATE MEMORY TABLE MESSAGE(ID BIGINT GENERATED ... CREATE MEMORY TABLE PERSON(ID BIGINT GENERATED ... ALTER TABLE MESSAGE ADD CONSTRAINT FK38EB0007D6D0B12 FOREIGN KEY(COMMITER_ID) REFERENCES PERSON(ID) ALTER TABLE MESSAGE ALTER COLUMN ID RESTART WITH 3 ALTER TABLE PERSON ALTER COLUMN ID RESTART WITH 2 CREATE USER SA PASSWORD "" GRANT DBA TO SA SET WRITE_DELAY 10 SET SCHEMA PUBLIC INSERT INTO MESSAGE VALUES(1,1,Im coming,1) INSERT INTO MESSAGE VALUES(2,0,on Jolly Jumper,1) INSERT INTO PERSON VALUES(1,0,Luke,lucky_luke, lucky.luke@morris.com,2010-05-17 01:18:16.607000000,Lucky)
  • More configuration‣ All configuration files reside into grails-app/conf
  • More configuration‣ All configuration files reside into grails-app/conf‣ Config.groovy handles much
  • More configuration‣ All configuration files reside into grails-app/conf‣ Config.groovy handles much‣ For example, to set failOnError:true by default on all .save() callsgrails.gorm.save.failOnError = true