Mastering Transactions In Java Transactions: Basics.

58

Transcript of Mastering Transactions In Java Transactions: Basics.

Page 1: Mastering Transactions In Java Transactions: Basics.
Page 2: Mastering Transactions In Java Transactions: Basics.

Mastering Transactions

In Java

Page 3: Mastering Transactions In Java Transactions: Basics.

Transactions:

Basics

Page 4: Mastering Transactions In Java Transactions: Basics.

• In the world of programming a Transaction is a series of actions that must occur as a group.

• If any portion of the group of actions fails, the entire Transaction fails.

Transactions: Basics

This lecture is based in part on the book Java Transaction Design Strategies by Mark Richards.

This lecture is based in part on Java Transaction Processing by Mark Little, Jon Maron and Greg Pavlik.

Page 5: Mastering Transactions In Java Transactions: Basics.

• A.C.I.D. is the well-known acronym for the characteristics of a successful and valid transaction.

• Atomicity• Consistency• Isolation• Durability

These principles were first describedby James Gray in the late 1970s and in 1983 the acronym A.C.I.D. was coined by Andreas Reuter and Theo Härder.

Transactions: Basics

Page 6: Mastering Transactions In Java Transactions: Basics.

• Atomicity—follows the metaphor of the “Atom”.

• You cannot have half an atom.

• If a transaction does not complete perfectly, it gets undone and the database should be left in a state as if nothing at all ever happened.

Transactions: Basics

Page 7: Mastering Transactions In Java Transactions: Basics.

• Consistency—change must take the database from one consistent state before the transaction started to another consistent state after the transaction has finished.

• Referential integrity must be maintained.

• The data meets all rules for validation.

• Numeric fields must only hold numeric data, etc.

Transactions: Basics

Page 8: Mastering Transactions In Java Transactions: Basics.

• Isolation—by its nature a transaction consists of several small steps. All the intermediate steps must be completed before the entire transaction can be called complete.

• While the transaction is in progress but not yet complete, the in-progress changes must be hidden or isolated from the rest of the database and other transactions.

• If I deduct money from the checking account and then deposit that money in the savings account, the checking deduction must not be visible to anyone until the transaction is done.

Transactions: Basics

Page 9: Mastering Transactions In Java Transactions: Basics.

• Durability—once a transaction is deemed complete, the transaction will not be lost.

• Don’t announce success until everything is committed.

• Transaction Log—a common strategy used to back up the contents of the transaction.

Transactions: Basics

Page 10: Mastering Transactions In Java Transactions: Basics.

Transactions:

Local Transactions

Page 11: Mastering Transactions In Java Transactions: Basics.

• A Local Transaction is a punt.

• We let the database handle our transactions entirely.

• This works fine if our Logical Unit of Work [LUW] consists of exactly one query.

• Not exactly what we have in mind when we think of a “transaction”.

Transactions: Local Transactions

Page 12: Mastering Transactions In Java Transactions: Basics.

• If we want to do two things, such as subtract from a savings account and deposit into a checking account, a Local Database transaction is not much help.

• If we subtract $100 from the savings account and then the system crashes before we get the deposit done—we just lost $100.

• So, already, we have reached the end of the usefulness of a database-local transaction.

• Java offers several ways to help us out.

Transactions: Local Transactions

Page 13: Mastering Transactions In Java Transactions: Basics.

Transactions:

Gotchas In Java

Page 14: Mastering Transactions In Java Transactions: Basics.

• In the Java world we are fortunate to have great APIs such as the JTA (Java Transaction API)

• Thanks to Rod Johnson, we have the wonderful Spring Framework.

• After the horror story that was EJB2.1Entity beans, JPA was a reason to pump your fist in joy.

• Thanks to EJB 3.0, we have some great frameworks that take away all the peril of using transactions… right?

Transactions: Gotchas In Java

Page 15: Mastering Transactions In Java Transactions: Basics.

• Consider this simple code. It runs flawlessly.

• So this JPA code will happily insert the deposit and return the idid of the generated row, right?

Transactions: Gotchas In Java

