Grails Transactions

36
1 1 Grails Plugin Best Practices Burt Beckwith SpringSource @burtbeckwith https://burtbeckwith.com/blog/ Grails Transactions Burt Beckwith SpringSource @burtbeckwith https://burtbeckwith.com/blog/

description

Speaker: Burt Beckwith Properly performing multiple data updates requires a transaction, but how do we do this in Grails? Services are the best option, but there are different approaches that can be used. We'll look at how to effectively use transactions and how to customize transaction attributes such as isolation and propagation levels. We'll also look at using two-phase commit (2PC) when using multiple datasources, or when combining database updates with JMS messaging. And we'll also look at testing to ensure your code is properly transactional.

Transcript of Grails Transactions

Page 1: Grails Transactions

11

Grails Plugin Best Practices

Burt Beckwith

SpringSource

@burtbeckwith

https://burtbeckwith.com/blog/

Grails Transactions

Burt Beckwith

SpringSource

@burtbeckwith

https://burtbeckwith.com/blog/

Page 2: Grails Transactions

22

What is a Transaction?

Page 3: Grails Transactions

33

What is a Transaction?

getConnection()

setAutoCommit(false)

do work

COMMIT

Plus error handling, rollback if necessary

Page 4: Grails Transactions
Page 5: Grails Transactions

55

ACID

Atomicity

Consistency

Isolation

Durability

Page 6: Grails Transactions

66

How To Use Transactions in Grails?

Page 7: Grails Transactions

77

How To Use Transactions in Grails?

Transactional services

Spring's @Transactional annotation

And new in Grails 2.3, the grails.transaction.Transactional

annotation

Page 8: Grails Transactions

88

Grails Transactional services

$ grails create-service the

package tx

class TheService {

}

Page 9: Grails Transactions

99

@Transactional

Page 10: Grails Transactions

1010

@Transactional

String value() default "";

Propagation propagation() default Propagation.REQUIRED;

Isolation isolation() default Isolation.DEFAULT;

int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

boolean readOnly() default false;

Class<? extends Throwable>[] rollbackFor() default {};

String[] rollbackForClassName() default {};

Class<? extends Throwable>[] noRollbackFor() default {};

String[] noRollbackForClassName() default {};

Page 11: Grails Transactions

1111

@Transactional

String value() default "";

Propagation propagation() default Propagation.REQUIRED;

Isolation isolation() default Isolation.DEFAULT;

int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

boolean readOnly() default false;

Class<? extends Throwable>[] rollbackFor() default {};

String[] rollbackForClassName() default {};

Class<? extends Throwable>[] noRollbackFor() default {};

String[] noRollbackForClassName() default {};

Page 12: Grails Transactions

1212

Transaction Isolation

org.springframework.transaction.annotation.Isolation

READ_UNCOMMITTED

• Order is maintained

• Allows dirty reads

READ_COMMITTED

• Only reads committed data, but allows non-repeatable reads

REPEATABLE_READ

• Allows phantom reads

SERIALIZABLE

• Blocks read and write access between transactions, guarantees correct results

DEFAULT

• Use the default database isolation, often READ_COMMITTED (MySQL uses REPEATABLE_READ)

Page 13: Grails Transactions

1313

@Transactional

String value() default "";

Propagation propagation() default Propagation.REQUIRED;

Isolation isolation() default Isolation.DEFAULT;

int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

boolean readOnly() default false;

Class<? extends Throwable>[] rollbackFor() default {};

String[] rollbackForClassName() default {};

Class<? extends Throwable>[] noRollbackFor() default {};

String[] noRollbackForClassName() default {};

Page 14: Grails Transactions

1414

Transaction Propagation

org.springframework.transaction.annotation.Propagation

REQUIRED

SUPPORTS

MANDATORY

REQUIRES_NEW

NOT_SUPPORTED

NEVER

NESTED

Page 15: Grails Transactions

1515

@Transactional

String value() default "";

Propagation propagation() default Propagation.REQUIRED;

Isolation isolation() default Isolation.DEFAULT;

int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

boolean readOnly() default false;

Class<? extends Throwable>[] rollbackFor() default {};

String[] rollbackForClassName() default {};

Class<? extends Throwable>[] noRollbackFor() default {};

String[] noRollbackForClassName() default {};

Page 16: Grails Transactions

1616

@Transactional

String value() default "";

Propagation propagation() default Propagation.REQUIRED;

