understanding gorm (greach 2014)

Post on 27-Aug-2014

1.130 Views

Category:

Software

5 Downloads

Preview:

Click to see full reader

DESCRIPTION

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”.

TRANSCRIPT

UNDERSTANDING GORM

Alonso Torres @alotorhttp://goo.gl/U6sK5E

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

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

top related