Transactions

Within a Transaction, you can also control JDBC batching (batch size, flushing), turn on/off transaction logging, and turn on/off cascading of save and delete. ...(under construction)... how?

Implicit

Transactions are created implicitly for you

When there is no current transaction you can still execute a query, save or delete a bean etc. When this happens Ebean will create an implicit transaction and commit it (or rollback if there was a failure).

// execute a query without an existing transaction...
// ... a "implicit" transaction will be created
// ... and be commited (or rolled back) for you
List<User> users =
            Ebean.find(User.class)
                .join("customer")
                .where().eq("state", UserState.ACTIVE)
                .findList();

// execute a save without an existing transaction...
// ... will create an implicit transaction
// ... and be commited (or rolled back) for you
Ebean.save(user);

The following SLF4J logs show the transaction that was implicitly created for the query.

16:38:26.362 [main] DEBUG c.a.e.server.lib.thread.ThreadPool - ThreadPool grow created [Ebean-h2.0] size[0]
16:38:26.369 [main] TRACE org.avaje.ebean.TXN - txn[1002] Begin
16:38:26.548 [main] INFO  org.avaje.ebean.TXN - txn[1002] Commit
16:38:26.551 [main] TRACE org.avaje.ebean.TXN - txn[1003] Begin
16:38:26.554 [main] TRACE org.avaje.ebean.SQL - txn[1003] delete from or_order_ship
16:38:26.555 [main] DEBUG org.avaje.ebean.SUM - txn[1003] Delete table[or_order_ship] rows[0] bind[null]
16:38:26.556 [main] TRACE org.avaje.ebean.SQL - txn[1003] delete from o_order_detail
16:38:26.557 [main] DEBUG org.avaje.ebean.SUM - txn[1003] Delete table[o_order_detail] rows[0] bind[null]
16:38:26.557 [main] TRACE org.avaje.ebean.SQL - txn[1003] delete from o_order
16:38:26.557 [main] DEBUG org.avaje.ebean.SUM - txn[1003] Delete table[o_order] rows[0] bind[null]
16:38:26.557 [main] TRACE org.avaje.ebean.SQL - txn[1003] delete from contact
16:38:26.557 [main] DEBUG org.avaje.ebean.SUM - txn[1003] Delete table[contact] rows[0] bind[null]
16:38:26.557 [main] TRACE org.avaje.ebean.SQL - txn[1003] delete from o_customer
16:38:26.557 [main] DEBUG org.avaje.ebean.SUM - txn[1003] Delete table[o_customer] rows[0] bind[null]
16:38:26.557 [main] TRACE org.avaje.ebean.SQL - txn[1003] delete from o_address
16:38:26.558 [main] DEBUG org.avaje.ebean.SUM - txn[1003] Delete table[o_address] rows[0] bind[null]
16:38:26.563 [main] TRACE org.avaje.ebean.SQL - txn[1003] delete from o_country
16:38:26.563 [main] DEBUG org.avaje.ebean.SUM - txn[1003] DeleteSql table[o_country] rows[0] bind[null]
16:38:26.563 [main] TRACE org.avaje.ebean.SQL - txn[1003] delete from o_product
16:38:26.564 [main] DEBUG org.avaje.ebean.SUM - txn[1003] DeleteSql table[o_product] rows[0] bind[null]
16:38:26.567 [main] TRACE org.avaje.ebean.SQL - txn[1003] insert into o_country (code, name) values (?,?); --bind(NZ,New Zealand,)
16:38:26.568 [main] DEBUG org.avaje.ebean.SUM - txn[1003] Inserted [Country] [NZ]
16:38:26.568 [main] TRACE org.avaje.ebean.SQL - txn[1003] insert into o_country (code, name) values (?,?); --bind(AU,Australia,)
16:38:26.568 [main] DEBUG org.avaje.ebean.SUM - txn[1003] Inserted [Country] [AU]
16:38:26.570 [main] TRACE org.avaje.ebean.SQL - txn[1003] insert into o_product (id, sku, name, cretime, updtime) values (?,?,?,?,?); --bind

Programmatic

Using begin, commit etc in a try finally block

A traditional approach for demarcating transactions via a try finally block.

