# Transactions
Transactions are where Objectify departs most significantly from the patterns set by the Low-Level API and JDO/JPA. Objectify's transactions are inspired by [EJB](http://en.wikipedia.org/wiki/Enterprise_JavaBeans) and [NDB](https://developers.google.com/appengine/docs/python/ndb/transactions) transactions: Designed to allow modular, convenient transactional logic with a minimum of boilerplate.
You should familiarize yourself with the [Datastore Transactions](https://cloud.google.com/datastore/docs/concepts/transactions) documentation.
## Basic Transactions
A basic transaction looks like this:
```java
import static com.googlecode.objectify.ObjectifyService.ofy;
Thing th = ofy().transact(() -> {
Thing thing = ofy().load().key(thingKey).now();
thing.modify();
ofy().save().entity(thing);
return thing;
});
```
Note that all pending async operations are automatically completed at the end of a transaction.
### Idempotence
**Work MUST be idempotent.** A variety of conditions, including `ConcurrentModificationException`, can cause a transaction to retry. If you need to limit the number of times a transaction can be tried, use `transactNew(int, Work)`.
### Transaction Inheritance
Transaction contexts are inherited. In this example, the inner `transact()` does **not** start a new transaction:
```java
public void doSomeWork() {
ofy().transact(() -> {
doSomeMoreWork();
});
}
public void doSomeMoreWork() {
ofy().transact(() -> {
// When called from doSomeWork(), executes in the original transaction context
// When called from outside a transaction, executes in a new transaction
});
}
```
This makes it easy to create modular bits of transactional logic that can be used from a variety of contexts without having to pass around `Objectify` instances as parameters.
If you need to suspend a transaction and begin a new one, use the `transactNew()` method:
```java
ofy().transactNew(() -> {
Thing thing = ofy().load().key(thingKey).now();
thing.modify();
ofy().save().entity(thing);
});
```
The old transaction (if present) is suspended for the duration of the new transaction. After the transaction commits (or rolls back), the original transaction will resume.
### Escaping Transactions
There are often times in the middle of a transaction when you would like to make a datastore request outside of a transaction. Perhaps you wish to make a query without an ancestor(), or perhaps you wish to load an entity without enlisting it in the original transaction.
Objectify makes it easy to run operations outside of a transaction:
```java
ofy().transact(() -> {
Thing thing = ofy().load().key(thingKey).get();
Other other = ofy().transactionless(() -> ofy().load().key(thing.otherKey).now());
if (other.specialState) {
thing.modify();
ofy().save().entity(thing);
}
});
```
This circumstance doesn't come up often but it does come up.
### Transactions and Caching
Starting a transaction creates a new `Objectify` instance with a fresh, empty session cache. Loads and saves will populate this new instance cache; because of this, entities modified will appear modified after being loaded again within the same transaction. Unlike the low-level API, the datastore will not appear "frozen in time", although transaction isolation is maintained.
When a transaction successfully commits, its session cache will be copied into the parent `Objectify` instance's session cache.
Transactions integrate correctly with Objectify's global memcache. Reads and writes bypass the memcache, but a successful commit will reset the memcache value for changed entities.
## execute()
Objectify provides a method designed to facilitate EJB-like behavior:
```java
ofy().execute(TxnType.REQUIRED, () -> {
Thing thing = ofy().load().key(thingKey).now();
thing.modify();
ofy().save().entity(thing);
});
```
The `TxnType` enum provides the common [EJB transaction attributes](http://en.wikipedia.org/wiki/Enterprise_JavaBeans#Transactions):
| TxnType | Explanation |
|:----------|:--------------------------------------------------------------------------------------|
| MANDATORY | If not already in a transaction, throw an exception. Otherwise, use the transaction. |
| REQUIRED | If there is already a transaction in progress, use it. If not, start a new transaction. |
| REQUIRES\_NEW | If there is already a transaction in progress, suspend it. Always start a new transaction. |
| SUPPORTS | If there is a transaction in progress, use it. Otherwise, execute without a transaction. |
| NOT\_SUPPORTED | If there is a transaction in progress, suspend it. Execute without a transaction. |
| NEVER | If there is a transaction in progress, throw an exception. Otherwise, execute without a transaction. |
You will probably not use this method directly. However, you can easily use it to build AOP interceptors for Guice and Spring.
## Transactions with Guice
Using Guice AOP you can place EJB-like annotations on methods and eliminate the `transact()` boilerplate:
```java
public class Worker {
@Transact(TxnType.REQUIRED)
public void doSomeWork() {
ofy().load()... // do some work
Thing th = doSomeMoreWork();
ofy().save()... // do some work
}
@Transaction(TxnType.REQUIRED)
public Thing doSomeMoreWork() {
ofy().load()... // do some more work
return someThing;
}
}
```
```java
Worker worker = injector.getInstance(Worker.class);
worker.doSomeWork(); // all work done in a single transaction!
```
These methods are automatically wrapped in `Work` classes and executed with `Objectify.execute()`.
This requires two classes:
```java
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Transact {
TxnType value();
}
```
```java
public class TransactInterceptor implements MethodInterceptor {
/** Work around java's annoying checked exceptions */
private static class ExceptionWrapper extends RuntimeException {
private static final long serialVersionUID = 1L;
public ExceptionWrapper(Throwable cause) {
super(cause);
}
/** This makes the cost of using the ExceptionWrapper negligible */
@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
}
/** The only trick here is that we need to wrap & unwrap checked exceptions that go through the Work interface */
@Override
public Object invoke(final MethodInvocation inv) throws Throwable {
Transact attr = inv.getStaticPart().getAnnotation(Transact.class);
TxnType type = attr.value();
try {
return ofy().execute(type, () -> {
try {
return inv.proceed();
}
catch (RuntimeException ex) { throw ex; }
catch (Throwable th) { throw new ExceptionWrapper(th); }
});
} catch (ExceptionWrapper ex) { throw ex.getCause(); }
}
}
```
Now, to enable this interceptor, add this to your Guice module configuration:
```java
public static class YourModule extends AbstractModule {
@Override
protected void configure() {
bindInterceptor(Matchers.any(), Matchers.annotatedWith(Transact.class), new TransactInterceptor());
// continue with your guice configuration
}
}
```
You can find a working example of this in the [Motomapia](http://www.motomapia.com/) sample application.
## Transactions with Spring
With Spring AOP transaction management can be implemented as a cross-cutting _aspect_ to be applied to [point-cuts](http://docs.spring.io/spring/docs/2.0.x/reference/aop.html) in the application.
Imagine you have the following test worker, for which you want all methods to run in a transaction:
```java
@Component
public class TestWorker {
public void doSomething() {
ofy().save().entity(new TestEntity(id)).now();
}
public TestEntity doSomethingElse() {
return ofy().load().key(testKey).now();
}
...
}
```
When injecting the bean:
```java
@Resource
private TestWorker testWorker;
```
For every method call, either a separate transaction should be opened or the preexisting transactional context should be inherited:
```java
testWorker.doSomething();
testWorker.doSomethingElse();
```
To achieve this, two things need to be done: firstly, one has to implement an _around advice_ in order to wrap every method invocation in a `Work` instance and execute it with `ofy().transact()`:
```java
public class AppEngineTransactionManager {
public Object transact(final ProceedingJoinPoint joinpoint) throws Throwable {
try {
return ofy().transact(() -> {
try {
/** The methods will be executed in the transact() method*/
return joinpoint.proceed();
} catch (Throwable t) {
/** Don't forget to wrap checked exceptions that go through the Work interface */
throw new ExceptionWrapper(t);
}
});
} catch (ExceptionWrapper e) {
/** Don't forget to unwrap checked exceptions that went through the Work interface */
throw e.getCause();
} catch (Throwable t) {
throw new RuntimeException("Unexpected exception during transaction management", t);
}
}
private static class ExceptionWrapper extends RuntimeException {
...
}
}
```
Secondly, one has to declare the _aspect_ with the _around advice_ in the Spring configuration:
```java
```
A working prototype can be found here:
https://github.com/dajudge/appengine-objectify-spring-transactions-example