diff --git a/CHANGELOG.md b/CHANGELOG.md
index 136fcd6e108843bcb079bc15fbf5a4ed529657ab..24af3d81242defb0f33227c5ba9e614f4131217d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,19 @@
 # Ferma Changelog
 
-## 3.0.4
+## 3.1.0
+
+* Added abstraction code for tinkerpop transactions which ease usage of transactions within ferma.
+  The new code provides functional interfaces for transactions and methods which allow 
+  access to transactions from nested functions without the need to pass the original transaction object along.
+  Once a transaction has been opened it can be accessed from anywhere within the same thread 
+  via the `Tx.getActive()` method.
+
+  The `TxFactoryTest` class and the [ferma-orientdb extension](https://github.com/syncleus/ferma-orientdb) 
+  contain examples how these classes and methods can be used.
+
+* Updated the following dependencies
+  * tinkergraph-gremlin: 3.2.4 -> 3.2.5
+
 
 ## 3.0.3
 
diff --git a/pom.xml b/pom.xml
index 7e4092255909c36a176faa193bfe00bd2d2826c2..03d1646bc2bd73b7856f5b1ad15f17495e612992 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,7 +12,7 @@
     <groupId>com.syncleus.ferma</groupId>
     <artifactId>ferma</artifactId>
     <packaging>jar</packaging>
-    <version>3.0.4-SNAPSHOT</version>
+    <version>3.1.0-SNAPSHOT</version>
 
     <name>Ferma</name>
     <description>An ORM for the Tinkerpop3 graph stack.</description>
@@ -158,12 +158,12 @@
         <dependency>
             <groupId>org.apache.tinkerpop</groupId>
             <artifactId>gremlin-core</artifactId>
-            <version>3.2.4</version>
+            <version>3.2.5</version>
         </dependency>
         <dependency>
             <groupId>org.apache.tinkerpop</groupId>
             <artifactId>tinkergraph-gremlin</artifactId>
-            <version>3.2.4</version>
+            <version>3.2.5</version>
             <scope>test</scope>
         </dependency>
         <dependency>
diff --git a/src/main/java/com/syncleus/ferma/AbstractVertexFrame.java b/src/main/java/com/syncleus/ferma/AbstractVertexFrame.java
index 3b2c1ca1af53a3a4a2818ad5fcdb881a7df6d87f..f00568b74187633346ea3294ef73082b1a464959 100644
--- a/src/main/java/com/syncleus/ferma/AbstractVertexFrame.java
+++ b/src/main/java/com/syncleus/ferma/AbstractVertexFrame.java
@@ -28,10 +28,8 @@ import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import com.google.gson.JsonObject;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
-import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
 import org.apache.tinkerpop.gremlin.structure.*;
 import org.apache.tinkerpop.gremlin.structure.util.wrapped.WrappedElement;
-import javax.annotation.Nullable;
 import java.util.Iterator;
 import java.util.function.Consumer;
 
diff --git a/src/main/java/com/syncleus/ferma/DelegatingFramedGraph.java b/src/main/java/com/syncleus/ferma/DelegatingFramedGraph.java
index d9a4d5544fc9aea01e1228456a26cef308fd390a..8a12aec59d5a712875897089a9558cedb21e450f 100644
--- a/src/main/java/com/syncleus/ferma/DelegatingFramedGraph.java
+++ b/src/main/java/com/syncleus/ferma/DelegatingFramedGraph.java
@@ -36,7 +36,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSo
 import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.Element;
 import org.apache.tinkerpop.gremlin.structure.Graph;
-import javax.annotation.Nullable;
 import java.io.IOException;
 import java.util.Collection;
 import java.util.Iterator;
diff --git a/src/main/java/com/syncleus/ferma/DelegatingTransaction.java b/src/main/java/com/syncleus/ferma/DelegatingTransaction.java
index b998c07b58a2d0b20333da8fd6cc41166f2772b0..b1254852613b0f75cb286b6ba5643c56db46fa7e 100644
--- a/src/main/java/com/syncleus/ferma/DelegatingTransaction.java
+++ b/src/main/java/com/syncleus/ferma/DelegatingTransaction.java
@@ -31,51 +31,62 @@ public class DelegatingTransaction implements WrappedTransaction {
 
     @Override
     public void open() {
-        this.delegate.open();
+        this.getDelegate().open();
     }
 
     @Override
     public void commit() {
-        this.delegate.commit();
+        this.getDelegate().commit();
     }
 
     @Override
     public void rollback() {
-        this.delegate.rollback();
+        this.getDelegate().rollback();
     }
 
     @Override
     public WrappedFramedGraph<? extends Graph> createThreadedTx() {
-        return new DelegatingFramedGraph<>(this.delegate.createThreadedTx(), this.parentGraph.getBuilder(), this.parentGraph.getTypeResolver());
+        return new DelegatingFramedGraph<>(this.getDelegate().createThreadedTx(), this.getGraph().getBuilder(), this.getGraph().getTypeResolver());
     }
 
     @Override
     public boolean isOpen() {
-        return this.delegate.isOpen();
+        return this.getDelegate().isOpen();
     }
 
     @Override
     public void readWrite() {
-        this.delegate.readWrite();
+        this.getDelegate().readWrite();
     }
 
     @Override
     public void close() {
-        this.delegate.close();
+        this.getDelegate().close();
     }
 
     @Override
     public void addTransactionListener(final Consumer<Transaction.Status> listener) {
-        this.delegate.addTransactionListener(listener);
+        this.getDelegate().addTransactionListener(listener);
     }
 
     @Override
     public void removeTransactionListener(final Consumer<Transaction.Status> listener) {
-        this.delegate.removeTransactionListener(listener);
+        this.getDelegate().removeTransactionListener(listener);
     }
 
     @Override
     public void clearTransactionListeners() {
-        this.delegate.clearTransactionListeners();
+        this.getDelegate().clearTransactionListeners();
     }
+
+    @Override
+    public Transaction getDelegate() {
+        return delegate;
+    }
+
+    @Override
+    public WrappedFramedGraph<? extends Graph> getGraph() {
+        return parentGraph;
+    }
+
 }
diff --git a/src/main/java/com/syncleus/ferma/WrappedTransaction.java b/src/main/java/com/syncleus/ferma/WrappedTransaction.java
index 58f7087f4f7f5108a72029e3acabe5522616d570..adc7794071abd64d041b77b15939a59cd1def9c1 100644
--- a/src/main/java/com/syncleus/ferma/WrappedTransaction.java
+++ b/src/main/java/com/syncleus/ferma/WrappedTransaction.java
@@ -15,9 +15,11 @@
  */
 package com.syncleus.ferma;
 
+import java.util.function.Consumer;
+
 import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Transaction;
 import org.apache.tinkerpop.gremlin.structure.util.AbstractTransaction;
-import java.util.function.Consumer;
 
 /**
  * A set of methods that allow for control of transactional behavior of a {@link WrappedFramedGraph} instance. Providers may
@@ -95,4 +97,16 @@ public interface WrappedTransaction extends AutoCloseable {
      * Removes all transaction listeners.
      */
     public void clearTransactionListeners();
+
+    /**
+     * Returns the raw wrapped tinkerpop transaction.
+     * @return
+     */
+     Transaction getDelegate();
+
+     /**
+      * Returns the parent graph for the transaction.
+      * @return
+      */
+     WrappedFramedGraph<? extends Graph> getGraph();
 }
