Date post: | 27-Aug-2014 |
Category: |
Software |
Upload: | alonso-torres |
View: | 1,129 times |
Download: | 5 times |
Ego-slideAlonso Torres
alotor @alotor
Engineer at Kaleidos
GORM? Really?Is so easy, the easiest part of Grails!
Only a few POGO's to access the database
Peachy!
Some pitfallsThe 'When did I modified that object?'
def renderBook(String isbn) { def book = Book.findByIsbn(isbn)
// Render as uppercase book.title = book.title.toUpperCase()
[book: book]}
Some pitfallsThe 'Rollback where are you?'
def buyBook(String user, String bookTitle, Long qty) { def found = Book.findByTitle(bookTitle)
// Save a new order for the user def order = new BookOrder(user:user, book:found, quantity: qty) order.save()
found.stock = found.stock - 1
// When not found throw exception to rollback if (found.stock < 0) { throw new Exception("This should rollback!") } return found}
Some pitfallsThe 'Parallel processing, It's easy!!'
def processOrders() { def orders = BookOrder.list()
// Parallel update orders GParsPool.withPool(10) { orders.eachParallel { order -> dispatchingService.dispatch(order) order.sent = true order.save() } }}
Some pitfallsThe 'Fail whale'
class User { String name static hasMany = [followers:User]}
user.followers.isEmpty()
Check Burt talk about "GORM Performance"
GORM is dark and full ofterrors
Let's take a step back...and start at the beginning
Understanding BootstrappingWHAT'S GOING ON BEHIND THE SCENES?
Your first Domain class
class Book { String name String isbn Author author}
Grails Bootstrap
Inspect /grails-app
Classes inside /domain
Annotates them with @grails.persistence.Entity
grails run-app
1) Domain classes are found
DEBUG commons.DefaultGrailsApplicationInspecting [es.greach.gorm.Book][es.greach.gorm.Book] is not a Filters class.[es.greach.gorm.Book] is not a Codec class.[es.greach.gorm.Book] is not a TagLib class.[es.greach.gorm.Book] is not a Service class.[es.greach.gorm.Book] is not a Controller class.[es.greach.gorm.Book] is not a Bootstrap class.[es.greach.gorm.Book] is a Domain class.Adding artefact class es.greach.gorm.Book of kind Domain
Grails initializes the Domain ClassPrepares the relationship properties
Resolves class hierarchy
Add validation capabilities
Integrates domain classes with Spring
@grails.persistence.EntityIncludes 'id' and 'version'
Marks the classes as domain entities
You can put domain classes inside /src/main/groovy
Manualy annotate them with @Entity
@Entityclass Book { Long id Long version
String name String isbn Author author}
2) GORM enhances classes
class Book { static mapWith = "mongo"
String name String isbn}
DEBUG cfg.HibernateUtils - Enhancing GORM entity Book
GORM EnhancerInstances API (save, delete...)
Classes API (findAll, where, withCriteria...)
Dynamic Finders
Validation (unique)
GORM EnhancerInstances API (save, delete...)
Classes API (findAll, where, withCriteria...)
Dynamic Finders
Validation (unique)
BootstrapingDistinct Grails-base vs GORM
@Entity could potentialy be used outside GrailsProblems with AST's
Grails dependencies
Understanding SpringHOW INTERACT DOMAIN CLASSES AND SPRING
class Book { def bookService
String name String isbn Author author
String toString() { return bookService.parseBook(this) }}
def myBook = new Book()println myBook.toString()
[DEBUG] BookService::parseBook
DI needs control on object instantiation
Spring should create the objectnew Book()
So Grails kind of "cheats"
Spring Beans for a Domain ClassBookValidator
BookPersistentClass
BookDomainClass
Book
new Book()
Book.constructor = {-> def ctx = .... // Spring context context.getBean("Book")}
What does that means?Spring creates your objects
Be careful when using some capabilities
Example: Spring AOP
Understanding Hibernate GORMWHERE THE DARKNESS LURKS
GORM > HibernateGORM provides a beautiful abstraction over hibernate
Convention over configuration
No more complicated XML or annotations
Better collections
But, Hibernate it's still there
class Book { String name String isbn Author author}
grails generate-controller Book
// Grails 2.2 scafoldingclass BookController { def save() { def instance = new Book(params) if (!instance.save(flush: true)) { render(view: "create", model: [bookInstance: instance]) return } redirect(action: "show", id: instance.id) }}
// Grails 2.2 scafoldingclass BookController { def save() { def bookInstance = new Book(params) if (!bookInstance.save(flush: true)) { render(view: "create", model: [bookInstance: bookInstance]) return } redirect(action: "show", id: bookInstance.id) }}
OpenSessionInViewInterceptorCreates a new Session within a Controller scope
Doesn't create a Transaction
So... there is NO transaction at the controller
After render the session is flushed
Session? Transaction? I'm confused
SessionEntry point to the Hibernate Framework
In-Memory cache
No thread safe and normaly thread-bound
GORM EntitiesEntities have a relationship with the session
This defines a "life-cycle"
Transient - not yet persisted
Persistent - has a session
Detached - persisted but without session
Session FlushSession checks "DIRTY OBJECTS"
When flushed the changes are persisted to database
After flush, are my objects in the DB?
DEPENDS
TransactionDatabase managed
Provider specific
Accessed through JDBC Driver
Peter Ledbrok - http://spring.io/blog/2010/06/23/gorm-gotchas-part-1/
Hibernate SessionFLUSHING
vsCOMMIT
Database Transaction
Automatic session flushingQuery executed
A controller completes
Before transaction commit
Some pitfallsThe 'When did I modified that object?'
def renderBook(String isbn) { def book = Book.findByIsbn(isbn)
// Render as uppercase book.title = book.title.toUpperCase()
[book: book]}
Some pitfallsThe 'When did I modified that object?'
def renderBook(String isbn) { def book = Book.findByIsbn(isbn)
// Deattach the object from the session book.discard()
// Render as uppercase book.title = book.title.toUpperCase()
[book: book]}
Where do I put my transactional logic?
withTransactionBook.withTransaction { // Transactional stuff}
Transactional services@Transactionalclass BookService { def doTransactionalStuff(){ ... }}
Be careful!Don't instanciate the services
Don't use closures
And...
new TransactionalService().doTransactionalStuff()
def transactionalMethod = { ... }
DON'T THROW CHECKED EXCEPTIONS
Some pitfallsThe 'Rollback where are you?'
def buyBook(String user, String bookTitle, Long qty) { def found = Book.findByTitle(bookTitle)
// Save a new order for the user def order = new BookOrder(user:user, book:found, quantity: qty) order.save()
found.stock = found.stock - 1
// When not found throw exception to rollback if (found.stock < 0) { throw new Exception("This should rollback!") } return found}
Some pitfallsThe 'Rollback where are you?'
def buyBook(String user, String bookTitle, Long qty) { def found = Book.findByTitle(bookTitle)
// Save a new order for the user def order = new BookOrder(user:user, book:found, quantity: qty) order.save()
found.stock = found.stock - 1
// When not found throw exception to rollback if (found.stock < 0) { throw new RuntimeException("This should rollback!") } return found}
@Transactional vs @TransactionalSpring @Transactional creates a PROXY
Grails new @Transactional is an AST
Understanding Parallel GORMBRACE YOURSELVES
Session is Thread-Local
Some pitfallsThe 'Concurrency, It's easy!!'
def processOrders() { def orders = BookOrder.list()
// Parallel update orders GParsPool.withPool(10) { orders.eachParallel { order -> dispatchingService.dispatch(order) order.sent = true order.save() } }}
Thread-local sessionRecovers a list of orders
Each item is bound to the request session
When we spawn the concurrent threads the objects don'tknow where is their session
def orders = Orders.findTodayOrders()
Some pitfallsThe 'Concurrency, It's easy!!'
def processOrders() { def orders = BookOrder.list()
// Parallel update orders GParsPool.withPool(10) { orders.eachParallel { order -> BookOrder.withNewSession { order.merge()
dispatchingService.dispatch(order) order.sent = true order.save() } } }}
Rule of thumbSession = Thread
If entity has recovered by another thread
Put it in the current thread session
Grails 2.3 Async GORMThe new async GORM solves some problems
Manages the session for the promise
def promise = Person.async.findByFirstName("Homer")def person = promise.get()
Grails 2.3 Async GORMProblem: Object has been retrieved by another thread
After the promise is resolved we have to attach the object
def promise = Person.async.findByFirstName("Homer")def person = promise.get()person.merge() // Rebound the object to the sessionperson.firstName = "Bart"
Closing thoughtsGORM is a very cool abstraction
You have to be aware of some of how things work
With great power, comes great responsibility
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
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