All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.java.ao.Transaction Maven / Gradle / Ivy

Go to download

This is the full Active Objects library, if you don't know which one to use, you probably want this one.

There is a newer version: 6.1.1
Show newest version
/*
 * Copyright 2007 Daniel Spiewak
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at
 * 
 *	    http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.java.ao;

import java.sql.Connection;
import java.sql.SQLException;

import static net.java.ao.sql.SqlUtils.closeQuietly;

/**
 * 

Allows for the syntactically simple use of database transactions within the * ActiveObjects API. This class's syntax is modeled after the transaction * do ... end syntax provided by Rails's ActiveRecord ORM. The intention * is to provide the simplest possible encapsulation around the transaction * functionality. As such, AO transactions lack some of the power of the * underlying database transaction function (such as arbitrary save-points).

* *

The design behind Transaction is modeled after the * following code snippet:

* *
new Transaction<Object>(manager) {
 *     public Object run() {
 *         Account a = getEntityManager().get(Account.class, 1);
 *         Account b = getEntityManager().get(Account.class, 2);
 *
 *         a.setBalance(a.getBalance() - 1000);
 *         a.save();
 *
 *         b.setBalance(b.getBalance() + 1000);
 *         b.save();
 *
 *         return null;
 *     }
 * }.execute();
* *

The transaction will be committed only after the run() * method returns. Thus, a.save() doesn't immediately modify * the database values, only upon the committal of the transaction. If any * conflicts are detected, JDBC will automatically throw an {@link SQLException}. * Transaction catches this exception and rolls back the * transaction, ensuring data integrity. Once the transaction is rolled back, the * exception is rethrown from the execute() method.

* *

In cases where the transaction generates data which must be returned, this * can be accomplished by returning from the {@link #run()} method against the * parameterized type. Thus if a transaction to create an account is utilized:

* *
Account result = new Transaction<Account>(manager) {
 *     public Account run() throws SQLException {
 *         Account back = getEntityManager().create(Account.class);
 *
 *         back.setBalance(0);
 *         back.save():
 *
 *         return back;
 *     }
 * }.execute();
* *

The value returned from run() will be passed back up the call * stack to execute(), which will return the value to the caller. * Thus in this example, result will be precisely the back * instance from within the transaction. This feature allows data to escape the * scope of the transaction, thereby achieving a greater usefulness.

* *

The JDBC transaction type used is {@link Connection#TRANSACTION_SERIALIZABLE}.

* * @author Daniel Spiewak * @see java.sql.Connection */ public abstract class Transaction { private final EntityManager manager; private enum TransactionState { START, RUNNING, COMMITTED } /** * Creates a new Transaction using the specified * {@link EntityManager} instance. If the specified instance is null, * an exception will be thrown. * * @param manager The EntityManager instance against which the * transaction should run. * @throws IllegalArgumentException If the {@link EntityManager} instance is null. */ public Transaction(EntityManager manager) { if (manager == null) { throw new IllegalArgumentException("EntityManager instance cannot be null"); } this.manager = manager; } protected final EntityManager getEntityManager() { return manager; } /** *

Executes the transaction defined within the overridden {@link #run()} * method. If the transaction fails for any reason (such as a conflict), it will * be rolled back and an exception thrown. The value returned from the * run() method will be returned from execute().

* *

Custom JDBC code can be executed within a transaction. However, one * should be a bit careful with the mutable state of the {@link Connection} * instance obtained from getEntityManager().getProvider().getConnection(). * This is because it is this exact instance which is used in all database * operations for that transaction. Thus it is technically possible to commit a * transaction prematurely, disable the transaction entirely, or otherwise really * mess up the internals of the implementation. You do not have to * call setAutoCommit(boolean) on the {@link Connection} * instance retrieved from the {@link DatabaseProvider}. The connection is * already initialized and within an open transaction by the time it gets to your * custom code within the transaction.

* * @return The value (if any) returned from the transaction run() * @throws SQLException If the transaction failed for any reason and was rolled back. * @see #run() */ public T execute() throws SQLException { final DatabaseProvider provider = manager.getProvider(); TransactionState state = TransactionState.START; Connection c = null; try { c = provider.startTransaction(); state = TransactionState.RUNNING; final T back = run(); provider.commitTransaction(c); state = TransactionState.COMMITTED; return back; } finally { if (state == TransactionState.RUNNING && c != null) { provider.rollbackTransaction(c); } provider.setCloseable(c, true); closeQuietly(c); } } /** *

Called internally by {@link #execute()} to actually perform the actions * within the transaction. Any SQLException(s) should be * allowed to propogate back up to the calling method, which will ensure * that the transaction is rolled back and the proper resources disposed. If * the transaction generates a value which must be passed back to the calling * method, this value may be returned as long as it is of the parameterized * type. If no value is generated, null is an acceptable return * value.

* *

Be aware that any operations performed within a transaction * (even if indirectly invoked by the run() method) will use * the exact same {@link Connection} instance. This is to ensure * integrity of the transaction's operations while at the same time allowing * custom JDBC code and queries within the transaction.

* * @return Any value which must be passed back to the calling point (outside * the transaction), or null. * @throws SQLException If something has gone wrong within the transaction and * it requires a roll-back. */ protected abstract T run() throws SQLException; }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy