Sie sind auf Seite 1von 73

UNDERSTANDING GORM

Alonso Torres @alotor


http://goo.gl/U6sK5E
Ego-slide
Alonso 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 pitfalls
The '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 pitfalls
The '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 pitfalls
The '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 pitfalls
The 'Fail whale'
class User {
String name
static hasMany = [followers:User]
}

user.followers.isEmpty()

Check Burt talk about "GORM Performance"


GORM is dark and full of
terrors
Let's take a step back...
and start at the beginning
Understanding Bootstrapping
WHAT'S GOING ON BEHIND THE SCENES?
Your first Domain class
class Book {
String name
String isbn
Author author
}
Grails Bootstrap
grails run-app

Inspect /grails-app

Classes inside /domain

Annotates them with @grails.persistence.Entity


1) Domain classes are found
DEBUG commons.DefaultGrailsApplication
Inspecting [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 Class
Prepares the relationship properties

Resolves class hierarchy

Add validation capabilities

Integrates domain classes with Spring


@grails.persistence.Entity
Includes 'id' and 'version'

Marks the classes as domain entities

You can put domain classes inside /src/main/groovy

Manualy annotate them with @Entity


@Entity
class 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 Enhancer
Instances API (save, delete...)

Classes API (findAll, where, withCriteria...)

Dynamic Finders

Validation (unique)
GORM Enhancer
Instances API (save, delete...)

Classes API (findAll, where, withCriteria...)

Dynamic Finders

Validation (unique)
Bootstraping
Distinct Grails-base vs GORM

@Entity could potentialy be used outside Grails


Problems with AST's

Grails dependencies
Understanding Spring
HOW 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
new Book()

Spring should create the object


So Grails kind of "cheats"
Spring Beans for a Domain Class
BookValidator

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 GORM
WHERE THE DARKNESS LURKS
GORM > Hibernate
GORM 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 scafolding


class 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 scafolding
class 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)
}
}
OpenSessionInViewInterceptor
Creates 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
Session
Entry point to the Hibernate Framework

In-Memory cache

No thread safe and normaly thread-bound


GORM Entities
Entities 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 Flush
Session checks "DIRTY OBJECTS"

When flushed the changes are persisted to database


After flush, are my objects in the DB?
DEPENDS
Transaction
Database managed

Provider specific

Accessed through JDBC Driver


Peter Ledbrok - http://spring.io/blog/2010/06/23/gorm-gotchas-part-1/
Hibernate Session
FLUSHING
vs
COMMIT
Database Transaction
Automatic session flushing
Query executed

A controller completes

Before transaction commit


Some pitfalls
The '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 pitfalls
The '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?
withTransaction
Book.withTransaction {
// Transactional stuff
}
Transactional services
@Transactional
class BookService {
def doTransactionalStuff(){
...
}
}
Be careful!
Don't instanciate the services
new TransactionalService().doTransactionalStuff()

Don't use closures


def transactionalMethod = { ... }

And...
DON'T THROW CHECKED EXCEPTIONS
Some pitfalls
The '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 pitfalls
The '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 @Transactional
Spring @Transactional creates a PROXY

Grails new @Transactional is an AST


Understanding Parallel GORM
BRACE YOURSELVES
Session is Thread-Local
Some pitfalls
The '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 session
Recovers a list of orders
def orders = Orders.findTodayOrders()

Each item is bound to the request session

When we spawn the concurrent threads the objects don't


know where is their session
Some pitfalls
The '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 thumb
Session = Thread

If entity has recovered by another thread

Put it in the current thread session


Grails 2.3 Async GORM
The 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 GORM
Problem: 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 session
person.firstName = "Bart"
Closing thoughts
GORM is a very cool abstraction

You have to be aware of some of how things work

With great power, comes great responsibility


THANKS

@alotor
http://goo.gl/U6sK5E
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

Das könnte Ihnen auch gefallen