public class JpaBankServiceImpl{ @PersistenceContext(unitName=“bank”) EntityManager entityMan;

public long makeDeposit( Deposit deposit ) throws Exception { entityMan.persist( deposit ); return deposit.getDepositId(); } }

Page 16: Mastering Transactions In Java Transactions: Basics.

• Consider this simple code. It runs flawlessly.

• So this code will happily insert the deposit and return the idid of the generated row, right?

• Nope! It won’t throw an exception, will return 0 and leave the database untouched. It won’t do a damn thing!

Transactions: Gotchas In Java

public class JpaBankServiceImpl{ @PersistenceContext(unitName=“bank”) EntityManager entityMan;

public long makeDeposit( Deposit deposit ) throws Exception { entityMan.persist( deposit ); return deposit.getDepositId(); } }

Page 17: Mastering Transactions In Java Transactions: Basics.

• What? The newbie asks. “What went wrong?”

• Nothing. For this to work you have to wrap it in a transaction.

• But it won’t tell you anything is wrong.

Transactions: Gotchas In Java

public class JpaBankServiceImpl{ @PersistenceContext(unitName=“bank”) EntityManager entityMan;

public long makeDeposit( Deposit deposit ) throws Exception { entityMan.persist( deposit ); return deposit.getDepositId(); } }

Page 18: Mastering Transactions In Java Transactions: Basics.

• Your changes were placed in the L1 object cache.

• But only a transactiontransaction tells JPA to synchronize its object cacheobject cache with the database.

Transactions: Gotchas In Java

Page 19: Mastering Transactions In Java Transactions: Basics.

Sidebar: What is the Object Cache?

• When an object and its dependencies are pulled from the database, it may require several queries.

• The L1L1 transaction Object Cache is part of every EnityManager and is not shared.

• Not to be confused with the L2 shared cache, stored in the EntityManagerFactory, which is visible to all EnityManagers.

• Each EnityManager keeps its own Object Cache.

Page 20: Mastering Transactions In Java Transactions: Basics.

• In JPA, when you persistpersist an object, it only writes the object to its Object CacheObject Cache.

• JPA onlyonly writes its object to the database upon the commitcommit of a transactionof a transaction.

• So, nono transaction transaction = nono commit commit.

Transactions: Gotchas In Java

Page 21: Mastering Transactions In Java Transactions: Basics.

Transactions:

Gotchas In Java: Spring

Page 22: Mastering Transactions In Java Transactions: Basics.

• Spring does a lot for us but we still need to think and understand.

• Remember there are two broad categories: programmatic transactions declarative transactions

• Using programmaticprogrammatic transactions, we have to code everything.

• Using declarativedeclarative transactions, we add an annotationannotation to our code and a framework such as Spring does the rest, right?

Transactions: Gotchas In Java: Spring

Page 23: Mastering Transactions In Java Transactions: Basics.

• In the case of Spring, our declarative model asks us to provide an annotation such as @Transactional@Transactional.

Transactions: Gotchas In Java: Spring

public class SpringBankServiceImpl{ @PersistenceContext(unitName=“bank”) EntityManager entityMan;

@Transactional public long makeDeposit( Deposit deposit ) throws Exception { entityMan.persist( deposit ); return deposit.getDepositId(); } }

• You test it again and no error, no exception and no change to the DB.

Page 24: Mastering Transactions In Java Transactions: Basics.

• The problem is, like with all things Spring, you need to update the Spring configuration file.

Transactions: Gotchas In Java: Spring

<tx:annotation-driven transaction-manager=“transactionManager” />

• So, transactionManagertransactionManager must be a Spring beanbean that is defined elsewhere in your Spring configuration file.

• Spring will implement this using a cool Spring feature called an interceptorinterceptor that in turn will use another annotation, @Transaction.

Page 25: Mastering Transactions In Java Transactions: Basics.

• Okay—sweet—now we’re good.

Transactions: Gotchas In Java: Spring

public class SpringBankServiceImpl{ @PersistenceContext(unitName=“bank”) EntityManager entityMan;

@Transactional public long makeDeposit( Deposit deposit ) throws Exception { entityMan.persist( deposit ); return deposit.getDepositId(); } }

