73
U NDERSTANDING G ORM Alonso Torres @alotor http://goo.gl/U6sK5E

Understanding GORM (Greach 2014)

Embed Size (px)

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

Citation preview

Page 1: Understanding GORM (Greach 2014)

UNDERSTANDING GORM

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

Page 2: Understanding GORM (Greach 2014)

Ego-slideAlonso Torres

alotor @alotor

Engineer at Kaleidos

Page 3: Understanding GORM (Greach 2014)

GORM? Really?Is so easy, the easiest part of Grails!

Only a few POGO's to access the database

Peachy!

Page 4: Understanding GORM (Greach 2014)
Page 5: Understanding GORM (Greach 2014)

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]}

Page 6: Understanding GORM (Greach 2014)

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}

Page 7: Understanding GORM (Greach 2014)

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() } }}

Page 8: Understanding GORM (Greach 2014)

Some pitfallsThe 'Fail whale'

class User { String name static hasMany = [followers:User]}

user.followers.isEmpty()

Check Burt talk about "GORM Performance"

Page 9: Understanding GORM (Greach 2014)

GORM is dark and full ofterrors

Page 10: Understanding GORM (Greach 2014)

Let's take a step back...and start at the beginning

Page 11: Understanding GORM (Greach 2014)

Understanding BootstrappingWHAT'S GOING ON BEHIND THE SCENES?

Page 12: Understanding GORM (Greach 2014)

Your first Domain class

Page 13: Understanding GORM (Greach 2014)

class Book { String name String isbn Author author}

Page 14: Understanding GORM (Greach 2014)

Grails Bootstrap

Inspect /grails-app

Classes inside /domain

Annotates them with @grails.persistence.Entity

grails run-app

Page 15: Understanding GORM (Greach 2014)

1) Domain classes are found

Page 16: Understanding GORM (Greach 2014)

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

Page 17: Understanding GORM (Greach 2014)

Grails initializes the Domain ClassPrepares the relationship properties

Resolves class hierarchy

Add validation capabilities

Integrates domain classes with Spring

Page 18: Understanding GORM (Greach 2014)

@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

Page 19: Understanding GORM (Greach 2014)

@Entityclass Book { Long id Long version

String name String isbn Author author}

Page 20: Understanding GORM (Greach 2014)

2) GORM enhances classes

Page 21: Understanding GORM (Greach 2014)

class Book { static mapWith = "mongo"

String name String isbn}

Page 22: Understanding GORM (Greach 2014)

DEBUG cfg.HibernateUtils - Enhancing GORM entity Book

Page 23: Understanding GORM (Greach 2014)

GORM EnhancerInstances API (save, delete...)

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

Dynamic Finders

Validation (unique)

Page 24: Understanding GORM (Greach 2014)

GORM EnhancerInstances API (save, delete...)

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

Dynamic Finders

Validation (unique)

Page 25: Understanding GORM (Greach 2014)

BootstrapingDistinct Grails-base vs GORM

@Entity could potentialy be used outside GrailsProblems with AST's

Grails dependencies

Page 26: Understanding GORM (Greach 2014)

Understanding SpringHOW INTERACT DOMAIN CLASSES AND SPRING

Page 27: Understanding GORM (Greach 2014)

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

Page 28: Understanding GORM (Greach 2014)

DI needs control on object instantiation

Spring should create the objectnew Book()

Page 29: Understanding GORM (Greach 2014)

So Grails kind of "cheats"

Page 30: Understanding GORM (Greach 2014)

Spring Beans for a Domain ClassBookValidator

BookPersistentClass

BookDomainClass

Book

Page 31: Understanding GORM (Greach 2014)

new Book()

