grails transactions

36
Grails Services, Transactions & Async calls

Upload: husain-dalal

Post on 13-Aug-2015

124 views

Category:

Software


4 download

TRANSCRIPT

Page 1: Grails   transactions

Grails Services, Transactions & Async calls

Page 2: Grails   transactions

Service

Page 3: Grails   transactions

ServiceBusiness logic goes in services.

Don’t use controllers to do any heavy lifting. They are designed only for controlling application flow and doing data marshaling. All your data access and business logic should happen in transactional services.

Also avoid much logic in Domain classes. Validation logic there can be limited to basic constraints and basic postUpdate and postCreate methods.

Page 4: Grails   transactions

Create Servicegrails> create-service com.netflix.Contract

| Created file grails-app/services/com/netflix/ContractService.groovy

| Created file test/unit/com/netflix/ContractServiceTests.groovy

package com.netflix

class ContractService {

def serviceMethod() {

}

}

Page 5: Grails   transactions

Session vs TransactionUser Session: Corresponds to time within which the user is logged in to the system.

Hibernate Session: Associated with JDBC Connection.

Hibernate Transaction: ACID compliant boundary for unit of work.

Page 6: Grails   transactions

Scope> singleton (default), prototype, request, session

class SomeUsefulService {

// this is a request scoped service

static scope = 'request'

}

Page 7: Grails   transactions

Service Injectionclass ContractController {

ContractService contractService

...

}

No need for AutoWire annotation.

Page 8: Grails   transactions

Command [email protected] LoginCommand { def loginService

String username String password

static constraints = { username validator: { val, obj -> obj.loginService.canLogin(obj.username, obj.password) } }}

Page 9: Grails   transactions

Transaction

Page 10: Grails   transactions

@Transactional annotationService methods are Transactional by default. However best practice is to add @Transactional annotation to each service

Transaction annotation is similar to Spring’s @Transactional annotation

The @Transactional annotation on the class ensures that all public methods in a service are transactional.

Annotating a service method (and not at class) with @Transactional disables the default Grails transactional behavior for that method. so if you use any annotations you must annotate all methods that require transactions.

Page 11: Grails   transactions

Exampleimport grails.transaction.Transactional