• But wait a second. There are a lot of choices involved in setting up a transaction. We’re using the defaults here.

<tx:annotation-driven transaction-manager=“transactionManager” />

Page 26: Mastering Transactions In Java Transactions: Basics.

• When we use @Transactional @Transactional by itself with no parameters, what are the default values for things like propagation mode, the read-only flag and transaction isolation?

• Most importantly, what happens when we need to roll back?

Transactions: Gotchas In Java: Spring

Page 27: Mastering Transactions In Java Transactions: Basics.

• When we use a bare @Transactional@Transactional, the default values make it as if we had written this:

• Most importantly, the transaction will not roll back on a checked exception.

Transactions: Gotchas In Java: Spring

@Transactional( read-only=false, propagation=REQUIRED, isolation-level=READ_COMMITTED )

Page 28: Mastering Transactions In Java Transactions: Basics.

• Here’s a great example from Mark Richards:

Transactions: Gotchas In Java: Spring

@Transactional(readOnly=true, propagation=Propagation.SUPPORTS)public long insertDeposit( Deposit deposit ) throws Exception{ // JDBC insert code}

• Notice this is read-only. So, what happens?

A. It throws a read-only connection exception.B. It inserts the deposit and commits the data.C. It does nothing because the propagation level says supports and there is no transaction to support.

Page 29: Mastering Transactions In Java Transactions: Basics.

• Here’s a great example from Mark Richards:

Transactions: Gotchas In Java: Spring

@Transactional(readOnly=true, propagation=Propagation.SUPPORTS)public long insertDeposit( Deposit deposit ) throws Exception{ // JDBC insert code}

• Notice this is read-only. So, what happens?

A. It throws a read-only connection exception.B. It inserts the deposit and commits the data.C. It does nothing because the propagation level says supports and there is no transaction to support.

WTF?

Page 30: Mastering Transactions In Java Transactions: Basics.

Transactions: Gotchas In Java: Spring@Transactional(readOnly=true, propagation=Propagation.SUPPORTS)

public long insertDeposit( Deposit deposit ) throws Exception{ // JDBC insert code}

• B. It inserts the deposit and commits the data.

• The read-only flag is only honored when there is a transaction. Since it’s SUPPORTS and there was no already-started transaction to support, it never got one.

• The JDBC code used a local (database) transaction and committed the code.

Page 31: Mastering Transactions In Java Transactions: Basics.

Transactions: Gotchas In Java: Spring

@Transactional(readOnly=true, propagation=Propagation.REQUIRED)public long insertDeposit( Deposit deposit ) throws Exception{ // JDBC insert code}

A. It throws a read-only connection exception.B. It inserts the deposit and commits the data.C. It does nothing because the readOnly flag is set to true.

• Okay, what if we make the Propagation REQUIRED?

Page 32: Mastering Transactions In Java Transactions: Basics.

Transactions: Gotchas In Java: Spring

@Transactional(readOnly=true, propagation=Propagation.REQUIRED)public long insertDeposit( Deposit deposit ) throws Exception{ // JDBC insert code}

A. It throws a read-only connection exception.B. It inserts the deposit and commits the data.C. It does nothing because the readOnly flag is set to true.

• Okay, what if we make the Propagation REQUIRED?

• Because there was a transaction [REQUIRED], the readOnly flag was honored.

Page 33: Mastering Transactions In Java Transactions: Basics.

Transactions: Gotchas In Java: Spring

• There are a lot of hidden problems with all of the various propagation modes and you need to be very careful.

Page 34: Mastering Transactions In Java Transactions: Basics.

Transactions: Gotchas In Java: Spring

• Next, I asked: what happens when you need a roll back?@Transactional(propagation=Propagation.REQUIRED)public long transferCash( Transfer transfer) throws Exception{ try { deductFromSavings( transfer ); depositIntoChecking( transfer ); return tranfer.getTransactionId(); } catch( Exception e ) { // log error throw e; }}

• What if depositIntoChecking() throws a checked Exception? Does everything get rolled back?

Page 35: Mastering Transactions In Java Transactions: Basics.

Transactions: Gotchas In Java: Spring

• No!No! The update from deductFromSavings() is committed!

@Transactional(propagation=Propagation.REQUIRED)public long transferCash( Transfer transfer) throws Exception{ try { deductFromSavings( transfer ); depositIntoChecking( transfer ); return tranfer.getTransactionId(); } catch( Exception e ) { // log error throw e; }}

Page 36: Mastering Transactions In Java Transactions: Basics.

Transactions: Gotchas In Java: Spring

• In this scenario ( using default values) for both Spring and EJB 3, only runtime (unchecked) exceptions cause the transaction to roll back.

• When you code your annotation like this, the transaction will NOT roll back for any checked exception.@Transactional(propagation=Propagation.REQUIRED)

• Why on earth would Spring or EJB be set up that way?

• The explanation is that there might be some exceptions that you can recover from. So, to not preclude that alternative, they are set up this way.

Page 37: Mastering Transactions In Java Transactions: Basics.

Transactions: Gotchas In Java: Spring

• So, the take away in Spring declarative transactions is that you must specify which checked exceptions warrant a roll back.@Transactional(propagation=Propagation.REQUIRED, rollbackFor=SQLException.class)

@Transactional(propagation=Propagation.REQUIRED, rollbackFor=SQLException.class)

• The rollbackFor parameter takes either a single exception or an array of them.

• There is another alternative rollbackForClassName and yet another for noRollbackFor.

Page 38: Mastering Transactions In Java Transactions: Basics.

Transactions:

3 Alternative Models

Page 39: Mastering Transactions In Java Transactions: Basics.

Transactions: 3 Alternative Models

• There are three broad approaches to doing transactions in Java.• These are known as “Transaction ModelsTransaction Models”.

Local (database) Transactions Programmatic Transactions Declarative Transactions

Page 40: Mastering Transactions In Java Transactions: Basics.

Transactions: 3 Alternative Models: Local

• Local (database) TransactionsLocal (database) Transactions are an abdication on the part of the Java tier. • The database does 100% of the work.• The Connection is the closest you get can to the transactions.• By turning off the setAutoCommit( false ); in your Java code you can achieve ACID over several database actions and by then calling either commit() at the end or rollback(); in the case of an Exception.

Page 41: Mastering Transactions In Java Transactions: Basics.

Transactions: 3 Alternative Models: Programmatic

• With Programmatic Transactions you write program logic to manage the transactions.• Instead of managing the Connection, you manage the transaction transaction from your Java code.

Page 42: Mastering Transactions In Java Transactions: Basics.

public class SpringProgrammaticTransaction{ @PersistenceContext(unitName=“banking”) EntityManager em; JpaTransactionManager transManager = null; public void doTransfer( TransactionValues modifications ) { TransactionStatus ts = jtm.getTransaction( new DefaultTransactionDefinition() ); try { em.persist( modifications ); AcctState acctState = em.find( AcctState.class, modifications.getTransactionId() ); applyTransactionModifications( acctState, modifications ); em.persist( acctState ); transManager.commit( ts ); } catch( Exception e ) { transManager.rollback( ts ); } }

// Spring injected public void setTransManager( JpaTransactionManager transManager ) { this.transManager = transManager; }}

Transactions: 3 Alternative Models: Programmatic Spring

Page 43: Mastering Transactions In Java Transactions: Basics.

Transactions: 3 Alternative Models: Declarative

• With Declarative Transactions you signal your wishes for the transaction and rely on your understanding of the nuances of the annotations and their various attribute values.

• Another name for CMT—Container-Managed Transactions.

• Spring uses the @Transactional@Transactional attribute.

• EJB 3.0 uses @TransactionAttribute@TransactionAttribute.

• In declarative transactions automatic roll-back is not a given after a checked exception occurs.

Page 44: Mastering Transactions In Java Transactions: Basics.

Transactions: 3 Alternative Models: Declarative EJB3

@Statelesspublic class EJB30DeclarativeBankingService implements BankingService { @PersistenceContext(unitName=“banking”) EntityManager em; @Resource SessionContext ctx;

@TransactionalAttribute( TransactionAttributeType.REQUIRED ) public void transferMoney( Transfer transfer ) throws Exception { try { AcctInfo acctInfo = em.find( AccountInfo.class, transfer.getTransferId(); if( transferAcceptible( acctInfo, transfer ) ) { em.persist( transfer ); em.persist( acctInfo ); } } catch( Exception e ) { ctx.setRollbackOnly(); } }}

Page 45: Mastering Transactions In Java Transactions: Basics.

Transactions: 3 Alternative Models: Declarative Spring

public class SpringDeclarativeBankingService { @PersistenceContext(unitName=“banking”) EntityManager em; @Transactional( @Transactional( propagation=Propagation.REQUIRED, =Propagation.REQUIRED, rollbackFor=Exception.class )=Exception.class ) public void transferMoney( Transfer transfer ) throws Exceptio { em.persist( transfer ); AcctData acctData = em.find( AcctData.class, transfer.getTransferId() ); if( accountAgreesWithTransfer( transfer, acctData ) ) { acctData.applyTransfer( transfer ); } }}

Page 46: Mastering Transactions In Java Transactions: Basics.

Transactions:

High Concurrency Strategies

Page 47: Mastering Transactions In Java Transactions: Basics.

Transactions: High Concurrency Strategies

• For many concurrent transactions, the transaction strategy changes.

• If your transactions take too long, you shorten the transaction.

• You remove operations such as reads and processing that do not need to be transactional.

Page 48: Mastering Transactions In Java Transactions: Basics.

Transactions: High Concurrency Strategies

• Naked reads—you read a value without a transaction—even though you are planning to update it.

• Another write could have gotten in there right after your read and then you would lose that update.

• Each read gets a version stamp and then after your optimistic read you trust but verify and compare the version stamp.

Page 49: Mastering Transactions In Java Transactions: Basics.

Transactions: High Concurrency Strategies: Not!@Stateless

@Remote(BankingService.class)public class BankingServiceImpl implements BankingService{ @Resource SessionContext ctx; @PersistenceContext( unitName=“banking” ) EntityManager em; @TransactionAttribute(TransactionAttributeType.REQUIRED) public void transferMoney( Transfer transfer ) throws Exception { try { AccountHolder holder = em.find( AccountHolder.class, transfer.getAccountId() ); Withdrawl withdrawl = sourceAccountContainsSufficientFunds( holder, transfer )

if( withdrawl != null ) { em.persist( withdrawl ); verifySpamBeingSent( holder ); verifyAddressSentToCatalogCompanies( holder ); verifyAccountIsEligibleForOverdraftProtection( holder ); verifySentEmailOfferingToStopSpam( holder ); Deposit deposit = buildDepositFromTransfer( holder, transfer );

em.persist( deposit ); } } catch( Exception e ) { ctx.setRollbackOnly(); } }}

Page 50: Mastering Transactions In Java Transactions: Basics.

Transactions: High Concurrency Strategies: Not!@Stateless

@Remote(BankingService.class)public class BankingServiceImpl implements BankingService{ @Resource SessionContext ctx; @PersistenceContext( unitName=“banking” ) EntityManager em; @TransactionAttribute(TransactionAttributeType.REQUIRED) public void transferMoney( Transfer transfer ) throws Exception { try { AccountHolder holder = em.find( AccountHolder.class, transfer.getAccountId() ); Withdrawl withdrawl = sourceAccountContainsSufficientFunds( holder, transfer )

if( withdrawl != null ) { em.persist( withdrawl ); verifySpamBeingSent( holder ); verifyAddressSentToCatalogCompanies( holder ); verifyAccountIsEligibleForOverdraftProtection( holder ); verifySentEmailOfferingToStopSpam( holder ); Deposit deposit = buildDepositFromTransfer( holder, transfer );

em.persist( deposit ); } } catch( Exception e ) { ctx.setRollbackOnly(); } }}

Everything in black is included in the transaction. Bad!

Page 51: Mastering Transactions In Java Transactions: Basics.

Transactions: High Concurrency Strategies: Yes!public void transferMoney( Transfer transfer ) throws Exception

{ UserTransaction txn = null;

try { AccountHolder holder = service.getAccountHolder( transfer.getAccountId() );

verifySpamBeingSent( holder ); verifyAddressSentToCatalogCompanies( holder ); verifyAccountIsEligibleForOverdraftProtection( holder ); verifySentEmailOfferingToStopSpam( holder );

Withdrawl withdrawl = sourceAccountContainsSufficientFunds( holder, transfer ); Deposit deposit = buildDepositFromTransfer( holder, transfer ); txn = (UserTransaction) ctx.lookup( “UserTransaction” ); txn.begin(); service.makeWithdrawl( withdrawl ); service.makeDeposit( deposit ); txn.commit();} catch( Exception e ) { if( txn != null ) { txn.rollback(); } }}

Page 52: Mastering Transactions In Java Transactions: Basics.

Transactions: High Concurrency Strategies: Yes!public void transferMoney( Transfer transfer ) throws Exception

{ UserTransaction txn = null;

try { AccountHolder holder = service.getAccountHolder( transfer.getAccountId() );

verifySpamBeingSent( holder ); verifyAddressSentToCatalogCompanies( holder ); verifyAccountIsEligibleForOverdraftProtection( holder ); verifySentEmailOfferingToStopSpam( holder );

Withdrawl withdrawl = sourceAccountContainsSufficientFunds( holder, transfer ); Deposit deposit = buildDepositFromTransfer( holder, transfer ); txn = (UserTransaction) ctx.lookup( “UserTransaction” ); txn.begin(); service.makeWithdrawl( withdrawl ); service.makeDeposit( deposit ); txn.commit();} catch( Exception e ) { if( txn != null ) { txn.rollback(); } }}

Everything in black is included in the transaction. Better!

Page 53: Mastering Transactions In Java Transactions: Basics.

Transactions:

High Performance Strategies

Page 54: Mastering Transactions In Java Transactions: Basics.

Transactions: High Performance Strategies

• If your goal is high performance, then there is a bag of techniques.

• You abandon any transaction management in the Java layer.

• You revert to local transactions managed 100% by the DB.

• No transaction-guaranteed ACID across multiple queries

Page 55: Mastering Transactions In Java Transactions: Basics.

Transactions: High Performance Strategies

• Each read, update, delete rides in its own transaction that commits at the end of each statement.

• It uses a Compensation StrategyCompensation Strategy.

• A Compensation StrategyCompensation Strategy keeps track of the transaction steps that were committed.

• In case of an error, the Compensation Compensation StrategyStrategy reverses the transaction steps to undoundo the committed steps.

• No transaction IsolationIsolation—data is shared instantly.

Page 56: Mastering Transactions In Java Transactions: Basics.

Transactions: High Performance Strategies

• J2EE Activity Service for Extended Transactions (JSR 95) can serve as a Compensation Strategy framework.

• JBoss Business Activity Framework

Page 57: Mastering Transactions In Java Transactions: Basics.

public class TradingService { private CompController compController = new CompController(); private AcctDAO acctDao = new AcctDAO(); private TradeDAO tradeDao = new TradeDAO();

public void processTrade(TradeData trade) throws Exception { String compId = UUID.randomUUID().toString(); try { //start the compensation scope compController.startScope(compId); //get the original account values and set the acct balance AcctData acct = acctDao.getAcct(trade.getAcctId()); double oldBalance = acct.getBalance(); if (trade.getSide().equals("BUY")) { acct.setBalance(acct.getBalance() - (trade.getShares() * trade.getPrice())); } else { acct.setBalance(acct.getBalance() + (trade.getShares() * trade.getPrice())); } //insert the trade and update the account long tradeId = tradeDao.insertTrade(trade); compController.registerAction(compId, "insertTrade", tradeId); acctDao.updateAcct(acct); compController.registerAction(compId, "updateAcct", oldBalance); //close the compensation scope compController.stopScope(compId); } catch (Exception e) { //reverse the individual database operations compController.compensate(compId); throw e; } } }

Page 58: Mastering Transactions In Java Transactions: Basics.

Transactions:

Questions?