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

groovy & grails - lecture 11

on

  • 438 views

CRUD

CRUD
Integration tests
Domain relationships
Application configuration

Statistics

Views

Total Views
438
Slideshare-icon Views on SlideShare
438
Embed Views
0

Actions

Likes
1
Downloads
15
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
  • \n
  • run-app generate the database\n
  • Domain bean class generate a table from the member fields\n
  • map one object SQL statements\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • id property is added by default to domain beans\nsetting the bean joe.id=123 is dangerous\n
  • \n
  • necessitate to know the beanId value\ntypically used with url show/id\n
  • \n
  • possible to configure sort on other condition in domain class definition\nunique:true => table index\n
  • possible to configure sort on other condition in domain class definition\nunique:true => table index\n
  • possible to configure sort on other condition in domain class definition\nunique:true => table index\n
  • if not valid => bean remains the same in the database\n
  • if not valid => bean remains the same in the database\n
  • if not valid => bean remains the same in the database\n
  • \n
  • \n
  • do not call generate-controller too early in development phase\n
  • do not call generate-controller too early in development phase\n
  • do not call generate-controller too early in development phase\n
  • \n
  • \n
  • database\nsame situation ~ than with run-app\n
  • PersonIntegrationTests instead PersonTests to avoid class name conflict\n
  • PersonIntegrationTests instead PersonTests to avoid class name conflict\n
  • PersonIntegrationTests instead PersonTests to avoid class name conflict\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • see error groovy 1.6 on the storyboard\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • This is not the correct way!\n
  • This is not the correct way!\n
  • \n
  • \n
  • \n
  • grails will provide the mechanism for consistency\n
  • \n
  • \n
  • addToMessages is implicitely created\n
  • addToMessages is implicitely created\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n