Book.constructor = {-> def ctx = .... // Spring context context.getBean("Book")}

Page 32: Understanding GORM (Greach 2014)

What does that means?Spring creates your objects

Be careful when using some capabilities

Example: Spring AOP

Page 33: Understanding GORM (Greach 2014)

Understanding Hibernate GORMWHERE THE DARKNESS LURKS

Page 34: Understanding GORM (Greach 2014)

GORM > HibernateGORM provides a beautiful abstraction over hibernate

Convention over configuration

No more complicated XML or annotations

Better collections

Page 35: Understanding GORM (Greach 2014)

But, Hibernate it's still there

Page 36: Understanding GORM (Greach 2014)

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) }}

Page 37: Understanding GORM (Greach 2014)
Page 38: Understanding GORM (Greach 2014)

// 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) }}

Page 39: Understanding GORM (Greach 2014)

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

Page 40: Understanding GORM (Greach 2014)

Session? Transaction? I'm confused

Page 41: Understanding GORM (Greach 2014)

SessionEntry point to the Hibernate Framework

In-Memory cache

No thread safe and normaly thread-bound

Page 42: Understanding GORM (Greach 2014)

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

Page 43: Understanding GORM (Greach 2014)
Page 44: Understanding GORM (Greach 2014)

Session FlushSession checks "DIRTY OBJECTS"

When flushed the changes are persisted to database

Page 45: Understanding GORM (Greach 2014)

After flush, are my objects in the DB?

DEPENDS

Page 46: Understanding GORM (Greach 2014)

TransactionDatabase managed

Provider specific

Accessed through JDBC Driver

Page 47: Understanding GORM (Greach 2014)

Peter Ledbrok - http://spring.io/blog/2010/06/23/gorm-gotchas-part-1/

Page 48: Understanding GORM (Greach 2014)

Hibernate SessionFLUSHING

vsCOMMIT

Database Transaction

Page 49: Understanding GORM (Greach 2014)

Automatic session flushingQuery executed

A controller completes

Before transaction commit

Page 50: Understanding GORM (Greach 2014)

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]}

Page 51: Understanding GORM (Greach 2014)

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]}

Page 52: Understanding GORM (Greach 2014)

Where do I put my transactional logic?

Page 53: Understanding GORM (Greach 2014)

withTransactionBook.withTransaction { // Transactional stuff}

Page 54: Understanding GORM (Greach 2014)

Transactional services@Transactionalclass BookService { def doTransactionalStuff(){ ... }}

Page 55: Understanding GORM (Greach 2014)
Page 56: Understanding GORM (Greach 2014)

Be careful!Don't instanciate the services

Don't use closures

And...

new TransactionalService().doTransactionalStuff()

def transactionalMethod = { ... }

Page 57: Understanding GORM (Greach 2014)

DON'T THROW CHECKED EXCEPTIONS

Page 58: Understanding GORM (Greach 2014)

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}

Page 59: Understanding GORM (Greach 2014)

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}

Page 60: Understanding GORM (Greach 2014)

@Transactional vs @TransactionalSpring @Transactional creates a PROXY

Grails new @Transactional is an AST

Page 61: Understanding GORM (Greach 2014)

Understanding Parallel GORMBRACE YOURSELVES

Page 62: Understanding GORM (Greach 2014)

Session is Thread-Local

Page 63: Understanding GORM (Greach 2014)

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() } }}

Page 64: Understanding GORM (Greach 2014)

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()

Page 65: Understanding GORM (Greach 2014)

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() } } }}

Page 66: Understanding GORM (Greach 2014)

Rule of thumbSession = Thread

If entity has recovered by another thread

Put it in the current thread session

Page 67: Understanding GORM (Greach 2014)

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()

Page 68: Understanding GORM (Greach 2014)

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"

Page 69: Understanding GORM (Greach 2014)
Page 70: Understanding GORM (Greach 2014)

Closing thoughtsGORM is a very cool abstraction

You have to be aware of some of how things work

With great power, comes great responsibility

Page 71: Understanding GORM (Greach 2014)

THANKS

@alotor

http://goo.gl/U6sK5E

Page 72: Understanding GORM (Greach 2014)

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

Page 73: Understanding GORM (Greach 2014)

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