diff --git a/src/main/java/com/syncleus/ferma/annotations/GraphElement.java b/src/main/java/com/syncleus/ferma/annotations/GraphElement.java
new file mode 100644
index 0000000000000000000000000000000000000000..fa9e20a7911c2f2648ae27d338f6b28b05b95bed
--- /dev/null
+++ b/src/main/java/com/syncleus/ferma/annotations/GraphElement.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright 2004 - 2016 Syncleus, Inc.
+ *
+ * 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 com.syncleus.ferma.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation which is used to identify classes which represent graph elements.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface GraphElement {
+
+}
diff --git a/src/main/java/com/syncleus/ferma/tx/AbstractTx.java b/src/main/java/com/syncleus/ferma/tx/AbstractTx.java
new file mode 100644
index 0000000000000000000000000000000000000000..e58f3e3fdc33c801e9ae4264c9005233bfecfb30
--- /dev/null
+++ b/src/main/java/com/syncleus/ferma/tx/AbstractTx.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright 2004 - 2016 Syncleus, Inc.
+ *
+ * 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 com.syncleus.ferma.tx;
+
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Transaction;
+
+import com.syncleus.ferma.DelegatingTransaction;
+import com.syncleus.ferma.WrappedFramedGraph;
+
+/**
+ * An abstract class that can be used to implement vendor specific graph database Tx classes.
+ */
+public abstract class AbstractTx<G extends FramedTxGraph> extends DelegatingTransaction implements Tx {
+
+    private boolean isSuccess = false;
+
+    public AbstractTx(Transaction delegate, WrappedFramedGraph<? extends Graph> parentGraph) {
+       super(delegate, parentGraph);
+    }
+
+    @Override
+    public void success() {
+        isSuccess = true;
+    }
+
+    @Override
+    public void failure() {
+        isSuccess = false;
+    }
+
+    /**
+     * Return the state of the success status flag.
+     * 
+     * @return
+     */
+    protected boolean isSuccess() {
+        return isSuccess;
+    }
+
+    @Override
+    public void close() {
+        Tx.setActive(null);
+        if (isSuccess()) {
+            commit();
+        } else {
+            rollback();
+        }
+        getDelegate().close();
+    }
+
+}
diff --git a/src/main/java/com/syncleus/ferma/tx/FramedTxGraph.java b/src/main/java/com/syncleus/ferma/tx/FramedTxGraph.java
new file mode 100644
index 0000000000000000000000000000000000000000..5a327b52f357f9b8c19e1fcf6d6fa82bf85f394a
--- /dev/null
+++ b/src/main/java/com/syncleus/ferma/tx/FramedTxGraph.java
@@ -0,0 +1,34 @@
+package com.syncleus.ferma.tx;
+
+import com.syncleus.ferma.FramedGraph;
+import com.syncleus.ferma.WrappedTransaction;
+
+/**
+ * Adapted flavor of the {@link FramedGraph}. This interface will return a {@link Tx} instead of a {@link WrappedTransaction}. The {@link Tx} interface contains
+ * some useful methods which makes it easier is some cases to work with transactions. This includes automatic rollback within the autoclosable and transaction
+ * reference handling.
+ */
+public interface FramedTxGraph extends FramedGraph {
+
+    /**
+     * Return an active transaction or create a new transaction if no active could be found.
+     */
+    @Override
+    default Tx tx() {
+        if (Tx.getActive() != null) {
+            return Tx.getActive();
+        } else {
+            Tx tx = createTx();
+            Tx.setActive(tx);
+            return tx; 
+        }
+    }
+
+    /**
+     * Create a new transaction.
+     * 
+     * @return
+     */
+    Tx createTx();
+
+}
diff --git a/src/main/java/com/syncleus/ferma/tx/Tx.java b/src/main/java/com/syncleus/ferma/tx/Tx.java
new file mode 100644
index 0000000000000000000000000000000000000000..acbcff73caac3e069ace84481ecbdb6157835caf
--- /dev/null
+++ b/src/main/java/com/syncleus/ferma/tx/Tx.java
@@ -0,0 +1,86 @@
+/**
+ * Copyright 2004 - 2016 Syncleus, Inc.
+ *
+ * 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 com.syncleus.ferma.tx;
+
+import java.io.IOException;
+
+import com.syncleus.ferma.WrappedTransaction;
+
+/**
+ * A {@link Tx} is an extended flavor of the {@link WrappedTransaction}. This interface provides methods to store and retrieve transaction references. This is
+ * useful if you want to access a running transaction without passing along the transaction object in your implementation. Additionally this interface provides
+ * the {@link #success()} and {@link #failure()} methods which can be used within the {@link #close()} method in order to rollback or commit the transaction
+ * according to the state of the flag which is set using these methods. The {@link AbstractTx} class contains an abstract implementation of this mechanism.
+ */
+public interface Tx extends WrappedTransaction {
+
+    /**
+     * Thread local that is used to store references to the used graph.
+     */
+    public static ThreadLocal<Tx> threadLocalGraph = new ThreadLocal<>();
+
+    /**
+     * Set the nested active transaction for the current thread.
+     * 
+     * @param tx
+     *            Transaction
+     */
+    public static void setActive(Tx tx) {
+        Tx.threadLocalGraph.set(tx);
+    }
+
+    /**
+     * Return the current active graph. A transaction should be the only place where this threadlocal is updated.
+     * 
+     * @return Currently active transaction
+     */
+    public static Tx getActive() {
+        return Tx.threadLocalGraph.get();
+    }
+
+    /**
+     * Mark the transaction as succeeded. The autoclosable will invoke a commit when completing.
+     */
+    void success();
+
+    /**
+     * Mark the transaction as failed. The autoclosable will invoke a rollback when completing.
+     */
+    void failure();
+
+    /**
+     * Invoke rollback or commit when closing the autoclosable. By default a rollback will be invoked.
+     * 
+     * @throws IOException
+     */
+    @Override
+    void close();
+
+    /**
+     * Add new isolated vertex to the graph.
+     * 
+     * @param <T>
+     *            The type used to frame the element.
+     * @param kind
+     *            The kind of the vertex
+     * @return The framed vertex
+     * 
+     */
+    default <T> T addVertex(Class<T> kind) {
+        return getGraph().addFramedVertex(kind);
+    }
+
+}
diff --git a/src/main/java/com/syncleus/ferma/tx/TxAction.java b/src/main/java/com/syncleus/ferma/tx/TxAction.java
new file mode 100644
index 0000000000000000000000000000000000000000..f946e58e422dab1003fcf12b86dc26ec0ead875e
--- /dev/null
+++ b/src/main/java/com/syncleus/ferma/tx/TxAction.java
@@ -0,0 +1,8 @@
+package com.syncleus.ferma.tx;
+
+@FunctionalInterface
+public interface TxAction<T> {
+
+    T handle(Tx tx) throws Exception;
+
+}
diff --git a/src/main/java/com/syncleus/ferma/tx/TxAction0.java b/src/main/java/com/syncleus/ferma/tx/TxAction0.java
new file mode 100644
index 0000000000000000000000000000000000000000..e354e99043e4159d34b1ada34ce2cbe6b07d4f82
--- /dev/null
+++ b/src/main/java/com/syncleus/ferma/tx/TxAction0.java
@@ -0,0 +1,8 @@
+package com.syncleus.ferma.tx;
+
+@FunctionalInterface
+public interface TxAction0 {
+
+    void handle() throws Exception;
+
+}
diff --git a/src/main/java/com/syncleus/ferma/tx/TxAction1.java b/src/main/java/com/syncleus/ferma/tx/TxAction1.java
new file mode 100644
index 0000000000000000000000000000000000000000..75efc341f931487c4a29f71ea244ca387c11e760
--- /dev/null
+++ b/src/main/java/com/syncleus/ferma/tx/TxAction1.java
@@ -0,0 +1,8 @@
+package com.syncleus.ferma.tx;
+
+@FunctionalInterface
+public interface TxAction1<T> {
+
+    T handle() throws Exception;
+
+}
diff --git a/src/main/java/com/syncleus/ferma/tx/TxAction2.java b/src/main/java/com/syncleus/ferma/tx/TxAction2.java
new file mode 100644
index 0000000000000000000000000000000000000000..c2afdf41031d0115fd4146eada3acff7505f47a5
--- /dev/null
+++ b/src/main/java/com/syncleus/ferma/tx/TxAction2.java
@@ -0,0 +1,8 @@
+package com.syncleus.ferma.tx;
+
+@FunctionalInterface
+public interface TxAction2 {
+
+    void handle(Tx tx) throws Exception;
+
+}
diff --git a/src/main/java/com/syncleus/ferma/tx/TxFactory.java b/src/main/java/com/syncleus/ferma/tx/TxFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..b93809d027508312d27c6b0b0c9fc19a1bd604f6
--- /dev/null
+++ b/src/main/java/com/syncleus/ferma/tx/TxFactory.java
@@ -0,0 +1,104 @@
+/**
+ * Copyright 2004 - 2016 Syncleus, Inc.
+ *
+ * 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 com.syncleus.ferma.tx;
+
+/**
+ * Interface which can be used for custom transaction factories in 
+ * order to provide various ways of executing transaction handlers.
+ */
+public interface TxFactory {
+
+    /**
+     * Return a new autoclosable transaction handler. This object should be used within a try-with-resource block.
+     * Return an active transaction or create a new transaction if no active could be found.
+     * 
+     * <pre>
+     * {
+     * 	&#64;code
+     * 	try(Tx tx = db.tx()) {
+     * 	  // interact with graph db here
+     *  }
+     * }
+     * </pre>
+     * 
+     * @return Created transaction
+     */
+    default Tx tx() {
+        if (Tx.getActive() != null) {
+            return Tx.getActive();
+        } else {
+            Tx tx = createTx();
+            Tx.setActive(tx);
+            return tx; 
+        }
+    }
+
+    /**
+     * Create a new transaction.
+     * 
+     * @return
+     */
+    Tx createTx();
+
+    /**
+     * Execute the txHandler within the scope of a transaction and call 
+     * the result handler once the transaction handler code has finished.
+     * 
+     * @param txHandler
+     *            Handler that will be executed within the scope of the transaction.
+     * @return Object which was returned by the handler
+     */
+    <T> T tx(TxAction<T> txHandler);
+
+    /**
+     * Execute the txHandler within the scope of a transaction.
+     * 
+     * @param txHandler
+     *            Handler that will be executed within the scope of the transaction.
+     */
+    default void tx(TxAction0 txHandler) {
+        tx((tx) -> {
+            txHandler.handle();
+        });
+    }
+
+    /**
+     * Execute the txHandler within the scope of a transaction.
+     * 
+     * @param txHandler
+     *            Handler that will be executed within the scope of the transaction.
+     * @return Result of the handler
+     */
+    default <T> T tx(TxAction1<T> txHandler) {
+        return tx((tx) -> {
+            return txHandler.handle();
+        });
+    }
+
+    /**
+     * Execute the txHandler within the scope of a transaction.
+     * 
+     * @param txHandler
+     *            Handler that will be executed within the scope of the transaction.
+     */
+    default void tx(TxAction2 txHandler) {
+        tx((tx) -> {
+            txHandler.handle(tx);
+            return null;
+        });
+    }
+
+}
diff --git a/src/main/java/com/syncleus/ferma/tx/WrappedFramedTxGraph.java b/src/main/java/com/syncleus/ferma/tx/WrappedFramedTxGraph.java
new file mode 100644
index 0000000000000000000000000000000000000000..384d01e91842296392b2613d78aaba1593ff59b0
--- /dev/null
+++ b/src/main/java/com/syncleus/ferma/tx/WrappedFramedTxGraph.java
@@ -0,0 +1,9 @@
+package com.syncleus.ferma.tx;
+
+import org.apache.tinkerpop.gremlin.structure.Graph;
+
+import com.syncleus.ferma.WrappedFramedGraph;
+
+public interface WrappedFramedTxGraph<G extends Graph> extends WrappedFramedGraph<G>, FramedTxGraph {
+
+}
diff --git a/src/test/java/com/syncleus/ferma/tx/DummyGraph.java b/src/test/java/com/syncleus/ferma/tx/DummyGraph.java
new file mode 100644
index 0000000000000000000000000000000000000000..a14e3ea6bd688cc1e543c7748f854c366cd7cbfc
--- /dev/null
+++ b/src/test/java/com/syncleus/ferma/tx/DummyGraph.java
@@ -0,0 +1,22 @@
+package com.syncleus.ferma.tx;
+
+import org.apache.tinkerpop.gremlin.structure.Graph;
+
+import com.syncleus.ferma.DelegatingFramedGraph;
+
+public class DummyGraph extends DelegatingFramedGraph<Graph> implements FramedTxGraph {
+
+    public DummyGraph(Graph delegate) {
+        super(delegate);
+    }
+
+    @Override
+    public Tx tx() {
+        return FramedTxGraph.super.tx();
+    }
+
+    @Override
+    public Tx createTx() {
+        return new DummyTransaction(null, this);
+    }
+}
diff --git a/src/test/java/com/syncleus/ferma/tx/DummyTransaction.java b/src/test/java/com/syncleus/ferma/tx/DummyTransaction.java
new file mode 100644
index 0000000000000000000000000000000000000000..4924d0be4a8d70f2ef54c427d814caabb4a98a00
--- /dev/null
+++ b/src/test/java/com/syncleus/ferma/tx/DummyTransaction.java
@@ -0,0 +1,14 @@
+package com.syncleus.ferma.tx;
+
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Transaction;
+
+import com.syncleus.ferma.WrappedFramedGraph;
+
+public class DummyTransaction extends AbstractTx<FramedTxGraph>{
+
+    public DummyTransaction(Transaction delegate, WrappedFramedGraph<? extends Graph> parentGraph) {
+        super(delegate, parentGraph);
+    }
+
+}
diff --git a/src/test/java/com/syncleus/ferma/tx/TxFactoryTest.java b/src/test/java/com/syncleus/ferma/tx/TxFactoryTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d79f46114c3bec3446005017ab46f290b4517588
--- /dev/null
+++ b/src/test/java/com/syncleus/ferma/tx/TxFactoryTest.java
@@ -0,0 +1,127 @@
+package com.syncleus.ferma.tx;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.verify;
+
+import org.apache.tinkerpop.gremlin.structure.Transaction;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class TxFactoryTest implements TxFactory {
+
+    private DummyTransaction mock = Mockito.mock(DummyTransaction.class, Mockito.CALLS_REAL_METHODS);
+
+    @Before
+    public void setupMocks() {
+        Transaction rawTx = Mockito.mock(Transaction.class);
+        Mockito.when(mock.getDelegate()).thenReturn(rawTx);
+    }
+
+    @Test
+    public void testTx0() {
+        try (Tx tx = tx()) {
+
+        }
+        verify(mock).close();
+    }
+
+    @Test
+    public void testTx1() {
+        tx(() -> {
+
+        });
+        verify(mock).close();
+    }
+
+    @Test
+    public void testTx2() {
+        assertEquals("test", tx(() -> {
+            return "test";
+        }));
+        verify(mock).close();
+    }
+
+    @Test
+    public void testTx3() {
+        assertEquals("test", tx((tx) -> {
+            tx.failure();
+            tx.success();
+            return "test";
+        }));
+        verify(mock).close();
+    }
+
+    @Test
+    public void testAbstractTxSucceeding() {
+        DummyTransaction tx = Mockito.mock(DummyTransaction.class, Mockito.CALLS_REAL_METHODS);
+        Transaction rawTx = Mockito.mock(Transaction.class);
+        Mockito.when(tx.getDelegate()).thenReturn(rawTx);
+        DummyGraph graphMock = Mockito.mock(DummyGraph.class, Mockito.CALLS_REAL_METHODS);
+        Mockito.when(graphMock.createTx()).thenReturn(tx);
+
+        try (Tx tx2 = graphMock.tx()) {
+            assertNotNull(Tx.getActive());
+            tx2.success();
+        }
+        assertNull(Tx.getActive());
+        verify(tx).commit();
+        verify(tx).close();
+        verify(tx, Mockito.never()).rollback();
+    }
+
+    @Test
+    public void testAbstractTxDefault() {
+        DummyTransaction tx = Mockito.mock(DummyTransaction.class, Mockito.CALLS_REAL_METHODS);
+        Transaction rawTx = Mockito.mock(Transaction.class);
+        Mockito.when(tx.getDelegate()).thenReturn(rawTx);
+        DummyGraph graphMock = Mockito.mock(DummyGraph.class, Mockito.CALLS_REAL_METHODS);
+        Mockito.when(graphMock.tx()).thenReturn(tx);
+        try (Tx tx2 = tx) {
+            assertNotNull(Tx.getActive());
+            // Don't call tx2.success() or tx2.failure()
+        }
+        assertNull(Tx.getActive());
+        verify(tx).close();
+        verify(graphMock.tx()).rollback();
+        verify(graphMock.tx()).close();
+        verify(graphMock.tx(), Mockito.never()).commit();
+    }
+
+    @Test
+    public void testAbstractTxFailing() {
+        DummyTransaction tx = Mockito.mock(DummyTransaction.class, Mockito.CALLS_REAL_METHODS);
+        Transaction rawTx = Mockito.mock(Transaction.class);
+        Mockito.when(tx.getDelegate()).thenReturn(rawTx);
+        DummyGraph graphMock = Mockito.mock(DummyGraph.class, Mockito.CALLS_REAL_METHODS);
+        Mockito.when(graphMock.tx()).thenReturn(tx);
+        try (Tx tx2 = tx) {
+            assertNotNull(Tx.getActive());
+            tx2.failure();
+        }
+        assertNull(Tx.getActive());
+        verify(tx).close();
+        verify(graphMock.tx()).rollback();
+        verify(graphMock.tx()).close();
+        verify(graphMock.tx(), Mockito.never()).commit();
+    }
+
+    @Override
+    public Tx createTx() {
+        return mock;
+    }
+
+    @Override
+    public <T> T tx(TxAction<T> txHandler) {
+        try (Tx tx = tx()) {
+            try {
+                return txHandler.handle(mock);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+}