diff --git a/src/main/java/com/avaje/ebean/Ebean.java b/src/main/java/com/avaje/ebean/Ebean.java index 8acf3f719..f06638c9b 100644 --- a/src/main/java/com/avaje/ebean/Ebean.java +++ b/src/main/java/com/avaje/ebean/Ebean.java @@ -281,7 +281,7 @@ public final class Ebean { * Register the server with this Ebean singleton. Specify if the registered * server is the primary/default server. */ - protected static void register(EbeanServer server, boolean isPrimaryServer) { + public static void register(EbeanServer server, boolean isPrimaryServer) { serverMgr.register(server, isPrimaryServer); } @@ -372,6 +372,19 @@ public final class Ebean { return serverMgr.getPrimaryServer().currentTransaction(); } + /** + * Register a TransactionCallback on the currently active transaction. + *
+ * If there is no currently active transaction then a PersistenceException is thrown. + * + * @param transactionCallback The transaction callback to be registered with the current transaction. + * + * @throws PersistenceException If there is no currently active transaction + */ + public static void register(TransactionCallback transactionCallback) throws PersistenceException { + serverMgr.getPrimaryServer().register(transactionCallback); + } + /** * Commit the current transaction. */ diff --git a/src/main/java/com/avaje/ebean/EbeanServer.java b/src/main/java/com/avaje/ebean/EbeanServer.java index e730a4b35..45f4bdc18 100644 --- a/src/main/java/com/avaje/ebean/EbeanServer.java +++ b/src/main/java/com/avaje/ebean/EbeanServer.java @@ -7,6 +7,7 @@ import java.util.Map; import java.util.Set; import javax.persistence.OptimisticLockException; +import javax.persistence.PersistenceException; import com.avaje.ebean.annotation.CacheStrategy; import com.avaje.ebean.cache.ServerCacheManager; @@ -307,6 +308,17 @@ public interface EbeanServer { */ public SqlUpdate createNamedSqlUpdate(String namedQuery); + /** + * Register a TransactionCallback on the currently active transaction. + * + * If there is no currently active transaction then a PersistenceException is thrown. + * + * @param transactionCallback The transaction callback to be registered with the current transaction. + * + * @throws PersistenceException If there is no currently active transaction + */ + public void register(TransactionCallback transactionCallback) throws PersistenceException; + /** * Create a new transaction that is not held in TransactionThreadLocal. *diff --git a/src/main/java/com/avaje/ebean/Transaction.java b/src/main/java/com/avaje/ebean/Transaction.java index 331cd8f1f..1621352df 100644 --- a/src/main/java/com/avaje/ebean/Transaction.java +++ b/src/main/java/com/avaje/ebean/Transaction.java @@ -35,6 +35,11 @@ public interface Transaction extends Closeable { */ public static final int SERIALIZABLE = java.sql.Connection.TRANSACTION_SERIALIZABLE; + /** + * Register a TransactionCallback with this transaction. + */ + public void register(TransactionCallback callback); + /** * Return true if this transaction is read only. */ diff --git a/src/main/java/com/avaje/ebean/TransactionCallback.java b/src/main/java/com/avaje/ebean/TransactionCallback.java new file mode 100644 index 000000000..449cd33a7 --- /dev/null +++ b/src/main/java/com/avaje/ebean/TransactionCallback.java @@ -0,0 +1,34 @@ +package com.avaje.ebean; + +/** + * Provides a callback that can be registered with a Transaction. + *
+ * The callback methods are called just prior to and after the transaction performs a commit or rollback. + * + * A typical use of TransactionCallback would be to clean up non-transactional resources like files. For example, + * when processing files on postCommit/postRollback clean up the associated files. As another example when + * on postCommit of a delete remove associated resources from the file system or remote service. + */ +public interface TransactionCallback { + + /** + * Perform processing just prior to the transaction commit. + */ + void preCommit(); + + /** + * Perform processing just after the transaction commit. + */ + void postCommit(); + + /** + * Perform processing just prior to the transaction rollback. + */ + void preRollback(); + + /** + * Perform processing just after the transaction rollback. + */ + void postRollback(); + +} diff --git a/src/main/java/com/avaje/ebean/TransactionCallbackAdapter.java b/src/main/java/com/avaje/ebean/TransactionCallbackAdapter.java new file mode 100644 index 000000000..6ba299e31 --- /dev/null +++ b/src/main/java/com/avaje/ebean/TransactionCallbackAdapter.java @@ -0,0 +1,42 @@ +package com.avaje.ebean; + +/** + * Adapter that can be extended for easier implementation of TransactionCallback. + * + * Provides 'no operation' implementation for each of the TransactionCallback methods. It is expected that this + * class is extended and override the methods you need to. + */ +public abstract class TransactionCallbackAdapter implements TransactionCallback { + + /** + * Perform processing just prior to the transaction commit. + */ + @Override + public void preCommit() { + // do nothing - override as necessary + } + + /** + * Perform processing just after the transaction commit. + */ + @Override + public void postCommit() { + // do nothing - override as necessary + } + + /** + * Perform processing just prior to the transaction rollback. + */ + @Override + public void preRollback() { + // do nothing - override as necessary + } + + /** + * Perform processing just after the transaction rollback. + */ + @Override + public void postRollback() { + // do nothing - override as necessary + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiTransaction.java b/src/main/java/com/avaje/ebeaninternal/api/SpiTransaction.java index f46215278..601d1b4d9 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/SpiTransaction.java +++ b/src/main/java/com/avaje/ebeaninternal/api/SpiTransaction.java @@ -19,179 +19,179 @@ public interface SpiTransaction extends Transaction { * End the transaction when had query only use. */ public void endQueryOnly(); - + /** * Return the string prefix with the transactin id and label used in logging. */ public String getLogPrefix(); - - /** - * Return true if generated SQL and Bind values should be logged to the - * transaction log. - */ - public boolean isLogSql(); - /** - * Return true if summary level events should be logged to the transaction - * log. - */ - public boolean isLogSummary(); + /** + * Return true if generated SQL and Bind values should be logged to the + * transaction log. + */ + public boolean isLogSql(); - /** - * Log a message to the SQL logger. - */ - public void logSql(String msg); + /** + * Return true if summary level events should be logged to the transaction + * log. + */ + public boolean isLogSummary(); - /** - * Log a message to the SUMMARY logger. - */ - public void logSummary(String msg); + /** + * Log a message to the SQL logger. + */ + public void logSql(String msg); - /** - * Register a "Derived Relationship" (that requires an additional update). - */ - public void registerDerivedRelationship(DerivedRelationshipData assocBean); + /** + * Log a message to the SUMMARY logger. + */ + public void logSummary(String msg); - /** - * Return the list of "Derived Relationships" that must be maintained after - * insert. - */ - public List- * This is to handle bi-directional relationships where both sides Cascade. - *
- */ - public void registerDeleteBean(Integer hash); + /** + * Return the list of "Derived Relationships" that must be maintained after + * insert. + */ + public List+ * This is to handle bi-directional relationships where both sides Cascade. + *
+ */ + public void registerDeleteBean(Integer hash); - /** - * Return true if this is a bean that has already been saved/deleted. - */ - public boolean isRegisteredDeleteBean(Integer hash); + /** + * Unregister the hash of the bean. + */ + public void unregisterDeleteBean(Integer hash); - /** - * Unregister the persisted bean. - */ - public void unregisterBean(Object bean); + /** + * Return true if this is a bean that has already been saved/deleted. + */ + public boolean isRegisteredDeleteBean(Integer hash); - /** - * Return true if this is a bean that has already been persisted in the - * current recursive save request. The goal is to stop recursively saving - * the bean when cascade persist is on both sides of a relationship). - *- * This will register the bean if it is not already. - *
- */ - public boolean isRegisteredBean(Object bean); + /** + * Unregister the persisted bean. + */ + public void unregisterBean(Object bean); - /** - * Returns a String used to identify the transaction. This id is used for - * Transaction logging. - */ - public String getId(); + /** + * Return true if this is a bean that has already been persisted in the + * current recursive save request. The goal is to stop recursively saving + * the bean when cascade persist is on both sides of a relationship). + *+ * This will register the bean if it is not already. + *
+ */ + public boolean isRegisteredBean(Object bean); - /** - * Return the batchSize specifically set for this transaction or 0. - *- * Returning 0 implies to use the system wide default batch size. - *
- */ - public int getBatchSize(); + /** + * Returns a String used to identify the transaction. This id is used for + * Transaction logging. + */ + public String getId(); - /** - * Modify and return the current 'depth' of the transaction. - *- * As we cascade save or delete we traverse the object graph tree. Going up - * to Assoc Ones the depth decreases and going down to Assoc Manys the depth - * increases. - *
- *- * The depth is used for ordering batching statements. The lowest depth get - * executed first during save. - *
- */ - public int depth(int diff); + /** + * Return the batchSize specifically set for this transaction or 0. + *+ * Returning 0 implies to use the system wide default batch size. + *
+ */ + public int getBatchSize(); - /** - * Return true if this transaction was created explicitly via - *Ebean.beginTransaction().
- */
- public boolean isExplicit();
+ /**
+ * Modify and return the current 'depth' of the transaction.
+ * + * As we cascade save or delete we traverse the object graph tree. Going up + * to Assoc Ones the depth decreases and going down to Assoc Manys the depth + * increases. + *
+ *+ * The depth is used for ordering batching statements. The lowest depth get + * executed first during save. + *
+ */ + public int depth(int diff); - /** - * Get the object that holds the event details. - *- * This information is used maintain the table state, cache and text - * indexes. On commit the Table modifications this generates is broadcast - * around the cluster (if you have a cluster). - *
- */ - public TransactionEvent getEvent(); + /** + * Return true if this transaction was created explicitly via + *Ebean.beginTransaction().
+ */
+ public boolean isExplicit();
- /**
- * Whether persistCascade is on for save and delete.
- */
- public boolean isPersistCascade();
+ /**
+ * Get the object that holds the event details.
+ * + * This information is used maintain the table state, cache and text + * indexes. On commit the Table modifications this generates is broadcast + * around the cluster (if you have a cluster). + *
+ */ + public TransactionEvent getEvent(); - /** - * Return true if this request should be batched. Conversely returns false - * if this request should be executed immediately. - */ - public boolean isBatchThisRequest(); + /** + * Whether persistCascade is on for save and delete. + */ + public boolean isPersistCascade(); - /** - * Return the queue used to batch up persist requests. - */ - public BatchControl getBatchControl(); + /** + * Return true if this request should be batched. Conversely returns false + * if this request should be executed immediately. + */ + public boolean isBatchThisRequest(); - /** - * Set the queue used to batch up persist requests. There should only be one - * PersistQueue set per transaction. - */ - public void setBatchControl(BatchControl control); + /** + * Return the queue used to batch up persist requests. + */ + public BatchControl getBatchControl(); - /** - * Return the persistence context associated with this transaction. - *- * You may wish to hold onto this and set it against another transaction - * later. This is along the lines of 'extended persistence context' - * behaviour. - *
- */ - public PersistenceContext getPersistenceContext(); + /** + * Set the queue used to batch up persist requests. There should only be one + * PersistQueue set per transaction. + */ + public void setBatchControl(BatchControl control); - /** - * Set the persistence context to this transaction. - *- * This could be considered similar to 'EJB3 Extended Persistence Context'. - * In that you can get the PersistenceContext from a transaction, hold onto - * it, and then set it back later to a second transaction. In general there - * is one PersistenceContext per Transaction. The getPersistenceContext() - * and setPersistenceContext() enable a developer to reuse a single - * PersistenceContext with multiple transactions. - *
- */ - public void setPersistenceContext(PersistenceContext context); + /** + * Return the persistence context associated with this transaction. + *+ * You may wish to hold onto this and set it against another transaction + * later. This is along the lines of 'extended persistence context' + * behaviour. + *
+ */ + public PersistenceContext getPersistenceContext(); - /** - * Return the underlying Connection for internal use. - *- * If the connection is made public from Transaction and the user code calls - * that method we can no longer trust the query only status of a - * Transaction. - *
- */ - public Connection getInternalConnection(); + /** + * Set the persistence context to this transaction. + *+ * This could be considered similar to 'EJB3 Extended Persistence Context'. + * In that you can get the PersistenceContext from a transaction, hold onto + * it, and then set it back later to a second transaction. In general there + * is one PersistenceContext per Transaction. The getPersistenceContext() + * and setPersistenceContext() enable a developer to reuse a single + * PersistenceContext with multiple transactions. + *
+ */ + public void setPersistenceContext(PersistenceContext context); - /** - * Return true if the manyToMany intersection should be persisted for this particular relationship direction. - */ + /** + * Return the underlying Connection for internal use. + *+ * If the connection is made public from Transaction and the user code calls + * that method we can no longer trust the query only status of a + * Transaction. + *
+ */ + public Connection getInternalConnection(); + + /** + * Return true if the manyToMany intersection should be persisted for this particular relationship direction. + */ public boolean isSaveAssocManyIntersection(String intersectionTable, String beanName); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java index 81431d375..635797e6d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java @@ -653,6 +653,15 @@ public final class DefaultServer implements SpiEbeanServer { return (T) ref; } + @Override + public void register(TransactionCallback transactionCallback) { + Transaction transaction = currentTransaction(); + if (transaction == null) { + throw new PersistenceException("Not currently active transaction when trying to register transactionCallback"); + } + transaction.register(transactionCallback); + } + /** * Creates a new Transaction that is NOT stored in TransactionThreadLocal. Use * this when you want a thread to have a second independent transaction. diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/JdbcTransaction.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/JdbcTransaction.java index 0a3705e77..a06ce88f3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/JdbcTransaction.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/JdbcTransaction.java @@ -13,6 +13,7 @@ import java.util.Map; import javax.persistence.PersistenceException; import javax.persistence.RollbackException; +import com.avaje.ebean.TransactionCallback; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -129,6 +130,8 @@ public class JdbcTransaction implements SpiTransaction { protected Map