groovy & grails - lecture 11 groovy & grails - lecture 11 Presentation Transcript

  • Groovy: Efficiency Oriented ProgrammingLecture 11Master Proteomics & Bioinformatics - University of GenevaAlexandre Masselot - summer 2011
  • Agenda‣ CRUD‣ Integration tests‣ Domain relationships‣ Application configuration
  • One domain based app ↔ one database
  • One domain class ↔ one table
  • One domain bean ↔ one table entry
  • One bean operations: CRUD
  • One bean operations: CRUD Action Create Read Update Delete
  • One bean operations: CRUD Action Create Read Update Delete
  • One bean operations: CRUD Action Create Read Update Delete
  • One bean operations: CRUD Action Create Read Update Delete
  • One bean operations: CRUD Action SQL Grails url Create INSERT create Read SELECT show Update UPDATE update Delete DELETE delete
  • One bean operations: CRUD Action SQL Grails url Create INSERT create Read SELECT show Update UPDATE update Delete DELETE delete
  • One bean operations: CRUD Action SQL Grails url Create INSERT create Read SELECT show Update UPDATE update Delete DELETE delete
  • Person joe = new Person(params) ➙ bean but no database entry creation
  • joe.save() ➙ insertion into table(only if valid bean - constraints)
  • Validation‣ Create only a valid bean‣ 3 validation ways
  • Validation‣ Create only a valid bean‣ 3 validation ways‣ Check explicitly for validation joe.validate()
  • Validation‣ Create only a valid bean‣ 3 validation ways‣ Check explicitly for validation joe.validate()‣ Save a catch exception joe.save(failOnError:true)
  • Validation‣ Create only a valid bean‣ 3 validation ways‣ Check explicitly for validation joe.validate()‣ Save a catch exception joe.save(failOnError:true)‣ Save a check for non-null return assert joe.save()
  • Registered bean ⇔ joe.id != null
  • Reading a bean from the database
  • joe = Person.get(beanId)
  • Dynamic finders: retrieve from constraints
  • Dynamic finders (for single return)‣ Domain class definition generate static methods def p = Person.findByUsername(‘lucky_luke’) def p = Person.findByFirstName(‘Lucky’) def p = Person.findByFirstNameAndLastName(‘Joe’, ‘Dalton’)
  • Dynamic finders (for single return)‣ Domain class definition generate static methods def p = Person.findByUsername(‘lucky_luke’) def p = Person.findByFirstName(‘Lucky’) def p = Person.findByFirstNameAndLastName(‘Joe’, ‘Dalton’)‣ Multiple results => returns first (sorted on id) def p = Person.findByLastName(‘Dalton’)
  • Dynamic finders (for single return)‣ Domain class definition generate static methods def p = Person.findByUsername(‘lucky_luke’) def p = Person.findByFirstName(‘Lucky’) def p = Person.findByFirstNameAndLastName(‘Joe’, ‘Dalton’)‣ Multiple results => returns first (sorted on id) def p = Person.findByLastName(‘Dalton’)‣ findByXxxx efficient with unique:true fields
  • Update ‣ Update: change fields values and save into database
  • Update ‣ Update: change fields values and save into database 1. modify bean as usual
  • Update ‣ Update: change fields values and save into database 1. modify bean as usual 2. validate/save as for creation
  • joe.delete() removes entry from table
  • Scaffolded controller hides CRUD operations
  • Explicit controller‣ It is possible to generate scaffold controller code generate-controller eop.lec11.twitter.Person
  • Explicit controller‣ It is possible to generate scaffold controller code generate-controller eop.lec11.twitter.Person‣ PersonController.groovy write operation & test
  • Explicit controller‣ It is possible to generate scaffold controller code generate-controller eop.lec11.twitter.Person‣ PersonController.groovy write operation & test‣ For example, read: def show = { def personInstance = Person.get(params.id) if (!personInstance) { flash.message = "${message(code: default.not.found.message, .....)}" redirect(action: "list") } else { [personInstance: personInstance] } }
  • Time to go back to test!
  • Unit testing ↔ no dependency
  • Integration testing ↔ more complex biotope
  • Integration tests‣ Resides under test/integration/PersonIntegrationTests.groovy
  • Integration tests‣ Resides under test/integration/PersonIntegrationTests.groovy‣ Launched withtest-app -integration
  • Integration tests‣ Resides under test/integration/ PersonIntegrationTests.groovy‣ Launched with test-app -integration‣ Results: - summary on the console output (count success/failures) - html files under target/tests-reports/html - plain text files under target/tests-reports/html - failure summary available - stdout/stderr accessible for each test case
  • Faster grails command‣ Launch command (<alt><ctrl>G) interactive
  • Faster grails command‣ Launch command (<alt><ctrl>G) interactive‣ On the console, enter commandtest-app -integration
  • Faster grails command‣ Launch command (<alt><ctrl>G) interactive‣ On the console, enter command test-app -integration‣ Hit enter to relaunch last command
  • Faster grails command‣ Launch command (<alt><ctrl>G) interactive‣ On the console, enter command test-app -integration‣ Hit enter to relaunch last command‣ After several commands, PermGenException can occur - terminate - relaunch interactive
  • Grails integration testing cons ‣ Slower to execute than unit
  • Grails integration testing cons ‣ Slower to execute than unit ‣ Test report is not integrated into eclipse
  • Grails integration testing cons ‣ Slower to execute than unit ‣ Test report is not integrated into eclipse ‣ Use only when unit test not possible
  • mockDomain: unit testing with domain class
  • mockDomain‣ It is possible to make some unit testing with domain
  • mockDomain‣ It is possible to make some unit testing with domain‣ No real database is connected, but a fake layer
  • mockDomain‣ It is possible to make some unit testing with domain‣ No real database is connected, but a fake layer‣ In each method (not setup()) mockDomain(Person) mockDomain(Person, initialBeanList)
  • mockDomain‣ It is possible to make some unit testing with domain‣ No real database is connected, but a fake layer‣ In each method (not setup()) mockDomain(Person) mockDomain(Person, initialBeanList)‣ All single domain CRUD (and more) operations possible
  • mockDomain example void testDelete(){ //buildDaltonFamily() return a list of 4 Person mockDomain(Person, buildDaltonFamily()) assert Person.count() == 4 Person p=Person.findByUsername(joe_dalton) assertNotNull p p.delete() // we should only have 3 members left assert Person.count() == 3 p=Person.findByUsername(joe_dalton) assertNull p }
  • mockDomain limits‣ No explicit database operation (hibernate criteria, HQL) are possible
  • mockDomain limits‣ No explicit database operation (hibernate criteria, HQL) are possible‣ Multiple domain class interaction are fully possible (cf. relationships)
  • mockDomain limits‣ No explicit database operation (hibernate criteria, HQL) are possible‣ Multiple domain class interaction are fully possible (cf. relationships)‣ Connection with data already entered in a database
  • Hermit domain not very useful
  • Need for relationshipsTwitter: Person ↔ Message
  • Message domaincreate-domain-class Domain
  • Message domaincreate-domain-class Domain‣ Just a text (String) and a commiter (Person)class Message { String text Person commiter static constraints = { text(size:1..140, blank:false) commiter(nullable:false) }}
  • Message + Person‣ Attach two messages to a user Person joe=Person.findByUsername(joe_dalton) new Message(text:hello, commiter:joe).save() new Message(text:world, commiter:joe).save()
  • Message + Person‣ Attach two messages to a user Person joe=Person.findByUsername(joe_dalton) new Message(text:hello, commiter:joe).save() new Message(text:world, commiter:joe).save()‣ Look for message from joe Message.findAllByCommiter(joe)
  • Message + Person‣ Attach two messages to a user Person joe=Person.findByUsername(joe_dalton) new Message(text:hello, commiter:joe).save() new Message(text:world, commiter:joe).save()‣ Look for message from joe Message.findAllByCommiter(joe)‣ Not possible to access to message directly from joe bean - one solution: explicitly declare setCommiter(Person p) in Message.groovy that would add the message to a list in joe; - problem for deletion, save inconsistency...
  • Define a one-to-many relationship
  • One-to-many relationship‣Message.groovy//Person commiterstatic belongsTo = [commiter:Person]
  • One-to-many relationship‣Message.groovy//Person commiterstatic belongsTo = [commiter:Person]‣Person.groovystatic hasMany = [messages: Message]
  • One-to-many relationship (cont’d)‣ Add a message:joe.addToMessages(new Message(text:‘hello world’)).save()
  • One-to-many relationship (cont’d)‣ Add a message: joe.addToMessages(new Message(text:‘hello world’)).save()‣ Will execute the following actions - create a message with joe as commiter - save the message - add the message to joe’s list
  • One-to-many relationship (cont’d)‣ Access to the list joe.messages
  • One-to-many relationship (cont’d)‣ Access to the list joe.messages‣ Deleting will cascade joe.delete() - all messages with joe as commiter will also be deleted
  • Testing‣ Test database consistency with two domain: integration testing // taken from MessageIntegrationTests.groovy // 4 Person are added in the setup() method public void testListMessagesUserDeletion(){ Person joe=Person.findByUsername(joe_dalton) Person averell=Person.findByUsername(averell_dalton) joe.addToMessages(new Message(text:hello world)).save() joe.addToMessages(new Message(text:im running)).save() averell.addToMessages(new Message(text:im eating)).save() assert Message.count() == 3 assert Person.count() == 4 joe.delete() assert Person.count() == 3 //having deleted joe should delete all message related to joe assert Message.count() == 1 }
  • Back to the web: 2 scaffolded controllers