Understanding GORM (Greach 2014)

2,946 views

Published on

GORM is one of the keys for the success of Grails, but for a Grails beginner some concepts may be a bit confusing. Even for a long time developer there can be some missconceptions due to the abstractions layers of the framework.

In this talk I’ll try to cover some of the basics of GORM, Hibernate and how to interact with transactions and sessions. I’ll show some of the problems that I had starting with the Grails framework and how I think they are best solved.

Some other topics that I’ll go over are the interaction with GPars, and the differences between “session” and “transaction”.

Published in: Software, Technology, Business

Understanding GORM (Greach 2014)

  1. 1. UNDERSTANDING GORM Alonso Torres @alotor http://goo.gl/U6sK5E
  2. 2. Ego-slide Alonso Torres alotor @alotor Engineer atKaleidos
  3. 3. GORM? Really? Is so easy, the easiestpartof Grails! Onlyafew POGO's to access the database Peachy!
  4. 4. Some pitfalls The 'When did I modified thatobject?' defrenderBook(Stringisbn){ defbook=Book.findByIsbn(isbn) //Renderasuppercase book.title=book.title.toUpperCase() [book:book] }
  5. 5. Some pitfalls The 'Rollback where are you?' defbuyBook(Stringuser,StringbookTitle,Longqty){ deffound=Book.findByTitle(bookTitle) //Saveaneworderfortheuser deforder=newBookOrder(user:user,book:found,quantity:qty) order.save() found.stock=found.stock-1 //Whennotfoundthrowexceptiontorollback if(found.stock<0){ thrownewException("Thisshouldrollback!") } returnfound }
  6. 6. Some pitfalls The 'Parallelprocessing, It's easy!!' defprocessOrders(){ deforders=BookOrder.list() //Parallelupdateorders GParsPool.withPool(10){ orders.eachParallel{order-> dispatchingService.dispatch(order) order.sent=true order.save() } } }
  7. 7. Some pitfalls The 'Failwhale' classUser{ Stringname statichasMany=[followers:User] } user.followers.isEmpty() CheckBurttalkabout"GORMPerformance"
  8. 8. GORM is dark and full of terrors
  9. 9. Let's take a step back... and start at the beginning
  10. 10. Understanding Bootstrapping WHAT'S GOING ON BEHIND THE SCENES?
  11. 11. Your first Domain class
  12. 12. classBook{ Stringname Stringisbn Authorauthor }
  13. 13. Grails Bootstrap Inspect/grails-app Classes inside /domain Annotates them with @grails.persistence.Entity grailsrun-app
  14. 14. 1) Domain classes are found
  15. 15. DEBUGcommons.DefaultGrailsApplication Inspecting[es.greach.gorm.Book] [es.greach.gorm.Book]isnotaFiltersclass. [es.greach.gorm.Book]isnotaCodecclass. [es.greach.gorm.Book]isnotaTagLibclass. [es.greach.gorm.Book]isnotaServiceclass. [es.greach.gorm.Book]isnotaControllerclass. [es.greach.gorm.Book]isnotaBootstrapclass. [es.greach.gorm.Book]isaDomainclass. Addingartefactclasses.greach.gorm.BookofkindDomain
  16. 16. Grails initializes the Domain Class Prepares the relationship properties Resolves class hierarchy Add validation capabilities Integrates domain classes with Spring
  17. 17. @grails.persistence.Entity Includes 'id'and 'version' Marks the classes as domain entities You can putdomain classes inside /src/main/groovy Manualyannotate them with @Entity
  18. 18. @Entity classBook{ Longid Longversion Stringname Stringisbn Authorauthor }
  19. 19. 2) GORM enhances classes
  20. 20. classBook{ staticmapWith="mongo" Stringname Stringisbn }
  21. 21. DEBUGcfg.HibernateUtils -EnhancingGORMentityBook
  22. 22. GORM Enhancer Instances API (save, delete...) Classes API (findAll, where, withCriteria...) Dynamic Finders Validation (unique)
  23. 23. GORM Enhancer Instances API (save, delete...) Classes API (findAll, where, withCriteria...) Dynamic Finders Validation (unique)
  24. 24. Bootstraping DistinctGrails-base vs GORM @Entitycould potentialybe used outside Grails Problems with AST's Grails dependencies
  25. 25. Understanding Spring HOW INTERACT DOMAIN CLASSES AND SPRING
  26. 26. classBook{ defbookService Stringname Stringisbn Authorauthor StringtoString(){ returnbookService.parseBook(this) } } defmyBook=newBook() printlnmyBook.toString() [DEBUG]BookService::parseBook
  27. 27. DI needs control on object instantiation Spring should create the object newBook()
  28. 28. So Grails kind of "cheats"
  29. 29. Spring Beans for a Domain Class BookValidator BookPersistentClass BookDomainClass Book
  30. 30. newBook() Book.constructor={-> defctx=....//Springcontext context.getBean("Book") }
  31. 31. What does that means? Springcreates your objects Be carefulwhen usingsome capabilities Example: SpringAOP
  32. 32. Understanding Hibernate GORM WHERE THE DARKNESS LURKS
  33. 33. GORM > Hibernate GORM provides abeautifulabstraction over hibernate Convention over configuration No more complicated XML or annotations Better collections
  34. 34. But, Hibernate it's still there
  35. 35. classBook{ Stringname Stringisbn Authorauthor } grailsgenerate-controllerBook //Grails2.2scafolding classBookController{ defsave(){ definstance=newBook(params) if(!instance.save(flush:true)){ render(view:"create",model:[bookInstance:instance]) return } redirect(action:"show",id:instance.id) } }
  36. 36. //Grails2.2scafolding classBookController{ defsave(){ defbookInstance=newBook(params) if(!bookInstance.save(flush:true)){ render(view:"create",model:[bookInstance:bookInstance]) return } redirect(action:"show",id:bookInstance.id) } }
  37. 37. OpenSessionInViewInterceptor Creates anew Sessionwithin aController scope Doesn'tcreate aTransaction So... there is NO transaction atthe controller After render the session is flushed
  38. 38. Session? Transaction? I'm confused
  39. 39. Session Entrypointto the Hibernate Framework In-Memorycache No thread safe and normalythread-bound
  40. 40. GORM Entities Entities have arelationship with the session This defines a"life-cycle" Transient -notyetpersisted Persistent -has asession Detached -persisted butwithoutsession
  41. 41. Session Flush Session checks "DIRTY OBJECTS" When flushed the changes are persisted to database
  42. 42. After flush, are my objects in the DB? DEPENDS
  43. 43. Transaction Database managed Provider specific Accessed through JDBC Driver
  44. 44. PeterLedbrok- http://spring.io/blog/2010/06/23/gorm-gotchas-part-1/
  45. 45. Hibernate Session FLUSHING vs COMMIT Database Transaction
  46. 46. Automatic session flushing Queryexecuted Acontroller completes Before transaction commit
  47. 47. Some pitfalls The 'When did I modified thatobject?' defrenderBook(Stringisbn){ defbook=Book.findByIsbn(isbn) //Renderasuppercase book.title=book.title.toUpperCase() [book:book] }
  48. 48. Some pitfalls The 'When did I modified thatobject?' defrenderBook(Stringisbn){ defbook=Book.findByIsbn(isbn) //Deattachtheobjectfromthesession book.discard() //Renderasuppercase book.title=book.title.toUpperCase() [book:book] }
  49. 49. Where do I put my transactional logic?
  50. 50. withTransaction Book.withTransaction{ //Transactionalstuff }
  51. 51. Transactional services @Transactional classBookService{ defdoTransactionalStuff(){ ... } }
  52. 52. Be careful! Don'tinstanciate the services Don'tuse closures And... newTransactionalService().doTransactionalStuff() deftransactionalMethod={...}
  53. 53. DON'T THROW CHECKED EXCEPTIONS
  54. 54. Some pitfalls The 'Rollback where are you?' defbuyBook(Stringuser,StringbookTitle,Longqty){ deffound=Book.findByTitle(bookTitle) //Saveaneworderfortheuser deforder=newBookOrder(user:user,book:found,quantity:qty) order.save() found.stock=found.stock-1 //Whennotfoundthrowexceptiontorollback if(found.stock<0){ thrownewException("Thisshouldrollback!") } returnfound }
  55. 55. Some pitfalls The 'Rollback where are you?' defbuyBook(Stringuser,StringbookTitle,Longqty){ deffound=Book.findByTitle(bookTitle) //Saveaneworderfortheuser deforder=newBookOrder(user:user,book:found,quantity:qty) order.save() found.stock=found.stock-1 //Whennotfoundthrowexceptiontorollback if(found.stock<0){ thrownewRuntimeException("Thisshouldrollback!") } returnfound }
  56. 56. @Transactional vs @Transactional Spring@Transactionalcreates aPROXY Grails new @Transactionalis an AST
  57. 57. Understanding Parallel GORM BRACE YOURSELVES
  58. 58. Session is Thread-Local
  59. 59. Some pitfalls The 'Concurrency, It's easy!!' defprocessOrders(){ deforders=BookOrder.list() //Parallelupdateorders GParsPool.withPool(10){ orders.eachParallel{order-> dispatchingService.dispatch(order) order.sent=true order.save() } } }
  60. 60. Thread-local session Recovers alistof orders Each item is bound to the requestsession When we spawn the concurrentthreads the objects don't know where is their session deforders=Orders.findTodayOrders()
  61. 61. Some pitfalls The 'Concurrency, It's easy!!' defprocessOrders(){ deforders=BookOrder.list() //Parallelupdateorders GParsPool.withPool(10){ orders.eachParallel{order-> BookOrder.withNewSession{ order.merge() dispatchingService.dispatch(order) order.sent=true order.save() } } } }
  62. 62. Rule of thumb Session = Thread If entityhas recovered byanother thread Putitin the currentthread session
  63. 63. Grails 2.3 Async GORM The new async GORM solves some problems Manages the session for the promise defpromise=Person.async.findByFirstName("Homer") defperson=promise.get()
  64. 64. Grails 2.3 Async GORM Problem: Objecthas been retrieved byanother thread After the promise is resolved we have to attach the object defpromise=Person.async.findByFirstName("Homer") defperson=promise.get() person.merge()//Reboundtheobjecttothesession person.firstName="Bart"
  65. 65. Closing thoughts GORM is averycoolabstraction You have to be aware of some of how things work With greatpower, comes greatresponsibility
  66. 66. THANKS @alotor http://goo.gl/U6sK5E
  67. 67. Bonus track (references) http://spring.io/blog/2010/06/23/gorm-gotchas-part-1/ http://sacharya.com/tag/gorm-transaction/ http://www.anyware.co.uk/2005/2012/11/12/the-false- optimism-of-gorm-and-hibernate/ http://docs.jboss.org/hibernate/orm/3.6/reference/en- US/html_single/#transactions-basics
  68. 68. Bonus track (references) http://www.infoq.com/presentations/GORM-Performance http://www.infoq.com/presentations/grails-transaction http://blog.octo.com/en/transactions-in-grails/ https://community.jboss.org/wiki/OpenSessioninView

×