@Transactionalclass BookService { @Transactional(readOnly = true) def listBooks() { Book.list() }

def updateBook() { // … }}

Page 12: Grails   transactions

No Transaction & ReadOnly@NonTransactional

class EmailService {

//OR static transactional = false

….

}

@Transactional(readOnly = true)

class ReportService {

….

}

Page 13: Grails   transactions

withTransactionpackage com.netflix

class ContractService {

// turn off automatic transaction management

static transactional = false

void someServiceMethod() {

Contract.withTransaction {TransactionStatus tx ->

// do some work with the database….

// if the transaction needs to be rolled back, call setRollbackOnly()

tx.setRollbackOnly()

}

}

}

Page 14: Grails   transactions

Savepointdef save() {

Album.withTransaction { status ->

def album = Album.get(params.id)

album.title = "Changed Title"

album.save(flush:true)

def savepoint = status.createSavepoint()

...

// something goes wrong

if(hasSomethingGoneWrong()) {

status.rollbackToSavepoint(savepoint)

// do something else

...

} } }

Page 15: Grails   transactions

Transaction Propagation :(// Doesn’t work well without additional configuration@Transactionalvoid someMethod(...) { // do some work ... storeAuditData(...)}

@Transactional(propagation=Propagation.REQUIRES_NEW)void storeAuditData(...) { //}http://techbus.safaribooksonline.com/book/programming/9781449324513/4dot-spring/_transactional_services_html

Page 16: Grails   transactions

Error Handling

Page 17: Grails   transactions

Exceptions and Validationsclass AuthorService { void updateAge(id, int age) { def author = Author.get(id) author.age = age if (author.isTooOld()) { throw new AuthorException("too old", author) } if (!author.validate()) { throw new ValidationException("Author is not valid", author.errors) } }}

Page 18: Grails   transactions

Exception on Savedef p = Person.get(1)try { p.save(failOnError: true)}catch (ValidationException e) { // deal with exception

p.errors.allErrors.each { println it }}

Page 19: Grails   transactions

LazyInitializationExceptionWhen a transaction is rolled back the Hibernate session used by GORM is cleared. This means any objects within the session become detached and accessing uninitialized lazy-loaded collections will lead to LazyInitializationException

class AuthorController { def authorService def updateAge() { try { authorService.updateAge(params.id, params.int("age")) } catch(e) { render "Author books ${e.author.books}" } } }

Page 20: Grails   transactions

Solution...class AuthorService { void updateAge(id, int age) { def author = Author.findById(id, [fetch:[books:"eager"]]) author.age = age if (author.isTooOld()) { throw new AuthorException("too old", author) } }}

Page 21: Grails   transactions

Sample

Page 22: Grails   transactions
Page 23: Grails   transactions
Page 24: Grails   transactions

Asynchronous

Page 25: Grails   transactions

Various ways...

● Event mechanism using Platform Core plugin

● JMS Messaging Queues Plugin● Quartz scheduling Plugin● Asynchronous Programming

Page 26: Grails   transactions

Asynchronous - Promiseimport static java.util.concurrent.TimeUnit.*import static grails.async.Promises.*Promise p = Promises.task {

// Long running task}p.onError { Throwable err ->

println "An error occured ${err.message}"}p.onComplete { result -> println "Promise returned $result"}

Page 27: Grails   transactions

Synchronous - Promiseimport static java.util.concurrent.TimeUnit.*import static grails.async.Promises.*Promise p = task {

// Long running task}…. other tasks// block until result is calleddef result = p.get()// block for the specified timedef result = p.get(1,MINUTES)

Page 28: Grails   transactions

PromiseListimport static grails.async.Promises.*import grails.async.PromiseListPromiseList promiseList = tasks([{ 2 * 2 }, { 4 * 4}, { 8 * 8 }])//... some other processesassert [4,16,64] == promiseList.get()

Page 29: Grails   transactions

PromiseMapimport grails.async.*

PromiseMap map = new PromiseMap()map['one'] = { 2 * 2 }map['two'] = { 4 * 4 }map['three'] = { 8 * 8 }//Async callmap.onComplete { Map results -> assert [one:4,two:16,three:64] == results}

Page 30: Grails   transactions

DelegateAsync Transformation//Sync serviceclass BookService { List<Book> findBooks(String title) { // implementation }}

//Async Serviceimport grails.async.*class AsyncBookService { @DelegateAsync BookService bookService}

Page 31: Grails   transactions

DelegateAsync call//Async service call AsyncBookService asyncBookServicedef findBooks(String title) { asyncBookService.findBooks(title) .onComplete { List results -> println "Books = ${results}" }}

Page 32: Grails   transactions

Asynchronous GORMimport static grails.async.Promises.*Person.async.list().onComplete { List results -> println "Got people = ${results}"}

PromiseList p = Person.async.getAll(1L, 2L, 3L)List results = p.get()

Promise p1 = Person.async.findByFirstName("Homer")Promise p2 = Person.async.findByFirstName("Bart")Promise p3 = Person.async.findByFirstName("Barney")results = waitAll(p1, p2, p3)

Page 33: Grails   transactions

Async and the SessionWhen using GORM async each promise is executed in a different thread. Since the Hibernate session is not concurrency safe, a new session is bound per thread.This means you cannot save objects returned from asynchronous queries without first merging them back into session.

def promise = Person.async.findByFirstName("Homer")def person = promise.get()person.merge()person.firstName = "Bart"

In general it is not recommended to read and write objects in different threads and you should avoid this technique unless absolutely necessary. Also fetch the dependent objects eagerly to avoid LazyInitializationException

Page 34: Grails   transactions

Quartzplugins { ... compile ":quartz:1.0.1" ...}

//New commands

grails create-jobgrails install-quartz-config

grails create-job com.netflix.SupplierImport

Page 35: Grails   transactions
Page 36: Grails   transactions

spock

Testing Services