Isolation isolation() default Isolation.DEFAULT;

int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

boolean readOnly() default false;

Class<? extends Throwable>[] rollbackFor() default {};

String[] rollbackForClassName() default {};

Class<? extends Throwable>[] noRollbackFor() default {};

String[] noRollbackForClassName() default {};

Page 17: Grails Transactions

1717

Annotated Service Examples

Page 18: Grails Transactions

1818

Annotated Service Examples

package com.mycompany

import org.springframework.transaction.annotation.Propagationimport org.springframework.transaction.annotation.Transactional

@Transactionalclass SomeService {

def someMethod() { ... }

@Transactional(propagation=Propagation.MANDATORY) def someOtherMethod() { ... }}

Page 19: Grails Transactions

1919

Annotated Service Examples

package com.mycompany

import org.springframework.transaction.annotation.Propagationimport org.springframework.transaction.annotation.Transactional

class SomeOtherService {

def someMethod() { ... }

@Transactional def someOtherMethod() { ... }

@Transactional(propagation=Propagation.MANDATORY) def yetAnotherMethod() { ... }}

Page 20: Grails Transactions

2020

Unintentionally Bypassing the Bean Proxy

Page 21: Grails Transactions

2121

Unintentionally Bypassing the Bean Proxy

@Transactionalvoid someMethod(...) { // do some work ... storeAuditData(...)}

@Transactional(propagation=Propagation.REQUIRES_NEW)void storeAuditData(...) { //}

Page 22: Grails Transactions

2222

Unintentionally Bypassing the Bean Proxy

def grailsApplication

@Transactionalvoid someMethod(...) { // do some work ... def myProxy = grailsApplication.mainContext.fooService myProxy.storeAuditData(...)}

@Transactional(propagation=Propagation.REQUIRES_NEW)void storeAuditData(...) { //}

Page 23: Grails Transactions

2323

Spring Classes and Utility Methods

Page 24: Grails Transactions

2424

PlatformTransactionManager

org.springframework.transaction.PlatformTransactionManager

(org.springframework.orm.hibernate3.HibernateTransactionManager)

• TransactionStatus getTransaction(TransactionDefinition definition) throws

TransactionException;

• void commit(TransactionStatus status) throws TransactionException;

• void rollback(TransactionStatus status) throws TransactionException;

Page 25: Grails Transactions

2525

TransactionSynchronizationManager

org.springframework.transaction.support.TransactionSynchronizationManager

• public static void bindResource(Object key, Object value)

• public static Object unbindResource(Object key)

• public static boolean hasResource(Object key)

• public static Object getResource(Object key)

Page 26: Grails Transactions

2626

TransactionSynchronizationManager

For example:

• TransactionSynchronizationManager.bindResource(

getSessionFactory(), new SessionHolder(session))

• TransactionSynchronizationManager.bindResource(

getDataSource(), new ConnectionHolder(connection));

Page 27: Grails Transactions

2727

TransactionAspectSupport

org.springframework.transaction.interceptor.TransactionAspectSupport

• public static TransactionStatus currentTransactionStatus() throws

NoTransactionException

Page 28: Grails Transactions

2828

TransactionAspectSupport

Useful for MetaClass utility methods:

• isTransactionActive()

• → TransactionSynchronizationManager.isSynchronizationActive()

• getCurrentTransactionStatus()

• → TransactionAspectSupport.currentTransactionStatus()

• setRollbackOnly()

• → TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()

• isRollbackOnly()

• → getCurrentTransactionStatus().isRollbackOnly()

Page 29: Grails Transactions

2929

LazyConnectionDataSourceProxy

org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy

• Used by default in Grails 2.3

• Waits to create a connection until it's actually needed

• Caches calls to setAutoCommit, setReadOnly, setTransactionIsolation, etc.

Page 30: Grails Transactions

3030

Two-phase Commit (2PC)

Page 31: Grails Transactions

3131

Two-phase Commit (2PC)

The Atomicos plugin is the easiest way

• http://grails.org/plugin/atomikos

• http://grails-plugins.github.io/grails-atomikos/docs/manual/index.html

Supports multiple databases and JMS (and any other XA-compliant

technology)

Page 32: Grails Transactions

3232

Some Thoughts About Scaffolding

Page 33: Grails Transactions

3333

Want To Learn More?

Page 36: Grails Transactions

3636

Thanks!