ebeanServer.beginTransaction();
ry {
 // fetch some stuff...
 Customer customer = ebeanServer.find(Customer.class, id);
 ...

 // save or delete stuff...
 ebeanServer.save(customer);
 ...

 // commit the transaction
 ebeanServer.commitTransaction();

 finally {
 // rollback the transaction if it was not committed
 ebeanServer.endTransaction();
}

Controlling behaviour in a transaction

You can use the Transaction to explicitly control behaviour.

Transaction transaction = ebeanServer.beginTransaction();
try {
  // turn of cascade persist
  transaction.setCascadePersist(false);

  // control the jdbc batch mode and size
  // transaction.setBatchMode(true); // equivalent to transaction.setBatch(PersistBatch.ALL);
  // transaction.setBatchMode(false); // equivalent to transaction.setBatch(PersistBatch.NONE);
  transaction.setBatch(PersistBatch.ALL); // PersistBatch: NONE, INSERT, ALL
  transaction.setCascadeBatch(PersistBatch.INSERT); // PersistBatch: NONE, INSERT, ALL
  transaction.setBatchSize(30);


  // for a large batch insert if you want to skip
  // getting the generated keys
  transaction.setBatchGetGeneratedKeys(false);

  // for batch processing via raw SQL you can inform
  // Ebean what tables were modified so that it can
  // clear the appropriate L2 caches
  String tableName = "o_customer";
  boolean inserts = true;
  boolean upates = true;
  boolean deletes = false;
  transaction.addModification(tableName, inserts, updates, deletes);

  ...
} finally {
  transaction.end();
}

@Transactional

You must be using ENHANCEMENT for @Transactional to work. That means you must be enhancing the classes via IDE Plugin, Ant Task or javaagent. Refer to the user guide for more details on enhancement.

Ebean can enhance your pojos to add transactional support. To do so put the @Transaction annotation on a method or class. Ebean enhancement will then add the supporting transactional logic (begin transaction, commit, rollback, suspend and resuming transactions etc).

...
// any old pojo
public class MyService {

	@Transactional
	public void runFirst() throws IOException {

		// run in a Transaction (REQUIRED is the default)

		// find a customer
		Customer customer = Ebean.find(Customer.class, 1);

		// call another "transactional" method
		runInTrans();
	}

	@Transactional(type=TxType.REQUIRES_NEW)
	public void runInTrans() throws IOException {

		// run in its own NEW transaction
		// ... suspend an existing transaction if required
		// ... and resume it when this method ends

		// find new orders
		List&lt;Order&gt; orders = Ebean.find(Order.class)
									.where().eq("status",OrderStatus.NEW)
									.findList();
		...

Put the @Transactional annotation on you methods and Ebean can enhance the classes adding the Transactional management.

Standard transaction scope types

This supports the standard propagation rules of REQUIRED (the default), REQUIRES_NEW, MANDATORY, SUPPORTS, NOT_SUPPORTS, NEVER. These are an exact match of the EJB TransactionAttributeTypes.

Nested Transactions

Transactional methods can be nested as in the above example where runFirst() calls runInTrans().

Isolation level and specific exception support

@Transactional (like Spring) supports isolation levels and explicit handling of Exceptions (to rollback or not for specific exceptions). Please refer to the User Guide for a more detailed explanation.

Interfaces

You can put @Transactional on interfaces and classes that implement those interfaces will get the transactional enhancement.

TxRunnable / TxCallable

TxRunnable and TxCallable are the programmatic equivalent to @Transactional.

You can mix @Transaction with TxRunnable and TxCallable if you like, they will behave correctly together.

public void myMethod() {
  ...
  System.out.println(" Some code in myMethod...");

  // run in Transactional scope...
  Ebean.execute(new TxRunnable() {
	public void run() {

		// code running in "REQUIRED" transactional scope
		// ... as "REQUIRED" is the default TxType
		System.out.println(Ebean.currentTransaction());

		// find stuff...
		User user = Ebean.find(User.class, 1);
		...

		// save and delete stuff...
		Ebean.save(user);
		Ebean.delete(order);
		...
	}
  });

  System.out.println(" more code in myMethod...");
}

Generally you will use TxRunnable like the above as anonymous inner classes.

The code inside the run() will execute inside a transactional scope with Ebean handling the transaction propagation for you (just like @Transactional).

// programmatic control over the scope such as
// ... isolation level
// ... and to rollback or not for specific exceptions

TxScope txScope = TxScope
			.requiresNew()
			.setIsolation(TxIsolation.SERIALIZABLE)
			.setNoRollbackFor(IOException.class);

Ebean.execute(txScope, new TxRunnable() {
	public void run() {
		...
}

Spring

(under construction)

JTA

(under construction)