#203: ENH: Add Transaction hook ... for preCommit(), postCommit(), postRollback()

This commit is contained in:
rbygrave
2014-11-22 12:14:21 +13:00
parent 6677a6199c
commit 65cb2ae94d
10 changed files with 413 additions and 152 deletions
@@ -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<DerivedRelationshipData> getDerivedRelationship(Object bean);
/**
* Register a "Derived Relationship" (that requires an additional update).
*/
public void registerDerivedRelationship(DerivedRelationshipData assocBean);
/**
* Add a deleting bean to the registered list.
* <p>
* This is to handle bi-directional relationships where both sides Cascade.
* </p>
*/
public void registerDeleteBean(Integer hash);
/**
* Return the list of "Derived Relationships" that must be maintained after
* insert.
*/
public List<DerivedRelationshipData> getDerivedRelationship(Object bean);
/**
* Unregister the hash of the bean.
*/
public void unregisterDeleteBean(Integer hash);
/**
* Add a deleting bean to the registered list.
* <p>
* This is to handle bi-directional relationships where both sides Cascade.
* </p>
*/
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).
* <p>
* This will register the bean if it is not already.
* </p>
*/
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).
* <p>
* This will register the bean if it is not already.
* </p>
*/
public boolean isRegisteredBean(Object bean);
/**
* Return the batchSize specifically set for this transaction or 0.
* <p>
* Returning 0 implies to use the system wide default batch size.
* </p>
*/
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.
* <p>
* 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.
* </p>
* <p>
* The depth is used for ordering batching statements. The lowest depth get
* executed first during save.
* </p>
*/
public int depth(int diff);
/**
* Return the batchSize specifically set for this transaction or 0.
* <p>
* Returning 0 implies to use the system wide default batch size.
* </p>
*/
public int getBatchSize();
/**
* Return true if this transaction was created explicitly via
* <code>Ebean.beginTransaction()</code>.
*/
public boolean isExplicit();
/**
* Modify and return the current 'depth' of the transaction.
* <p>
* 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.
* </p>
* <p>
* The depth is used for ordering batching statements. The lowest depth get
* executed first during save.
* </p>
*/
public int depth(int diff);
/**
* Get the object that holds the event details.
* <p>
* 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).
* </p>
*/
public TransactionEvent getEvent();
/**
* Return true if this transaction was created explicitly via
* <code>Ebean.beginTransaction()</code>.
*/
public boolean isExplicit();
/**
* Whether persistCascade is on for save and delete.
*/
public boolean isPersistCascade();
/**
* Get the object that holds the event details.
* <p>
* 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).
* </p>
*/
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.
* <p>
* You may wish to hold onto this and set it against another transaction
* later. This is along the lines of 'extended persistence context'
* behaviour.
* </p>
*/
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.
* <p>
* 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.
* </p>
*/
public void setPersistenceContext(PersistenceContext context);
/**
* Return the persistence context associated with this transaction.
* <p>
* You may wish to hold onto this and set it against another transaction
* later. This is along the lines of 'extended persistence context'
* behaviour.
* </p>
*/
public PersistenceContext getPersistenceContext();
/**
* Return the underlying Connection for internal use.
* <p>
* 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.
* </p>
*/
public Connection getInternalConnection();
/**
* Set the persistence context to this transaction.
* <p>
* 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.
* </p>
*/
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.
* <p>
* 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.
* </p>
*/
public Connection getInternalConnection();
/**
* Return true if the manyToMany intersection should be persisted for this particular relationship direction.
*/
public boolean isSaveAssocManyIntersection(String intersectionTable, String beanName);
}
@@ -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.
@@ -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<String, Object> userObjects;
protected List<TransactionCallback> callbackList;
/**
* Create a new JdbcTransaction.
*/
@@ -171,6 +174,63 @@ public class JdbcTransaction implements SpiTransaction {
return logPrefix;
}
@Override
public void register(TransactionCallback callback) {
if (callbackList == null) {
callbackList = new ArrayList<TransactionCallback>(4);
}
callbackList.add(callback);
}
protected void firePreRollback() {
if (callbackList != null) {
for (TransactionCallback callback : callbackList) {
try {
callback.preRollback();
} catch (Exception e) {
logger.error("Error executing preRollback callback", e);
}
}
}
}
protected void firePostRollback() {
if (callbackList != null) {
for (TransactionCallback callback : callbackList) {
try {
callback.postRollback();
} catch (Exception e) {
logger.error("Error executing postRollback callback", e);
}
}
}
}
protected void firePreCommit() {
if (callbackList != null) {
for (TransactionCallback callback : callbackList) {
try {
callback.preCommit();
} catch (Exception e) {
logger.error("Error executing preCommit callback", e);
}
}
}
}
protected void firePostCommit() {
if (callbackList != null) {
for (TransactionCallback callback : callbackList) {
try {
callback.postCommit();
} catch (Exception e) {
logger.error("Error executing postCommit callback", e);
}
}
}
}
public List<DerivedRelationshipData> getDerivedRelationship(Object bean) {
if (derivedRelMap == null) {
return null;
@@ -603,6 +663,9 @@ public class JdbcTransaction implements SpiTransaction {
if (!isActive()) {
throw new IllegalStateException(illegalStateMessage);
}
firePreCommit();
try {
if (queryOnly) {
// can rollback or just close for performance
@@ -620,6 +683,7 @@ public class JdbcTransaction implements SpiTransaction {
} finally {
// these will not throw an exception
firePostCommit();
deactivate();
notifyCommit();
}
@@ -653,6 +717,7 @@ public class JdbcTransaction implements SpiTransaction {
if (!isActive()) {
throw new IllegalStateException(illegalStateMessage);
}
firePreRollback();
try {
performRollback();
@@ -661,6 +726,7 @@ public class JdbcTransaction implements SpiTransaction {
} finally {
// these will not throw an exception
firePostRollback();
deactivate();
notifyRollback(cause);
}
@@ -427,10 +427,9 @@ public class TransactionManager {
for (TransactionEventListener listener : transactionEventListeners) {
listener.postTransactionCommit(transaction);
}
} catch (Exception ex) {
String m = "NotifyOfCommit failed. L2 Cache potentially not notified.";
logger.error(m, ex);
} catch (Exception ex) {
logger.error("NotifyOfCommit failed. L2 Cache potentially not notified.", ex);
}
}