killbill-aplcache

audit, history: revisit the way we generate entries Try to

11/3/2012 4:58:23 PM

Changes

Details

diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
index aa08ee5..a9e7564 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
@@ -22,21 +22,21 @@ import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 
 import com.ning.billing.account.api.Account;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.callcontext.InternalTenantContextBinder;
 import com.ning.billing.util.dao.EntityHistory;
 import com.ning.billing.util.dao.UuidMapper;
+import com.ning.billing.util.entity.dao.Audited;
 import com.ning.billing.util.entity.dao.UpdatableEntitySqlDao;
 
 @ExternalizedSqlViaStringTemplate3
 @RegisterMapper({UuidMapper.class, AccountMapper.class})
-public interface AccountSqlDao extends UpdatableEntitySqlDao<Account>, Transactional<AccountSqlDao>, Transmogrifier {
+public interface AccountSqlDao extends UpdatableEntitySqlDao<Account> {
 
     @SqlQuery
     public Account getAccountByKey(@Bind("externalKey") final String key,
@@ -48,21 +48,24 @@ public interface AccountSqlDao extends UpdatableEntitySqlDao<Account>, Transacti
 
     @Override
     @SqlUpdate
+    @Audited(ChangeType.INSERT)
     public void create(@AccountBinder Account account,
                        @InternalTenantContextBinder final InternalCallContext context);
 
     @Override
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     public void update(@AccountBinder Account account,
                        @InternalTenantContextBinder final InternalCallContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     public void updatePaymentMethod(@Bind("id") String accountId,
                                     @Bind("paymentMethodId") String paymentMethodId,
                                     @InternalTenantContextBinder final InternalCallContext context);
 
     @Override
     @SqlUpdate
-    public void insertHistoryFromTransaction(@AccountHistoryBinder final EntityHistory<Account> account,
-                                             @InternalTenantContextBinder final InternalCallContext context);
+    public void addHistoryFromTransaction(@AccountHistoryBinder final EntityHistory<Account> account,
+                                          @InternalTenantContextBinder final InternalCallContext context);
 }
diff --git a/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java b/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java
index b51a4a8..c8605bf 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java
@@ -21,8 +21,6 @@ import java.util.List;
 import java.util.UUID;
 
 import org.skife.jdbi.v2.IDBI;
-import org.skife.jdbi.v2.Transaction;
-import org.skife.jdbi.v2.TransactionStatus;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -31,14 +29,14 @@ import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.user.DefaultAccountChangeEvent;
 import com.ning.billing.account.api.user.DefaultAccountCreationEvent;
-import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.dao.EntityAudit;
-import com.ning.billing.util.dao.EntityHistory;
-import com.ning.billing.util.dao.TableName;
 import com.ning.billing.util.entity.EntityPersistenceException;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.events.AccountChangeInternalEvent;
 import com.ning.billing.util.events.AccountCreationInternalEvent;
 import com.ning.billing.util.svcsapi.bus.InternalBus;
@@ -50,6 +48,7 @@ public class AuditedAccountDao implements AccountDao {
 
     private static final Logger log = LoggerFactory.getLogger(AuditedAccountDao.class);
 
+    private final EntitySqlDaoTransactionalJdbiWrapper transactionalSqlDao;
     private final AccountSqlDao accountSqlDao;
     private final InternalBus eventBus;
     private final InternalCallContextFactory internalCallContextFactory;
@@ -59,6 +58,7 @@ public class AuditedAccountDao implements AccountDao {
         this.eventBus = eventBus;
         this.accountSqlDao = dbi.onDemand(AccountSqlDao.class);
         this.internalCallContextFactory = internalCallContextFactory;
+        transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi);
     }
 
     @Override
@@ -99,9 +99,11 @@ public class AuditedAccountDao implements AccountDao {
     public void create(final Account account, final InternalCallContext context) throws EntityPersistenceException {
         final String key = account.getExternalKey();
         try {
-            accountSqlDao.inTransaction(new Transaction<Void, AccountSqlDao>() {
+            transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
                 @Override
-                public Void inTransaction(final AccountSqlDao transactionalDao, final TransactionStatus status) throws AccountApiException, InternalBus.EventBusException {
+                public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws AccountApiException, InternalBus.EventBusException {
+                    final AccountSqlDao transactionalDao = entitySqlDaoWrapperFactory.become(AccountSqlDao.class);
+
                     final Account currentAccount = transactionalDao.getAccountByKey(key, context);
                     if (currentAccount != null) {
                         throw new AccountApiException(ErrorCode.ACCOUNT_ALREADY_EXISTS, key);
@@ -113,19 +115,10 @@ public class AuditedAccountDao implements AccountDao {
                     // We need to re-hydrate the context with the account record id
                     final InternalCallContext rehydratedContext = internalCallContextFactory.createInternalCallContext(recordId, context);
 
-                    // Insert history
-                    final EntityHistory<Account> history = new EntityHistory<Account>(account.getId(), recordId, account, ChangeType.INSERT);
-                    accountSqlDao.insertHistoryFromTransaction(history, rehydratedContext);
-
-                    // Insert audit
-                    final Long historyRecordId = accountSqlDao.getHistoryRecordId(recordId, rehydratedContext);
-                    final EntityAudit audit = new EntityAudit(TableName.ACCOUNT_HISTORY, historyRecordId, ChangeType.INSERT);
-                    accountSqlDao.insertAuditFromTransaction(audit, rehydratedContext);
-
                     final AccountCreationInternalEvent creationEvent = new DefaultAccountCreationEvent(account,
-                            rehydratedContext.getUserToken(),
-                            context.getAccountRecordId(),
-                            context.getTenantRecordId());
+                                                                                                       rehydratedContext.getUserToken(),
+                                                                                                       context.getAccountRecordId(),
+                                                                                                       context.getTenantRecordId());
                     try {
                         eventBus.postFromTransaction(creationEvent, transactionalDao, rehydratedContext);
                     } catch (final EventBusException e) {
@@ -148,9 +141,11 @@ public class AuditedAccountDao implements AccountDao {
     @Override
     public void update(final Account specifiedAccount, final InternalCallContext context) throws EntityPersistenceException {
         try {
-            accountSqlDao.inTransaction(new Transaction<Void, AccountSqlDao>() {
+            transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
                 @Override
-                public Void inTransaction(final AccountSqlDao transactional, final TransactionStatus status) throws EntityPersistenceException, InternalBus.EventBusException {
+                public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws EntityPersistenceException, InternalBus.EventBusException {
+                    final AccountSqlDao transactional = entitySqlDaoWrapperFactory.become(AccountSqlDao.class);
+
                     final UUID accountId = specifiedAccount.getId();
                     final Account currentAccount = transactional.getById(accountId.toString(), context);
                     if (currentAccount == null) {
@@ -162,14 +157,6 @@ public class AuditedAccountDao implements AccountDao {
 
                     transactional.update(account, context);
 
-                    final Long recordId = accountSqlDao.getRecordId(accountId.toString(), context);
-                    final EntityHistory<Account> history = new EntityHistory<Account>(accountId, recordId, account, ChangeType.UPDATE);
-                    accountSqlDao.insertHistoryFromTransaction(history, context);
-
-                    final Long historyRecordId = accountSqlDao.getHistoryRecordId(recordId, context);
-                    final EntityAudit audit = new EntityAudit(TableName.ACCOUNT_HISTORY, historyRecordId, ChangeType.UPDATE);
-                    accountSqlDao.insertAuditFromTransaction(audit, context);
-
                     final AccountChangeInternalEvent changeEvent = new DefaultAccountChangeEvent(accountId,
                             context.getUserToken(),
                             currentAccount,
@@ -203,9 +190,10 @@ public class AuditedAccountDao implements AccountDao {
     @Override
     public void updatePaymentMethod(final UUID accountId, final UUID paymentMethodId, final InternalCallContext context) throws EntityPersistenceException {
         try {
-            accountSqlDao.inTransaction(new Transaction<Void, AccountSqlDao>() {
+            transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
                 @Override
-                public Void inTransaction(final AccountSqlDao transactional, final TransactionStatus status) throws EntityPersistenceException, InternalBus.EventBusException {
+                public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws EntityPersistenceException, InternalBus.EventBusException {
+                    final AccountSqlDao transactional = entitySqlDaoWrapperFactory.become(AccountSqlDao.class);
 
                     final Account currentAccount = transactional.getById(accountId.toString(), context);
                     if (currentAccount == null) {
@@ -215,15 +203,6 @@ public class AuditedAccountDao implements AccountDao {
                     transactional.updatePaymentMethod(accountId.toString(), thePaymentMethodId, context);
 
                     final Account account = transactional.getById(accountId.toString(), context);
-
-                    final Long recordId = accountSqlDao.getRecordId(accountId.toString(), context);
-                    final EntityHistory<Account> history = new EntityHistory<Account>(accountId, recordId, account, ChangeType.UPDATE);
-                    accountSqlDao.insertHistoryFromTransaction(history, context);
-
-                    final Long historyRecordId = accountSqlDao.getHistoryRecordId(recordId, context);
-                    final EntityAudit audit = new EntityAudit(TableName.ACCOUNT_HISTORY, historyRecordId, ChangeType.UPDATE);
-                    accountSqlDao.insertAuditFromTransaction(audit, context);
-
                     final AccountChangeInternalEvent changeEvent = new DefaultAccountChangeEvent(accountId, context.getUserToken(), currentAccount, account,
                             context.getAccountRecordId(), context.getTenantRecordId());
 
diff --git a/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg b/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
index 0329a2f..f366a47 100644
--- a/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
+++ b/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
@@ -110,7 +110,7 @@ getHistoryRecordId() ::= <<
     WHERE record_id = :recordId <AND_CHECK_TENANT()>;
 >>
 
-insertHistoryFromTransaction() ::= <<
+addHistoryFromTransaction() ::= <<
     INSERT INTO account_history(<historyFields()>)
     VALUES
     (:recordId, :id, :externalKey, :email, :name, :firstNameLength, :currency,
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/AuditedEntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/AuditedEntitlementDao.java
index de8814c..98e3f9a 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/AuditedEntitlementDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/AuditedEntitlementDao.java
@@ -31,8 +31,6 @@ import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.IDBI;
-import org.skife.jdbi.v2.Transaction;
-import org.skife.jdbi.v2.TransactionStatus;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -65,12 +63,13 @@ import com.ning.billing.entitlement.events.user.ApiEventCancel;
 import com.ning.billing.entitlement.events.user.ApiEventChange;
 import com.ning.billing.entitlement.events.user.ApiEventType;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
-import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.clock.Clock;
-import com.ning.billing.util.dao.EntityAudit;
-import com.ning.billing.util.dao.TableName;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.events.RepairEntitlementInternalEvent;
 import com.ning.billing.util.notificationq.NotificationKey;
 import com.ning.billing.util.notificationq.NotificationQueue;
@@ -91,6 +90,7 @@ public class AuditedEntitlementDao implements EntitlementDao {
     private final SubscriptionSqlDao subscriptionsDao;
     private final BundleSqlDao bundlesDao;
     private final EntitlementEventSqlDao eventsDao;
+    private final EntitySqlDaoTransactionalJdbiWrapper transactionalSqlDao;
     private final NotificationQueueService notificationQueueService;
     private final AddonUtils addonUtils;
     private final InternalBus eventBus;
@@ -102,6 +102,7 @@ public class AuditedEntitlementDao implements EntitlementDao {
         this.subscriptionsDao = dbi.onDemand(SubscriptionSqlDao.class);
         this.eventsDao = dbi.onDemand(EntitlementEventSqlDao.class);
         this.bundlesDao = dbi.onDemand(BundleSqlDao.class);
+        this.transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi);
         this.notificationQueueService = notificationQueueService;
         this.addonUtils = addonUtils;
         this.eventBus = eventBus;
@@ -129,15 +130,11 @@ public class AuditedEntitlementDao implements EntitlementDao {
 
     @Override
     public SubscriptionBundle createSubscriptionBundle(final SubscriptionBundleData bundle, final InternalCallContext context) {
-        return bundlesDao.inTransaction(new Transaction<SubscriptionBundle, BundleSqlDao>() {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<SubscriptionBundle>() {
             @Override
-            public SubscriptionBundle inTransaction(final BundleSqlDao transactional, final TransactionStatus status) {
+            public SubscriptionBundle inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) {
+                final BundleSqlDao bundlesDao = entitySqlDaoWrapperFactory.become(BundleSqlDao.class);
                 bundlesDao.insertBundle(bundle, context);
-                final Long recordId = bundlesDao.getRecordId(bundle.getId().toString(), context);
-
-                final EntityAudit audit = new EntityAudit(TableName.BUNDLES, recordId, ChangeType.INSERT);
-                bundlesDao.insertAuditFromTransaction(audit, context);
-
                 return bundle;
             }
         });
@@ -196,23 +193,16 @@ public class AuditedEntitlementDao implements EntitlementDao {
     public void updateChargedThroughDate(final SubscriptionData subscription, final InternalCallContext context) {
         final Date ctd = (subscription.getChargedThroughDate() != null) ? subscription.getChargedThroughDate().toDate() : null;
 
-        subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
-            public Void inTransaction(final SubscriptionSqlDao transactionalDao, final TransactionStatus status) throws Exception {
-                final String subscriptionId = subscription.getId().toString();
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final SubscriptionSqlDao transactionalDao = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class);
                 transactionalDao.updateChargedThroughDate(subscription.getId().toString(), ctd, context);
-                final Long subscriptionRecordId = transactionalDao.getRecordId(subscriptionId, context);
-                final EntityAudit subscriptionAudit = new EntityAudit(TableName.SUBSCRIPTIONS, subscriptionRecordId, ChangeType.UPDATE);
-                transactionalDao.insertAuditFromTransaction(subscriptionAudit, context);
 
-                final BundleSqlDao bundleSqlDao = transactionalDao.become(BundleSqlDao.class);
+                final BundleSqlDao bundleSqlDao = entitySqlDaoWrapperFactory.become(BundleSqlDao.class);
                 final String bundleId = subscription.getBundleId().toString();
                 bundleSqlDao.updateBundleLastSysTime(bundleId, clock.getUTCNow().toDate(), context);
 
-                final Long recordId = bundleSqlDao.getRecordId(bundleId, context);
-                final EntityAudit bundleAudit = new EntityAudit(TableName.BUNDLES, recordId, ChangeType.UPDATE);
-                bundleSqlDao.insertAuditFromTransaction(bundleAudit, context);
-
                 return null;
             }
         });
@@ -220,17 +210,14 @@ public class AuditedEntitlementDao implements EntitlementDao {
 
     @Override
     public void createNextPhaseEvent(final SubscriptionData subscription, final EntitlementEvent nextPhase, final InternalCallContext context) {
-        eventsDao.inTransaction(new Transaction<Void, EntitlementEventSqlDao>() {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
-            public Void inTransaction(final EntitlementEventSqlDao transactional, final TransactionStatus status) throws Exception {
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final EntitlementEventSqlDao transactional = entitySqlDaoWrapperFactory.become(EntitlementEventSqlDao.class);
                 final UUID subscriptionId = subscription.getId();
                 cancelNextPhaseEventFromTransaction(subscriptionId, transactional, context);
                 transactional.insertEvent(nextPhase, context);
 
-                final Long recordId = transactional.getRecordId(nextPhase.getId().toString(), context);
-                final EntityAudit audit = new EntityAudit(TableName.SUBSCRIPTION_EVENTS, recordId, ChangeType.INSERT);
-                transactional.insertAuditFromTransaction(audit, context);
-
                 recordFutureNotificationFromTransaction(transactional,
                                                         nextPhase.getEffectiveDate(),
                                                         new EntitlementNotificationKey(nextPhase.getId()),
@@ -256,16 +243,17 @@ public class AuditedEntitlementDao implements EntitlementDao {
 
     @Override
     public Map<UUID, List<EntitlementEvent>> getEventsForBundle(final UUID bundleId, final InternalTenantContext context) {
-        return subscriptionsDao.inTransaction(new Transaction<Map<UUID, List<EntitlementEvent>>, SubscriptionSqlDao>() {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Map<UUID, List<EntitlementEvent>>>() {
             @Override
-            public Map<UUID, List<EntitlementEvent>> inTransaction(final SubscriptionSqlDao transactional,
-                                                                   final TransactionStatus status) throws Exception {
+            public Map<UUID, List<EntitlementEvent>> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final SubscriptionSqlDao transactional = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class);
+
                 final List<Subscription> subscriptions = transactional.getSubscriptionsFromBundleId(bundleId.toString(), context);
                 if (subscriptions.size() == 0) {
                     return Collections.emptyMap();
                 }
 
-                final EntitlementEventSqlDao eventsDaoFromSameTransaction = transactional.become(EntitlementEventSqlDao.class);
+                final EntitlementEventSqlDao eventsDaoFromSameTransaction = entitySqlDaoWrapperFactory.become(EntitlementEventSqlDao.class);
                 final Map<UUID, List<EntitlementEvent>> result = new HashMap<UUID, List<EntitlementEvent>>();
                 for (final Subscription cur : subscriptions) {
                     final List<EntitlementEvent> events = eventsDaoFromSameTransaction.getEventsForSubscription(cur.getId().toString(), context);
@@ -285,31 +273,22 @@ public class AuditedEntitlementDao implements EntitlementDao {
 
     @Override
     public void createSubscription(final SubscriptionData subscription, final List<EntitlementEvent> initialEvents, final InternalCallContext context) {
-        subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
-            public Void inTransaction(final SubscriptionSqlDao transactional, final TransactionStatus status) throws Exception {
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final SubscriptionSqlDao transactional = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class);
                 transactional.insertSubscription(subscription, context);
 
-                final Long subscriptionRecordId = transactional.getRecordId(subscription.getId().toString(), context);
-                final EntityAudit audit = new EntityAudit(TableName.SUBSCRIPTIONS, subscriptionRecordId, ChangeType.INSERT);
-                transactional.insertAuditFromTransaction(audit, context);
-
                 // STEPH batch as well
-                final EntitlementEventSqlDao eventsDaoFromSameTransaction = transactional.become(EntitlementEventSqlDao.class);
-                final List<EntityAudit> audits = new ArrayList<EntityAudit>();
-
+                final EntitlementEventSqlDao eventsDaoFromSameTransaction = entitySqlDaoWrapperFactory.become(EntitlementEventSqlDao.class);
                 for (final EntitlementEvent cur : initialEvents) {
                     eventsDaoFromSameTransaction.insertEvent(cur, context);
-                    final Long recordId = eventsDaoFromSameTransaction.getRecordId(cur.getId().toString(), context);
-                    audits.add(new EntityAudit(TableName.SUBSCRIPTION_EVENTS, recordId, ChangeType.INSERT));
                     recordFutureNotificationFromTransaction(transactional,
                                                             cur.getEffectiveDate(),
                                                             new EntitlementNotificationKey(cur.getId()),
                                                             context);
                 }
 
-                eventsDaoFromSameTransaction.insertAuditFromTransaction(audits, context);
-
                 // Notify the Bus of the latest requested change, if needed
                 if (initialEvents.size() > 0) {
                     notifyBusOfRequestedChange(eventsDaoFromSameTransaction, subscription, initialEvents.get(initialEvents.size() - 1), context);
@@ -322,15 +301,13 @@ public class AuditedEntitlementDao implements EntitlementDao {
 
     @Override
     public void recreateSubscription(final SubscriptionData subscription, final List<EntitlementEvent> recreateEvents, final InternalCallContext context) {
-        eventsDao.inTransaction(new Transaction<Void, EntitlementEventSqlDao>() {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
-            public Void inTransaction(final EntitlementEventSqlDao transactional,
-                                      final TransactionStatus status) throws Exception {
-                final List<EntityAudit> audits = new ArrayList<EntityAudit>();
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final EntitlementEventSqlDao transactional = entitySqlDaoWrapperFactory.become(EntitlementEventSqlDao.class);
+
                 for (final EntitlementEvent cur : recreateEvents) {
                     transactional.insertEvent(cur, context);
-                    final Long recordId = transactional.getRecordId(cur.getId().toString(), context);
-                    audits.add(new EntityAudit(TableName.SUBSCRIPTION_EVENTS, recordId, ChangeType.INSERT));
                     recordFutureNotificationFromTransaction(transactional,
                                                             cur.getEffectiveDate(),
                                                             new EntitlementNotificationKey(cur.getId()),
@@ -338,8 +315,6 @@ public class AuditedEntitlementDao implements EntitlementDao {
 
                 }
 
-                transactional.insertAuditFromTransaction(audits, context);
-
                 // Notify the Bus of the latest requested change
                 notifyBusOfRequestedChange(transactional, subscription, recreateEvents.get(recreateEvents.size() - 1), context);
 
@@ -350,9 +325,10 @@ public class AuditedEntitlementDao implements EntitlementDao {
 
     @Override
     public void cancelSubscription(final SubscriptionData subscription, final EntitlementEvent cancelEvent, final InternalCallContext context, final int seqId) {
-        eventsDao.inTransaction(new Transaction<Void, EntitlementEventSqlDao>() {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
-            public Void inTransaction(final EntitlementEventSqlDao transactional, final TransactionStatus status) throws Exception {
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final EntitlementEventSqlDao transactional = entitySqlDaoWrapperFactory.become(EntitlementEventSqlDao.class);
                 cancelSubscriptionFromTransaction(subscription, cancelEvent, transactional, context, seqId);
                 return null;
             }
@@ -361,9 +337,11 @@ public class AuditedEntitlementDao implements EntitlementDao {
 
     @Override
     public void uncancelSubscription(final SubscriptionData subscription, final List<EntitlementEvent> uncancelEvents, final InternalCallContext context) {
-        eventsDao.inTransaction(new Transaction<Void, EntitlementEventSqlDao>() {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
-            public Void inTransaction(final EntitlementEventSqlDao transactional, final TransactionStatus status) throws Exception {
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final EntitlementEventSqlDao transactional = entitySqlDaoWrapperFactory.become(EntitlementEventSqlDao.class);
+
                 final UUID subscriptionId = subscription.getId();
                 EntitlementEvent cancelledEvent = null;
                 final Date now = clock.getUTCNow().toDate();
@@ -379,25 +357,17 @@ public class AuditedEntitlementDao implements EntitlementDao {
                 }
 
                 if (cancelledEvent != null) {
-                    final List<EntityAudit> eventAudits = new ArrayList<EntityAudit>();
-
                     final String cancelledEventId = cancelledEvent.getId().toString();
                     transactional.unactiveEvent(cancelledEventId, context);
-                    final Long cancelledRecordId = transactional.getRecordId(cancelledEventId, context);
-                    eventAudits.add(new EntityAudit(TableName.SUBSCRIPTION_EVENTS, cancelledRecordId, ChangeType.UPDATE));
 
                     for (final EntitlementEvent cur : uncancelEvents) {
                         transactional.insertEvent(cur, context);
-                        final Long recordId = transactional.getRecordId(cur.getId().toString(), context);
-                        eventAudits.add(new EntityAudit(TableName.SUBSCRIPTION_EVENTS, recordId, ChangeType.INSERT));
                         recordFutureNotificationFromTransaction(transactional,
                                                                 cur.getEffectiveDate(),
                                                                 new EntitlementNotificationKey(cur.getId()),
                                                                 context);
                     }
 
-                    transactional.insertAuditFromTransaction(eventAudits, context);
-
                     // Notify the Bus of the latest requested change
                     notifyBusOfRequestedChange(transactional, subscription, uncancelEvents.get(uncancelEvents.size() - 1), context);
                 }
@@ -409,17 +379,16 @@ public class AuditedEntitlementDao implements EntitlementDao {
 
     @Override
     public void changePlan(final SubscriptionData subscription, final List<EntitlementEvent> changeEvents, final InternalCallContext context) {
-        eventsDao.inTransaction(new Transaction<Void, EntitlementEventSqlDao>() {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
-            public Void inTransaction(final EntitlementEventSqlDao transactional, final TransactionStatus status) throws Exception {
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final EntitlementEventSqlDao transactional = entitySqlDaoWrapperFactory.become(EntitlementEventSqlDao.class);
+
                 final UUID subscriptionId = subscription.getId();
                 cancelFutureEventsFromTransaction(subscriptionId, transactional, context);
 
-                final List<EntityAudit> eventAudits = new ArrayList<EntityAudit>();
                 for (final EntitlementEvent cur : changeEvents) {
                     transactional.insertEvent(cur, context);
-                    final Long recordId = transactional.getRecordId(cur.getId().toString(), context);
-                    eventAudits.add(new EntityAudit(TableName.SUBSCRIPTION_EVENTS, recordId, ChangeType.INSERT));
 
                     recordFutureNotificationFromTransaction(transactional,
                                                             cur.getEffectiveDate(),
@@ -427,8 +396,6 @@ public class AuditedEntitlementDao implements EntitlementDao {
                                                             context);
                 }
 
-                transactional.insertAuditFromTransaction(eventAudits, context);
-
                 // Notify the Bus of the latest requested change
                 final EntitlementEvent finalEvent = changeEvents.get(changeEvents.size() - 1);
                 notifyBusOfRequestedChange(transactional, subscription, finalEvent, context);
@@ -442,11 +409,6 @@ public class AuditedEntitlementDao implements EntitlementDao {
         final UUID subscriptionId = subscription.getId();
         cancelFutureEventsFromTransaction(subscriptionId, transactional, context);
         transactional.insertEvent(cancelEvent, context);
-        final String cancelEventId = cancelEvent.getId().toString();
-
-        final Long recordId = transactional.getRecordId(cancelEventId, context);
-        final EntityAudit audit = new EntityAudit(TableName.SUBSCRIPTION_EVENTS, recordId, ChangeType.INSERT);
-        transactional.insertAuditFromTransaction(audit, context);
 
         recordFutureNotificationFromTransaction(transactional,
                                                 cancelEvent.getEffectiveDate(),
@@ -491,9 +453,6 @@ public class AuditedEntitlementDao implements EntitlementDao {
         if (event != null) {
             final String eventId = event.getId().toString();
             dao.unactiveEvent(eventId, context);
-            final Long recordId = dao.getRecordId(eventId, context);
-            final EntityAudit audit = new EntityAudit(TableName.SUBSCRIPTION_EVENTS, recordId, ChangeType.UPDATE);
-            dao.insertAuditFromTransaction(audit, context);
         }
     }
 
@@ -601,11 +560,13 @@ public class AuditedEntitlementDao implements EntitlementDao {
 
     @Override
     public void migrate(final UUID accountId, final AccountMigrationData accountData, final InternalCallContext context) {
-        eventsDao.inTransaction(new Transaction<Void, EntitlementEventSqlDao>() {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
-            public Void inTransaction(final EntitlementEventSqlDao transactional, final TransactionStatus status) throws Exception {
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final EntitlementEventSqlDao transactional = entitySqlDaoWrapperFactory.become(EntitlementEventSqlDao.class);
+
                 for (final BundleMigrationData curBundle : accountData.getData()) {
-                    migrateBundleDataFromTransaction(curBundle, transactional, context);
+                    migrateBundleDataFromTransaction(curBundle, transactional, entitySqlDaoWrapperFactory, context);
                 }
                 return null;
             }
@@ -614,10 +575,12 @@ public class AuditedEntitlementDao implements EntitlementDao {
 
     @Override
     public void repair(final UUID accountId, final UUID bundleId, final List<SubscriptionDataRepair> inRepair, final InternalCallContext context) {
-        subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
-            public Void inTransaction(final SubscriptionSqlDao transactional, final TransactionStatus status) throws Exception {
-                final EntitlementEventSqlDao transEventDao = transactional.become(EntitlementEventSqlDao.class);
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final SubscriptionSqlDao transactional = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class);
+
+                final EntitlementEventSqlDao transEventDao = entitySqlDaoWrapperFactory.become(EntitlementEventSqlDao.class);
                 for (final SubscriptionDataRepair cur : inRepair) {
                     transactional.updateForRepair(cur.getId().toString(), cur.getActiveVersion(), cur.getAlignStartDate().toDate(), cur.getBundleStartDate().toDate(), context);
                     for (final EntitlementEvent event : cur.getInitialEvents()) {
@@ -652,16 +615,17 @@ public class AuditedEntitlementDao implements EntitlementDao {
     public void transfer(final UUID srcAccountId, final UUID destAccountId, final BundleMigrationData bundleTransferData,
                          final List<TransferCancelData> transferCancelData, final InternalCallContext context) {
 
-        eventsDao.inTransaction(new Transaction<Void, EntitlementEventSqlDao>() {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
-            public Void inTransaction(final EntitlementEventSqlDao transactional, final TransactionStatus status) throws Exception {
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final EntitlementEventSqlDao transactional = entitySqlDaoWrapperFactory.become(EntitlementEventSqlDao.class);
 
                 // Cancel the subscriptions for the old bundle
                 for (final TransferCancelData cancel : transferCancelData) {
                     cancelSubscriptionFromTransaction(cancel.getSubscription(), cancel.getCancelEvent(), transactional, context, 0);
                 }
 
-                migrateBundleDataFromTransaction(bundleTransferData, transactional, context);
+                migrateBundleDataFromTransaction(bundleTransferData, transactional, entitySqlDaoWrapperFactory, context);
                 return null;
             }
         });
@@ -700,13 +664,12 @@ public class AuditedEntitlementDao implements EntitlementDao {
         }
     }
 
-    private void migrateBundleDataFromTransaction(final BundleMigrationData bundleTransferData, final EntitlementEventSqlDao transactional, final InternalCallContext context) {
+    private void migrateBundleDataFromTransaction(final BundleMigrationData bundleTransferData, final EntitlementEventSqlDao transactional,
+                                                  final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalCallContext context) {
 
-        final SubscriptionSqlDao transSubDao = transactional.become(SubscriptionSqlDao.class);
-        final BundleSqlDao transBundleDao = transactional.become(BundleSqlDao.class);
+        final SubscriptionSqlDao transSubDao = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class);
+        final BundleSqlDao transBundleDao = entitySqlDaoWrapperFactory.become(BundleSqlDao.class);
 
-        final List<EntityAudit> audits = new ArrayList<EntityAudit>();
-        Long recordId;
         final SubscriptionBundleData bundleData = bundleTransferData.getData();
 
         final SubscriptionBundle existingBundle = transBundleDao.getBundleFromAccountAndKey(bundleData.getAccountId().toString(), bundleData.getKey(), context);
@@ -719,8 +682,6 @@ public class AuditedEntitlementDao implements EntitlementDao {
             final SubscriptionData subData = curSubscription.getData();
             for (final EntitlementEvent curEvent : curSubscription.getInitialEvents()) {
                 transactional.insertEvent(curEvent, context);
-                recordId = transactional.getRecordId(curEvent.getId().toString(), context);
-                audits.add(new EntityAudit(TableName.SUBSCRIPTION_EVENTS, recordId, ChangeType.INSERT));
 
                 recordFutureNotificationFromTransaction(transactional,
                                                         curEvent.getEffectiveDate(),
@@ -728,8 +689,6 @@ public class AuditedEntitlementDao implements EntitlementDao {
                                                         context);
             }
             transSubDao.insertSubscription(subData, context);
-            recordId = transSubDao.getRecordId(subData.getId().toString(), context);
-            audits.add(new EntityAudit(TableName.SUBSCRIPTIONS, recordId, ChangeType.INSERT));
 
             // Notify the Bus of the latest requested change
             final EntitlementEvent finalEvent = curSubscription.getInitialEvents().get(curSubscription.getInitialEvents().size() - 1);
@@ -737,10 +696,5 @@ public class AuditedEntitlementDao implements EntitlementDao {
         }
 
         transBundleDao.insertBundle(bundleData, context);
-        recordId = transBundleDao.getRecordId(bundleData.getId().toString(), context);
-        audits.add(new EntityAudit(TableName.BUNDLES, recordId, ChangeType.INSERT));
-
-        // add audit records for bundles, subscriptions, and events
-        transSubDao.insertAuditFromTransaction(audits, context);
     }
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
index 0750c93..37a3b43 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
@@ -30,31 +30,30 @@ import org.skife.jdbi.v2.sqlobject.Binder;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
-import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.dao.AuditSqlDao;
 import com.ning.billing.util.dao.BinderBase;
 import com.ning.billing.util.dao.MapperBase;
+import com.ning.billing.util.entity.dao.Audited;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
 
 @ExternalizedSqlViaStringTemplate3()
 @RegisterMapper(BundleSqlDao.ISubscriptionBundleSqlMapper.class)
-public interface BundleSqlDao extends Transactional<BundleSqlDao>, EntitySqlDao<SubscriptionBundle>,
-                                      AuditSqlDao, CloseMe, Transmogrifier {
+public interface BundleSqlDao extends EntitySqlDao<SubscriptionBundle> {
     @SqlUpdate
+    @Audited(ChangeType.INSERT)
     public void insertBundle(@Bind(binder = SubscriptionBundleBinder.class) SubscriptionBundleData bundle,
                              @InternalTenantContextBinder final InternalCallContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     public void updateBundleLastSysTime(@Bind("id") String id,
                                         @Bind("lastSysUpdateDate") Date lastSysUpdate,
                                         @InternalTenantContextBinder final InternalCallContext context);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.java
index 9fff155..725edd9 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.java
@@ -29,13 +29,11 @@ import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.Binder;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
-import org.skife.jdbi.v2.sqlobject.customizers.Mapper;
-import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
+import com.ning.billing.entitlement.engine.dao.EntitlementEventSqlDao.EventSqlMapper;
 import com.ning.billing.entitlement.events.EntitlementEvent;
 import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
 import com.ning.billing.entitlement.events.EventBaseBuilder;
@@ -54,48 +52,50 @@ import com.ning.billing.entitlement.events.user.ApiEventTransfer;
 import com.ning.billing.entitlement.events.user.ApiEventType;
 import com.ning.billing.entitlement.events.user.ApiEventUncancel;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.dao.AuditSqlDao;
 import com.ning.billing.util.dao.BinderBase;
 import com.ning.billing.util.dao.MapperBase;
+import com.ning.billing.util.entity.dao.Audited;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
 
 @ExternalizedSqlViaStringTemplate3()
-public interface EntitlementEventSqlDao extends Transactional<EntitlementEventSqlDao>, AuditSqlDao,
-                                                EntitySqlDao<EntitlementEvent>, CloseMe, Transmogrifier {
+@RegisterMapper(EventSqlMapper.class)
+public interface EntitlementEventSqlDao extends EntitySqlDao<EntitlementEvent> {
 
     @SqlQuery
-    @Mapper(EventSqlMapper.class)
     public EntitlementEvent getEventById(@Bind("id") String id,
                                          @InternalTenantContextBinder final InternalTenantContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.INSERT)
     public void insertEvent(@Bind(binder = EventSqlDaoBinder.class) EntitlementEvent evt,
                             @InternalTenantContextBinder final InternalCallContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     public void unactiveEvent(@Bind("id") String id,
                               @InternalTenantContextBinder final InternalCallContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     public void reactiveEvent(@Bind("id") String id,
                               @InternalTenantContextBinder final InternalCallContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     public void updateVersion(@Bind("id") String id,
                               @Bind("currentVersion") Long currentVersion,
                               @InternalTenantContextBinder final InternalCallContext context);
 
     @SqlQuery
-    @Mapper(EventSqlMapper.class)
     public List<EntitlementEvent> getFutureActiveEventForSubscription(@Bind("subscriptionId") String subscriptionId,
                                                                       @Bind("now") Date now,
                                                                       @InternalTenantContextBinder final InternalTenantContext context);
 
     @SqlQuery
-    @Mapper(EventSqlMapper.class)
     public List<EntitlementEvent> getEventsForSubscription(@Bind("subscriptionId") String subscriptionId,
                                                            @InternalTenantContextBinder final InternalTenantContext context);
 
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
index 9a7dc3f..743afd4 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
@@ -29,10 +29,7 @@ import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.Binder;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
-import org.skife.jdbi.v2.sqlobject.customizers.Mapper;
-import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
@@ -40,39 +37,45 @@ import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.user.DefaultSubscriptionFactory.SubscriptionBuilder;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.engine.dao.SubscriptionSqlDao.SubscriptionMapper;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.dao.AuditSqlDao;
 import com.ning.billing.util.dao.BinderBase;
 import com.ning.billing.util.dao.MapperBase;
+import com.ning.billing.util.entity.dao.Audited;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
 
 @ExternalizedSqlViaStringTemplate3()
-public interface SubscriptionSqlDao extends Transactional<SubscriptionSqlDao>, AuditSqlDao, CloseMe, Transmogrifier {
+@RegisterMapper(SubscriptionMapper.class)
+public interface SubscriptionSqlDao extends EntitySqlDao<Subscription> {
 
     @SqlUpdate
+    @Audited(ChangeType.INSERT)
     public void insertSubscription(@Bind(binder = SubscriptionBinder.class) SubscriptionData sub,
                                    @InternalTenantContextBinder final InternalCallContext context);
 
     @SqlQuery
-    @Mapper(SubscriptionMapper.class)
     public Subscription getSubscriptionFromId(@Bind("id") String id,
                                               @InternalTenantContextBinder final InternalTenantContext context);
 
     @SqlQuery
-    @Mapper(SubscriptionMapper.class)
     public List<Subscription> getSubscriptionsFromBundleId(@Bind("bundleId") String bundleId,
                                                            @InternalTenantContextBinder final InternalTenantContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     public void updateChargedThroughDate(@Bind("id") String id, @Bind("chargedThroughDate") Date chargedThroughDate,
                                          @InternalTenantContextBinder final InternalCallContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     void updateActiveVersion(@Bind("id") String id, @Bind("activeVersion") long activeVersion,
                              @InternalTenantContextBinder final InternalCallContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     public void updateForRepair(@Bind("id") String id, @Bind("activeVersion") long activeVersion,
                                 @Bind("startDate") Date startDate,
                                 @Bind("bundleStartDate") Date bundleStartDate,
@@ -93,7 +96,7 @@ public interface SubscriptionSqlDao extends Transactional<SubscriptionSqlDao>, A
         }
     }
 
-    public static class SubscriptionMapper extends MapperBase implements ResultSetMapper<SubscriptionData> {
+    public static class SubscriptionMapper extends MapperBase implements ResultSetMapper<Subscription> {
 
         @Override
         public SubscriptionData map(final int arg0, final ResultSet r, final StatementContext ctx)
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
index 84fe7d3..7e41f60 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/BundleSqlDao.sql.stg
@@ -70,6 +70,13 @@ getRecordId() ::= <<
     ;
 >>
 
+getById() ::= <<
+    SELECT <fields()>
+    FROM bundles
+    WHERE id = :id <AND_CHECK_TENANT()>
+    ;
+>>
+
 auditFields(prefix) ::= <<
     <prefix>table_name,
     <prefix>record_id,
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.sql.stg
index b8d63d3..9a16aca 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EntitlementEventSqlDao.sql.stg
@@ -126,6 +126,13 @@ getRecordId() ::= <<
     ;
 >>
 
+getById() ::= <<
+    SELECT record_id, <fields()>
+    FROM subscription_events
+    WHERE id = :id <AND_CHECK_TENANT()>
+    ;
+>>
+
 auditFields(prefix) ::= <<
     <prefix>table_name,
     <prefix>record_id,
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg
index e74145a..2b08da3 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg
@@ -3,23 +3,26 @@ group SubscriptionSqlDao;
 CHECK_TENANT() ::= "tenant_record_id = :tenantRecordId"
 AND_CHECK_TENANT() ::= "AND <CHECK_TENANT()>"
 
+fields(prefix) ::= <<
+    <prefix>id,
+    <prefix>bundle_id,
+    <prefix>category,
+    <prefix>start_date,
+    <prefix>bundle_start_date,
+    <prefix>active_version,
+    <prefix>charged_through_date,
+    <prefix>paid_through_date,
+    <prefix>created_by,
+    <prefix>created_date,
+    <prefix>updated_by,
+    <prefix>updated_date,
+    <prefix>account_record_id,
+    <prefix>tenant_record_id
+>>
+
 insertSubscription() ::= <<
-    insert into subscriptions (
-        id
-      , bundle_id
-      , category
-      , start_date
-      , bundle_start_date
-      , active_version
-      , charged_through_date
-      , paid_through_date
-      , created_by
-      , created_date
-      , updated_by
-      , updated_date
-      , account_record_id
-      , tenant_record_id
-    ) values (
+    insert into subscriptions (<fields()>)
+    values (
         :id
       , :bundleId
       , :category
@@ -115,6 +118,13 @@ getRecordId() ::= <<
     ;
 >>
 
+getById() ::= <<
+    SELECT record_id, <fields()>
+    FROM subscriptions
+    WHERE id = :id <AND_CHECK_TENANT()>
+    ;
+>>
+
 auditFields(prefix) ::= <<
     <prefix>table_name,
     <prefix>record_id,
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/AuditedInvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/AuditedInvoiceDao.java
index 007e13b..27d9da4 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/AuditedInvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/AuditedInvoiceDao.java
@@ -27,8 +27,6 @@ import javax.annotation.Nullable;
 
 import org.joda.time.LocalDate;
 import org.skife.jdbi.v2.IDBI;
-import org.skife.jdbi.v2.Transaction;
-import org.skife.jdbi.v2.TransactionStatus;
 import org.skife.jdbi.v2.exceptions.TransactionFailedException;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.slf4j.Logger;
@@ -52,12 +50,13 @@ import com.ning.billing.invoice.model.ItemAdjInvoiceItem;
 import com.ning.billing.invoice.model.RecurringInvoiceItem;
 import com.ning.billing.invoice.model.RefundAdjInvoiceItem;
 import com.ning.billing.invoice.notification.NextBillingDatePoster;
-import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.clock.Clock;
-import com.ning.billing.util.dao.EntityAudit;
-import com.ning.billing.util.dao.TableName;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.svcsapi.bus.InternalBus;
 import com.ning.billing.util.svcsapi.bus.InternalBus.EventBusException;
 
@@ -72,6 +71,7 @@ public class AuditedInvoiceDao implements InvoiceDao {
 
     private static final Logger log = LoggerFactory.getLogger(AuditedInvoiceDao.class);
 
+    private final EntitySqlDaoTransactionalJdbiWrapper transactionalSqlDao;
     private final InvoiceSqlDao invoiceSqlDao;
     private final InvoicePaymentSqlDao invoicePaymentSqlDao;
     private final NextBillingDatePoster nextBillingDatePoster;
@@ -90,15 +90,18 @@ public class AuditedInvoiceDao implements InvoiceDao {
         this.nextBillingDatePoster = nextBillingDatePoster;
         this.clock = clock;
         this.eventBus = eventBus;
+        this.transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi);
     }
 
     @Override
     public List<Invoice> getInvoicesByAccount(final UUID accountId, final InternalTenantContext context) {
-        return invoiceSqlDao.inTransaction(new Transaction<List<Invoice>, InvoiceSqlDao>() {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<Invoice>>() {
             @Override
-            public List<Invoice> inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
+            public List<Invoice> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao invoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+
                 final List<Invoice> invoices = invoiceDao.getInvoicesByAccount(accountId.toString(), context);
-                populateChildren(invoices, invoiceDao, context);
+                populateChildren(invoices, entitySqlDaoWrapperFactory, context);
                 return invoices;
             }
         });
@@ -106,24 +109,28 @@ public class AuditedInvoiceDao implements InvoiceDao {
 
     @Override
     public List<Invoice> getAllInvoicesByAccount(final UUID accountId, final InternalTenantContext context) {
-        return invoiceSqlDao.inTransaction(new Transaction<List<Invoice>, InvoiceSqlDao>() {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<Invoice>>() {
             @Override
-            public List<Invoice> inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
-                return getAllInvoicesByAccountFromTransaction(accountId, invoiceDao, context);
+            public List<Invoice> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao invoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+
+                return getAllInvoicesByAccountFromTransaction(accountId, entitySqlDaoWrapperFactory, context);
             }
         });
     }
 
     @Override
     public List<Invoice> getInvoicesByAccount(final UUID accountId, final LocalDate fromDate, final InternalTenantContext context) {
-        return invoiceSqlDao.inTransaction(new Transaction<List<Invoice>, InvoiceSqlDao>() {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<Invoice>>() {
             @Override
-            public List<Invoice> inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
+            public List<Invoice> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao invoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+
                 final List<Invoice> invoices = invoiceDao.getInvoicesByAccountAfterDate(accountId.toString(),
                                                                                         fromDate.toDateTimeAtStartOfDay().toDate(),
                                                                                         context);
 
-                populateChildren(invoices, invoiceDao, context);
+                populateChildren(invoices, entitySqlDaoWrapperFactory, context);
 
                 return invoices;
             }
@@ -132,12 +139,14 @@ public class AuditedInvoiceDao implements InvoiceDao {
 
     @Override
     public List<Invoice> get(final InternalTenantContext context) {
-        return invoiceSqlDao.inTransaction(new Transaction<List<Invoice>, InvoiceSqlDao>() {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<Invoice>>() {
             @Override
-            public List<Invoice> inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
+            public List<Invoice> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao invoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+
                 final List<Invoice> invoices = invoiceDao.get(context);
 
-                populateChildren(invoices, invoiceDao, context);
+                populateChildren(invoices, entitySqlDaoWrapperFactory, context);
 
                 return invoices;
             }
@@ -147,14 +156,16 @@ public class AuditedInvoiceDao implements InvoiceDao {
     @Override
     public Invoice getById(final UUID invoiceId, final InternalTenantContext context) throws InvoiceApiException {
         try {
-            return invoiceSqlDao.inTransaction(new Transaction<Invoice, InvoiceSqlDao>() {
+            return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Invoice>() {
                 @Override
-                public Invoice inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
+                public Invoice inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                    final InvoiceSqlDao invoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+
                     final Invoice invoice = invoiceDao.getById(invoiceId.toString(), context);
                     if (invoice == null) {
                         throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceId);
                     }
-                    populateChildren(invoice, invoiceDao, context);
+                    populateChildren(invoice, entitySqlDaoWrapperFactory, context);
                     return invoice;
                 }
             });
@@ -179,63 +190,46 @@ public class AuditedInvoiceDao implements InvoiceDao {
 
     @Override
     public void create(final Invoice invoice, final int billCycleDayUTC, final boolean isRealInvoice, final InternalCallContext context) {
-        invoiceSqlDao.inTransaction(new Transaction<Void, InvoiceSqlDao>() {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
-            public Void inTransaction(final InvoiceSqlDao transactional, final TransactionStatus status) throws Exception {
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+
                 final Invoice currentInvoice = transactional.getById(invoice.getId().toString(), context);
                 if (currentInvoice == null) {
-                    final List<EntityAudit> audits = new ArrayList<EntityAudit>();
-
                     // We only want to insert that invoice if there are real invoiceItems associated to it-- if not, this is just
                     // a shell invoice and we only need to insert the invoiceItems-- for the already existing invoices
 
                     if (isRealInvoice) {
                         transactional.create(invoice, context);
-                        final Long recordId = transactional.getRecordId(invoice.getId().toString(), context);
-                        audits.add(new EntityAudit(TableName.INVOICES, recordId, ChangeType.INSERT));
                     }
 
-                    List<Long> recordIdList;
-
                     final List<InvoiceItem> invoiceItems = invoice.getInvoiceItems();
-                    final InvoiceItemSqlDao transInvoiceItemSqlDao = transactional.become(InvoiceItemSqlDao.class);
+                    final InvoiceItemSqlDao transInvoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
                     transInvoiceItemSqlDao.batchCreateFromTransaction(invoiceItems, context);
-                    recordIdList = transInvoiceItemSqlDao.getRecordIds(invoice.getId().toString(), context);
-                    audits.addAll(createAudits(TableName.INVOICE_ITEMS, recordIdList));
 
                     final List<InvoiceItem> recurringInvoiceItems = invoice.getInvoiceItems(RecurringInvoiceItem.class);
                     notifyOfFutureBillingEvents(transactional, invoice.getAccountId(), recurringInvoiceItems);
 
                     final List<InvoicePayment> invoicePayments = invoice.getPayments();
-                    final InvoicePaymentSqlDao invoicePaymentSqlDao = transactional.become(InvoicePaymentSqlDao.class);
+                    final InvoicePaymentSqlDao invoicePaymentSqlDao = entitySqlDaoWrapperFactory.become(InvoicePaymentSqlDao.class);
                     invoicePaymentSqlDao.batchCreateFromTransaction(invoicePayments, context);
-                    recordIdList = invoicePaymentSqlDao.getRecordIds(invoice.getId().toString(), context);
-                    audits.addAll(createAudits(TableName.INVOICE_PAYMENTS, recordIdList));
-
-                    transactional.insertAuditFromTransaction(audits, context);
                 }
                 return null;
             }
         });
     }
 
-    private List<EntityAudit> createAudits(final TableName tableName, final List<Long> recordIdList) {
-        final List<EntityAudit> entityAuditList = new ArrayList<EntityAudit>();
-        for (final Long recordId : recordIdList) {
-            entityAuditList.add(new EntityAudit(tableName, recordId, ChangeType.INSERT));
-        }
-
-        return entityAuditList;
-    }
-
     @Override
     public List<Invoice> getInvoicesBySubscription(final UUID subscriptionId, final InternalTenantContext context) {
-        return invoiceSqlDao.inTransaction(new Transaction<List<Invoice>, InvoiceSqlDao>() {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<Invoice>>() {
             @Override
-            public List<Invoice> inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
+            public List<Invoice> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao invoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+
                 final List<Invoice> invoices = invoiceDao.getInvoicesBySubscription(subscriptionId.toString(), context);
 
-                populateChildren(invoices, invoiceDao, context);
+                populateChildren(invoices, entitySqlDaoWrapperFactory, context);
 
                 return invoices;
             }
@@ -245,13 +239,13 @@ public class AuditedInvoiceDao implements InvoiceDao {
     @Override
     public BigDecimal getAccountBalance(final UUID accountId, final InternalTenantContext context) {
 
-        return invoiceSqlDao.inTransaction(new Transaction<BigDecimal, InvoiceSqlDao>() {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<BigDecimal>() {
             @Override
-            public BigDecimal inTransaction(final InvoiceSqlDao transactional, final TransactionStatus status) throws Exception {
+            public BigDecimal inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
                 BigDecimal cba = BigDecimal.ZERO;
 
                 BigDecimal accountBalance = BigDecimal.ZERO;
-                final List<Invoice> invoices = getAllInvoicesByAccountFromTransaction(accountId, transactional, context);
+                final List<Invoice> invoices = getAllInvoicesByAccountFromTransaction(accountId, entitySqlDaoWrapperFactory, context);
                 for (final Invoice cur : invoices) {
                     accountBalance = accountBalance.add(cur.getBalance());
                     cba = cba.add(cur.getCBAAmount());
@@ -263,25 +257,23 @@ public class AuditedInvoiceDao implements InvoiceDao {
 
     @Override
     public BigDecimal getAccountCBA(final UUID accountId, final InternalTenantContext context) {
-        return invoiceSqlDao.inTransaction(new Transaction<BigDecimal, InvoiceSqlDao>() {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<BigDecimal>() {
             @Override
-            public BigDecimal inTransaction(final InvoiceSqlDao transactional, final TransactionStatus status) throws Exception {
-                return getAccountCBAFromTransaction(accountId, transactional, context);
+            public BigDecimal inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+                return getAccountCBAFromTransaction(accountId, entitySqlDaoWrapperFactory, context);
             }
         });
     }
 
     @Override
     public void notifyOfPayment(final InvoicePayment invoicePayment, final InternalCallContext context) {
-        invoicePaymentSqlDao.inTransaction(new Transaction<Void, InvoicePaymentSqlDao>() {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
-            public Void inTransaction(final InvoicePaymentSqlDao transactional, final TransactionStatus status) throws Exception {
-                transactional.notifyOfPayment(invoicePayment, context);
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoicePaymentSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoicePaymentSqlDao.class);
 
-                final String invoicePaymentId = invoicePayment.getId().toString();
-                final Long recordId = transactional.getRecordId(invoicePaymentId, context);
-                final EntityAudit audit = new EntityAudit(TableName.INVOICE_PAYMENTS, recordId, ChangeType.INSERT);
-                transactional.insertAuditFromTransaction(audit, context);
+                transactional.notifyOfPayment(invoicePayment, context);
 
                 return null;
             }
@@ -290,11 +282,12 @@ public class AuditedInvoiceDao implements InvoiceDao {
 
     @Override
     public List<Invoice> getUnpaidInvoicesByAccountId(final UUID accountId, @Nullable final LocalDate upToDate, final InternalTenantContext context) {
-        return invoiceSqlDao.inTransaction(new Transaction<List<Invoice>, InvoiceSqlDao>() {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<Invoice>>() {
             @Override
-            public List<Invoice> inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
+            public List<Invoice> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao invoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
 
-                final List<Invoice> invoices = getAllInvoicesByAccountFromTransaction(accountId, invoiceDao, context);
+                final List<Invoice> invoices = getAllInvoicesByAccountFromTransaction(accountId, entitySqlDaoWrapperFactory, context);
                 final Collection<Invoice> unpaidInvoices = Collections2.filter(invoices, new Predicate<Invoice>() {
                     @Override
                     public boolean apply(final Invoice in) {
@@ -324,12 +317,12 @@ public class AuditedInvoiceDao implements InvoiceDao {
             throws InvoiceApiException {
         final boolean isInvoiceItemAdjusted = isInvoiceAdjusted && invoiceItemIdsWithNullAmounts.size() > 0;
 
-        return invoicePaymentSqlDao.inTransaction(new Transaction<InvoicePayment, InvoicePaymentSqlDao>() {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<InvoicePayment>() {
             @Override
-            public InvoicePayment inTransaction(final InvoicePaymentSqlDao transactional, final TransactionStatus status) throws Exception {
-                final List<EntityAudit> audits = new ArrayList<EntityAudit>();
+            public InvoicePayment inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoicePaymentSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoicePaymentSqlDao.class);
 
-                final InvoiceSqlDao transInvoiceDao = transactional.become(InvoiceSqlDao.class);
+                final InvoiceSqlDao transInvoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
 
                 final InvoicePayment payment = transactional.getByPaymentId(paymentId.toString(), context);
                 if (payment == null) {
@@ -338,7 +331,7 @@ public class AuditedInvoiceDao implements InvoiceDao {
 
                 // Retrieve the amounts to adjust, if needed
                 final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts = computeItemAdjustments(payment.getInvoiceId().toString(),
-                                                                                               transInvoiceDao,
+                                                                                               entitySqlDaoWrapperFactory,
                                                                                                invoiceItemIdsWithNullAmounts,
                                                                                                context);
 
@@ -356,19 +349,17 @@ public class AuditedInvoiceDao implements InvoiceDao {
                         payment.getInvoiceId(), context.getCreatedDate(), requestedPositiveAmount.negate(),
                         payment.getCurrency(), paymentCookieId, payment.getId());
                 transactional.create(refund, context);
-                final Long refundRecordId = transactional.getRecordId(refund.getId().toString(), context);
-                audits.add(new EntityAudit(TableName.REFUNDS, refundRecordId, ChangeType.INSERT));
 
                 // Retrieve invoice after the Refund
                 final Invoice invoice = transInvoiceDao.getById(payment.getInvoiceId().toString(), context);
                 if (invoice != null) {
-                    populateChildren(invoice, transInvoiceDao, context);
+                    populateChildren(invoice, entitySqlDaoWrapperFactory, context);
                 } else {
                     throw new IllegalStateException("Invoice shouldn't be null for payment " + payment.getId());
                 }
 
                 final BigDecimal invoiceBalanceAfterRefund = invoice.getBalance();
-                final InvoiceItemSqlDao transInvoiceItemDao = transInvoiceDao.become(InvoiceItemSqlDao.class);
+                final InvoiceItemSqlDao transInvoiceItemDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
 
                 // At this point, we created the refund which made the invoice balance positive and applied any existing
                 // available CBA to that invoice.
@@ -380,28 +371,21 @@ public class AuditedInvoiceDao implements InvoiceDao {
                     if (requestedPositiveAmountToAdjust.compareTo(BigDecimal.ZERO) > 0) {
                         final InvoiceItem adjItem = new RefundAdjInvoiceItem(invoice.getId(), invoice.getAccountId(), context.getCreatedDate().toLocalDate(), requestedPositiveAmountToAdjust.negate(), invoice.getCurrency());
                         transInvoiceItemDao.create(adjItem, context);
-                        final Long adjItemRecordId = transInvoiceItemDao.getRecordId(adjItem.getId().toString(), context);
-                        audits.add(new EntityAudit(TableName.INVOICE_ITEMS, adjItemRecordId, ChangeType.INSERT));
                     }
                 } else if (isInvoiceAdjusted) {
                     // Invoice item adjustment
                     for (final UUID invoiceItemId : invoiceItemIdsWithAmounts.keySet()) {
                         final BigDecimal adjAmount = invoiceItemIdsWithAmounts.get(invoiceItemId);
-                        final InvoiceItem item = createAdjustmentItem(transInvoiceDao, invoice.getId(), invoiceItemId, adjAmount,
+                        final InvoiceItem item = createAdjustmentItem(entitySqlDaoWrapperFactory, invoice.getId(), invoiceItemId, adjAmount,
                                                                       invoice.getCurrency(), context.getCreatedDate().toLocalDate(),
                                                                       context);
                         transInvoiceItemDao.create(item, context);
-                        final Long itemRecordId = transInvoiceItemDao.getRecordId(item.getId().toString(), context);
-                        audits.add(new EntityAudit(TableName.INVOICE_ITEMS, itemRecordId, ChangeType.INSERT));
                     }
                 }
 
                 // Notify the bus since the balance of the invoice changed
                 notifyBusOfInvoiceAdjustment(transactional, invoice.getId(), invoice.getAccountId(), context.getUserToken(), context);
 
-                // Save audit logs
-                transactional.insertAuditFromTransaction(audits, context);
-
                 return refund;
             }
         });
@@ -414,14 +398,14 @@ public class AuditedInvoiceDao implements InvoiceDao {
      * item.
      *
      * @param invoiceId                     original invoice id
-     * @param transInvoiceDao               the transactional InvoiceSqlDao
+     * @param entitySqlDaoWrapperFactory    the EntitySqlDaoWrapperFactory from the current transaction
      * @param invoiceItemIdsWithNullAmounts the original mapping between invoice item ids and amount to refund (contains null)
      * @param context                       the tenant context
      * @return the final mapping between invoice item ids and amount to refund
      * @throws InvoiceApiException
      */
     private Map<UUID, BigDecimal> computeItemAdjustments(final String invoiceId,
-                                                         final InvoiceSqlDao transInvoiceDao,
+                                                         final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory,
                                                          final Map<UUID, BigDecimal> invoiceItemIdsWithNullAmounts,
                                                          final InternalTenantContext context) throws InvoiceApiException {
         // Populate the missing amounts for individual items, if needed
@@ -431,9 +415,9 @@ public class AuditedInvoiceDao implements InvoiceDao {
         }
 
         // Retrieve invoice before the Refund
-        final Invoice invoice = transInvoiceDao.getById(invoiceId, context);
+        final Invoice invoice = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class).getById(invoiceId, context);
         if (invoice != null) {
-            populateChildren(invoice, transInvoiceDao, context);
+            populateChildren(invoice, entitySqlDaoWrapperFactory, context);
         } else {
             throw new IllegalStateException("Invoice shouldn't be null for id " + invoiceId);
         }
@@ -511,9 +495,11 @@ public class AuditedInvoiceDao implements InvoiceDao {
     @Override
     public InvoicePayment postChargeback(final UUID invoicePaymentId, final BigDecimal amount, final InternalCallContext context) throws InvoiceApiException {
 
-        return invoicePaymentSqlDao.inTransaction(new Transaction<InvoicePayment, InvoicePaymentSqlDao>() {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<InvoicePayment>() {
             @Override
-            public InvoicePayment inTransaction(final InvoicePaymentSqlDao transactional, final TransactionStatus status) throws Exception {
+            public InvoicePayment inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoicePaymentSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoicePaymentSqlDao.class);
+
                 final BigDecimal maxChargedBackAmount = getRemainingAmountPaidFromTransaction(invoicePaymentId, transactional, context);
                 final BigDecimal requestedChargedBackAmout = (amount == null) ? maxChargedBackAmount : amount;
                 if (requestedChargedBackAmout.compareTo(BigDecimal.ZERO) <= 0) {
@@ -523,7 +509,7 @@ public class AuditedInvoiceDao implements InvoiceDao {
                     throw new InvoiceApiException(ErrorCode.CHARGE_BACK_AMOUNT_TOO_HIGH, requestedChargedBackAmout, maxChargedBackAmount);
                 }
 
-                final InvoicePayment payment = invoicePaymentSqlDao.getById(invoicePaymentId.toString(), context);
+                final InvoicePayment payment = entitySqlDaoWrapperFactory.become(InvoicePaymentSqlDao.class).getById(invoicePaymentId.toString(), context);
                 if (payment == null) {
                     throw new InvoiceApiException(ErrorCode.INVOICE_PAYMENT_NOT_FOUND, invoicePaymentId.toString());
                 } else {
@@ -531,11 +517,6 @@ public class AuditedInvoiceDao implements InvoiceDao {
                             payment.getInvoiceId(), context.getCreatedDate(), requestedChargedBackAmout.negate(), payment.getCurrency(), null, payment.getId());
                     transactional.create(chargeBack, context);
 
-                    // Add audit
-                    final Long recordId = transactional.getRecordId(chargeBack.getId().toString(), context);
-                    final EntityAudit audit = new EntityAudit(TableName.INVOICE_PAYMENTS, recordId, ChangeType.INSERT);
-                    transactional.insertAuditFromTransaction(audit, context);
-
                     // Notify the bus since the balance of the invoice changed
                     final UUID accountId = transactional.getAccountIdFromInvoicePaymentId(chargeBack.getId().toString(), context);
                     notifyBusOfInvoiceAdjustment(transactional, payment.getInvoiceId(), accountId, context.getUserToken(), context);
@@ -590,18 +571,16 @@ public class AuditedInvoiceDao implements InvoiceDao {
     public InvoiceItem insertExternalCharge(final UUID accountId, @Nullable final UUID invoiceId, @Nullable final UUID bundleId, final String description,
                                             final BigDecimal amount, final LocalDate effectiveDate, final Currency currency, final InternalCallContext context)
             throws InvoiceApiException {
-        return invoiceSqlDao.inTransaction(new Transaction<InvoiceItem, InvoiceSqlDao>() {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<InvoiceItem>() {
             @Override
-            public InvoiceItem inTransaction(final InvoiceSqlDao transactional, final TransactionStatus status) throws Exception {
-                final List<EntityAudit> audits = new ArrayList<EntityAudit>();
+            public InvoiceItem inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
 
                 UUID invoiceIdForExternalCharge = invoiceId;
                 // Create an invoice for that external charge if it doesn't exist
                 if (invoiceIdForExternalCharge == null) {
                     final Invoice invoiceForExternalCharge = new DefaultInvoice(accountId, effectiveDate, effectiveDate, currency);
                     transactional.create(invoiceForExternalCharge, context);
-                    final Long invoiceRecordId = transactional.getRecordId(invoiceForExternalCharge.getId().toString(), context);
-                    audits.add(new EntityAudit(TableName.INVOICES, invoiceRecordId, ChangeType.INSERT));
 
                     invoiceIdForExternalCharge = invoiceForExternalCharge.getId();
                 }
@@ -610,33 +589,26 @@ public class AuditedInvoiceDao implements InvoiceDao {
                         bundleId, description,
                         effectiveDate, amount, currency);
 
-                final InvoiceItemSqlDao transInvoiceItemDao = transactional.become(InvoiceItemSqlDao.class);
+                final InvoiceItemSqlDao transInvoiceItemDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
                 transInvoiceItemDao.create(externalCharge, context);
-                final Long invoiceItemRecordId = transInvoiceItemDao.getRecordId(externalCharge.getId().toString(), context);
-                audits.add(new EntityAudit(TableName.INVOICE_ITEMS, invoiceItemRecordId, ChangeType.INSERT));
 
                 // At this point, reread the invoice and figure out if we need to consume some of the CBA
                 final Invoice invoice = transactional.getById(invoiceIdForExternalCharge.toString(), context);
                 if (invoice == null) {
                     throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceIdForExternalCharge);
                 }
-                populateChildren(invoice, transactional, context);
+                populateChildren(invoice, entitySqlDaoWrapperFactory, context);
 
-                final BigDecimal accountCbaAvailable = getAccountCBAFromTransaction(invoice.getAccountId(), transactional, context);
+                final BigDecimal accountCbaAvailable = getAccountCBAFromTransaction(invoice.getAccountId(), entitySqlDaoWrapperFactory, context);
                 if (accountCbaAvailable.compareTo(BigDecimal.ZERO) > 0 && invoice.getBalance().compareTo(BigDecimal.ZERO) > 0) {
                     final BigDecimal cbaAmountToConsume = accountCbaAvailable.compareTo(invoice.getBalance()) > 0 ? invoice.getBalance().negate() : accountCbaAvailable.negate();
                     final InvoiceItem cbaAdjItem = new CreditBalanceAdjInvoiceItem(invoice.getId(), invoice.getAccountId(), context.getCreatedDate().toLocalDate(), cbaAmountToConsume, invoice.getCurrency());
                     transInvoiceItemDao.create(cbaAdjItem, context);
-                    final Long cbaAdjItemRecordId = transInvoiceItemDao.getRecordId(cbaAdjItem.getId().toString(), context);
-                    audits.add(new EntityAudit(TableName.INVOICE_ITEMS, cbaAdjItemRecordId, ChangeType.INSERT));
                 }
 
                 // Notify the bus since the balance of the invoice changed
                 notifyBusOfInvoiceAdjustment(transactional, invoiceId, accountId, context.getUserToken(), context);
 
-                // Save audit logs
-                transactional.insertAuditFromTransaction(audits, context);
-
                 return externalCharge;
             }
         });
@@ -650,32 +622,27 @@ public class AuditedInvoiceDao implements InvoiceDao {
     @Override
     public InvoiceItem insertCredit(final UUID accountId, @Nullable final UUID invoiceId, final BigDecimal positiveCreditAmount,
                                     final LocalDate effectiveDate, final Currency currency, final InternalCallContext context) {
-        return invoiceSqlDao.inTransaction(new Transaction<InvoiceItem, InvoiceSqlDao>() {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<InvoiceItem>() {
             @Override
-            public InvoiceItem inTransaction(final InvoiceSqlDao transactional, final TransactionStatus status) throws Exception {
-                final List<EntityAudit> audits = new ArrayList<EntityAudit>();
+            public InvoiceItem inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
 
                 UUID invoiceIdForCredit = invoiceId;
                 // Create an invoice for that credit if it doesn't exist
                 if (invoiceIdForCredit == null) {
                     final Invoice invoiceForCredit = new DefaultInvoice(accountId, effectiveDate, effectiveDate, currency);
                     transactional.create(invoiceForCredit, context);
-                    final Long invoiceForCreditRecordId = transactional.getRecordId(invoiceForCredit.getId().toString(), context);
-                    audits.add(new EntityAudit(TableName.INVOICES, invoiceForCreditRecordId, ChangeType.INSERT));
 
                     invoiceIdForCredit = invoiceForCredit.getId();
                 }
 
                 // Note! The amount is negated here!
                 final InvoiceItem credit = new CreditAdjInvoiceItem(invoiceIdForCredit, accountId, effectiveDate, positiveCreditAmount.negate(), currency);
-                insertItemAndAddCBAIfNeeded(transactional, credit, audits, context);
+                insertItemAndAddCBAIfNeeded(entitySqlDaoWrapperFactory, credit, context);
 
                 // Notify the bus since the balance of the invoice changed
                 notifyBusOfInvoiceAdjustment(transactional, invoiceId, accountId, context.getUserToken(), context);
 
-                // Save audit logs
-                transactional.insertAuditFromTransaction(audits, context);
-
                 return credit;
             }
         });
@@ -685,18 +652,15 @@ public class AuditedInvoiceDao implements InvoiceDao {
     public InvoiceItem insertInvoiceItemAdjustment(final UUID accountId, final UUID invoiceId, final UUID invoiceItemId,
                                                    final LocalDate effectiveDate, @Nullable final BigDecimal positiveAdjAmount,
                                                    @Nullable final Currency currency, final InternalCallContext context) {
-        return invoiceSqlDao.inTransaction(new Transaction<InvoiceItem, InvoiceSqlDao>() {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<InvoiceItem>() {
             @Override
-            public InvoiceItem inTransaction(final InvoiceSqlDao transactional, final TransactionStatus status) throws Exception {
-                final List<EntityAudit> audits = new ArrayList<EntityAudit>();
-                final InvoiceItem invoiceItemAdjustment = createAdjustmentItem(transactional, invoiceId, invoiceItemId, positiveAdjAmount,
+            public InvoiceItem inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+                final InvoiceItem invoiceItemAdjustment = createAdjustmentItem(entitySqlDaoWrapperFactory, invoiceId, invoiceItemId, positiveAdjAmount,
                                                                                currency, effectiveDate, context);
-                insertItemAndAddCBAIfNeeded(transactional, invoiceItemAdjustment, audits, context);
+                insertItemAndAddCBAIfNeeded(entitySqlDaoWrapperFactory, invoiceItemAdjustment, context);
                 notifyBusOfInvoiceAdjustment(transactional, invoiceId, accountId, context.getUserToken(), context);
 
-                // Save audit logs
-                transactional.insertAuditFromTransaction(audits, context);
-
                 return invoiceItemAdjustment;
             }
         });
@@ -704,10 +668,10 @@ public class AuditedInvoiceDao implements InvoiceDao {
 
     @Override
     public void deleteCBA(final UUID accountId, final UUID invoiceId, final UUID invoiceItemId, final InternalCallContext context) throws InvoiceApiException {
-        invoiceSqlDao.inTransaction(new Transaction<Void, InvoiceSqlDao>() {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
-            public Void inTransaction(final InvoiceSqlDao transactional, final TransactionStatus status) throws Exception {
-                final List<EntityAudit> audits = new ArrayList<EntityAudit>();
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
 
                 // Retrieve the invoice and make sure it belongs to the right account
                 final Invoice invoice = transactional.getById(invoiceId.toString(), context);
@@ -716,7 +680,7 @@ public class AuditedInvoiceDao implements InvoiceDao {
                 }
 
                 // Retrieve the invoice item and make sure it belongs to the right invoice
-                final InvoiceItemSqlDao invoiceItemSqlDao = transactional.become(InvoiceItemSqlDao.class);
+                final InvoiceItemSqlDao invoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
                 final InvoiceItem cbaItem = invoiceItemSqlDao.getById(invoiceItemId.toString(), context);
                 if (cbaItem == null || !cbaItem.getInvoiceId().equals(invoice.getId())) {
                     throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_NOT_FOUND, invoiceItemId);
@@ -726,18 +690,16 @@ public class AuditedInvoiceDao implements InvoiceDao {
                 final InvoiceItem cbaAdjItem = new CreditBalanceAdjInvoiceItem(invoice.getId(), invoice.getAccountId(), context.getCreatedDate().toLocalDate(),
                         cbaItem.getId(), cbaItem.getAmount().negate(), cbaItem.getCurrency());
                 invoiceItemSqlDao.create(cbaAdjItem, context);
-                final Long cbaAdjItemRecordId = invoiceItemSqlDao.getRecordId(cbaAdjItem.getId().toString(), context);
-                audits.add(new EntityAudit(TableName.INVOICE_ITEMS, cbaAdjItemRecordId, ChangeType.INSERT));
 
                 // Verify the final invoice balance is not negative
-                populateChildren(invoice, transactional, context);
+                populateChildren(invoice, entitySqlDaoWrapperFactory, context);
                 if (invoice.getBalance().compareTo(BigDecimal.ZERO) < 0) {
                     throw new InvoiceApiException(ErrorCode.INVOICE_WOULD_BE_NEGATIVE);
                 }
 
                 // If there is more account credit than CBA we adjusted, we're done.
                 // Otherwise, we need to find further invoices on which this credit was consumed
-                final BigDecimal accountCBA = getAccountCBAFromTransaction(accountId, invoiceSqlDao, context);
+                final BigDecimal accountCBA = getAccountCBAFromTransaction(accountId, entitySqlDaoWrapperFactory, context);
                 if (accountCBA.compareTo(BigDecimal.ZERO) < 0) {
                     if (accountCBA.compareTo(cbaItem.getAmount().negate()) < 0) {
                         throw new IllegalStateException("The account balance can't be lower than the amount adjusted");
@@ -745,7 +707,7 @@ public class AuditedInvoiceDao implements InvoiceDao {
                     final List<Invoice> invoicesFollowing = transactional.getInvoicesByAccountAfterDate(accountId.toString(),
                                                                                                         invoice.getInvoiceDate().toDateTimeAtStartOfDay().toDate(),
                                                                                                         context);
-                    populateChildren(invoicesFollowing, transactional, context);
+                    populateChildren(invoicesFollowing, entitySqlDaoWrapperFactory, context);
 
                     // The remaining amount to adjust (i.e. the amount of credits used on following invoices)
                     // is the current account CBA balance (minus the sign)
@@ -785,8 +747,6 @@ public class AuditedInvoiceDao implements InvoiceDao {
                         final InvoiceItem nextCBAAdjItem = new CreditBalanceAdjInvoiceItem(invoiceFollowing.getId(), invoice.getAccountId(), context.getCreatedDate().toLocalDate(),
                                 cbaItem.getId(), positiveCBAAdjItemAmount, cbaItem.getCurrency());
                         invoiceItemSqlDao.create(nextCBAAdjItem, context);
-                        final Long nextCBAAdjItemRecordId = invoiceItemSqlDao.getRecordId(nextCBAAdjItem.getId().toString(), context);
-                        audits.add(new EntityAudit(TableName.INVOICE_ITEMS, nextCBAAdjItemRecordId, ChangeType.INSERT));
 
                         if (positiveRemainderToAdjust.compareTo(BigDecimal.ZERO) == 0) {
                             break;
@@ -794,9 +754,6 @@ public class AuditedInvoiceDao implements InvoiceDao {
                     }
                 }
 
-                // Save audit logs
-                transactional.insertAuditFromTransaction(audits, context);
-
                 return null;
             }
         });
@@ -817,11 +774,11 @@ public class AuditedInvoiceDao implements InvoiceDao {
      * @param currency          the currency of the amount. Pass null to default to the original currency used
      * @return the adjustment item
      */
-    private InvoiceItem createAdjustmentItem(final InvoiceSqlDao transactional, final UUID invoiceId, final UUID invoiceItemId,
+    private InvoiceItem createAdjustmentItem(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final UUID invoiceId, final UUID invoiceItemId,
                                              final BigDecimal positiveAdjAmount, final Currency currency,
                                              final LocalDate effectiveDate, final InternalTenantContext context) throws InvoiceApiException {
         // First, retrieve the invoice item in question
-        final InvoiceItemSqlDao invoiceItemSqlDao = transactional.become(InvoiceItemSqlDao.class);
+        final InvoiceItemSqlDao invoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
         final InvoiceItem invoiceItemToBeAdjusted = invoiceItemSqlDao.getById(invoiceItemId.toString(), context);
         if (invoiceItemToBeAdjusted == null) {
             throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_NOT_FOUND, invoiceItemId);
@@ -845,72 +802,68 @@ public class AuditedInvoiceDao implements InvoiceDao {
     /**
      * Create an invoice item and adjust the invoice with a CBA item if the new invoice balance is negative.
      *
-     * @param transactional the InvoiceSqlDao
+     * @param entitySqlDaoWrapperFactory the EntitySqlDaoWrapperFactory from the current transaction
      * @param item          the invoice item to create
-     * @param audits        the audits to populate
      * @param context       the call context
      */
-    private void insertItemAndAddCBAIfNeeded(final InvoiceSqlDao transactional, final InvoiceItem item,
-                                             final List<EntityAudit> audits, final InternalCallContext context) {
-        final InvoiceItemSqlDao transInvoiceItemDao = transactional.become(InvoiceItemSqlDao.class);
+    private void insertItemAndAddCBAIfNeeded(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InvoiceItem item,
+                                             final InternalCallContext context) {
+        final InvoiceItemSqlDao transInvoiceItemDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
         transInvoiceItemDao.create(item, context);
 
-        final Long invoiceItemRecordId = transInvoiceItemDao.getRecordId(item.getId().toString(), context);
-        audits.add(new EntityAudit(TableName.INVOICE_ITEMS, invoiceItemRecordId, ChangeType.INSERT));
-
-        addCBAIfNeeded(transactional, item.getInvoiceId(), audits, context);
+        addCBAIfNeeded(entitySqlDaoWrapperFactory, item.getInvoiceId(), context);
     }
 
     /**
      * Adjust the invoice with a CBA item if the new invoice balance is negative.
      *
-     * @param transactional the InvoiceSqlDao
+     * @param entitySqlDaoWrapperFactory the EntitySqlDaoWrapperFactory from the current transaction
      * @param invoiceId     the invoice id to adjust
-     * @param audits        the audits to populate
      * @param context       the call context
      */
-    private void addCBAIfNeeded(final InvoiceSqlDao transactional, final UUID invoiceId,
-                                final List<EntityAudit> audits, final InternalCallContext context) {
-        final Invoice invoice = transactional.getById(invoiceId.toString(), context);
+    private void addCBAIfNeeded(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory,
+                                final UUID invoiceId,
+                                final InternalCallContext context) {
+        final Invoice invoice = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class).getById(invoiceId.toString(), context);
         if (invoice != null) {
-            populateChildren(invoice, transactional, context);
+            populateChildren(invoice, entitySqlDaoWrapperFactory, context);
         } else {
             throw new IllegalStateException("Invoice shouldn't be null for this item at this stage " + invoiceId);
         }
 
         // If invoice balance becomes negative we add some CBA item
         if (invoice.getBalance().compareTo(BigDecimal.ZERO) < 0) {
-            final InvoiceItemSqlDao transInvoiceItemDao = transactional.become(InvoiceItemSqlDao.class);
+            final InvoiceItemSqlDao transInvoiceItemDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
             final InvoiceItem cbaAdjItem = new CreditBalanceAdjInvoiceItem(invoice.getId(), invoice.getAccountId(), context.getCreatedDate().toLocalDate(),
                     invoice.getBalance().negate(), invoice.getCurrency());
             transInvoiceItemDao.create(cbaAdjItem, context);
-            final Long cbaAdjItemRecordId = transInvoiceItemDao.getRecordId(cbaAdjItem.getId().toString(), context);
-            audits.add(new EntityAudit(TableName.INVOICE_ITEMS, cbaAdjItemRecordId, ChangeType.INSERT));
         }
     }
 
-    private BigDecimal getAccountCBAFromTransaction(final UUID accountId, final InvoiceSqlDao transactional, final InternalTenantContext context) {
+    private BigDecimal getAccountCBAFromTransaction(final UUID accountId,
+                                                    final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory,
+                                                    final InternalTenantContext context) {
         BigDecimal cba = BigDecimal.ZERO;
-        final List<Invoice> invoices = getAllInvoicesByAccountFromTransaction(accountId, transactional, context);
+        final List<Invoice> invoices = getAllInvoicesByAccountFromTransaction(accountId, entitySqlDaoWrapperFactory, context);
         for (final Invoice cur : invoices) {
             cba = cba.add(cur.getCBAAmount());
         }
         return cba;
     }
 
-    private void populateChildren(final Invoice invoice, final InvoiceSqlDao invoiceSqlDao, final InternalTenantContext context) {
-        getInvoiceItemsWithinTransaction(invoice, invoiceSqlDao, context);
-        getInvoicePaymentsWithinTransaction(invoice, invoiceSqlDao, context);
+    private void populateChildren(final Invoice invoice, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
+        getInvoiceItemsWithinTransaction(invoice, entitySqlDaoWrapperFactory, context);
+        getInvoicePaymentsWithinTransaction(invoice, entitySqlDaoWrapperFactory, context);
     }
 
-    private void populateChildren(final List<Invoice> invoices, final InvoiceSqlDao invoiceSqlDao, final InternalTenantContext context) {
-        getInvoiceItemsWithinTransaction(invoices, invoiceSqlDao, context);
-        getInvoicePaymentsWithinTransaction(invoices, invoiceSqlDao, context);
+    private void populateChildren(final List<Invoice> invoices, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
+        getInvoiceItemsWithinTransaction(invoices, entitySqlDaoWrapperFactory, context);
+        getInvoicePaymentsWithinTransaction(invoices, entitySqlDaoWrapperFactory, context);
     }
 
-    private List<Invoice> getAllInvoicesByAccountFromTransaction(final UUID accountId, final InvoiceSqlDao transactional, final InternalTenantContext context) {
-        final List<Invoice> invoices = transactional.getAllInvoicesByAccount(accountId.toString(), context);
-        populateChildren(invoices, transactional, context);
+    private List<Invoice> getAllInvoicesByAccountFromTransaction(final UUID accountId, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
+        final List<Invoice> invoices = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class).getAllInvoicesByAccount(accountId.toString(), context);
+        populateChildren(invoices, entitySqlDaoWrapperFactory, context);
         return invoices;
     }
 
@@ -919,28 +872,28 @@ public class AuditedInvoiceDao implements InvoiceDao {
         return amount == null ? BigDecimal.ZERO : amount;
     }
 
-    private void getInvoiceItemsWithinTransaction(final List<Invoice> invoices, final InvoiceSqlDao invoiceDao, final InternalTenantContext context) {
+    private void getInvoiceItemsWithinTransaction(final List<Invoice> invoices, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
         for (final Invoice invoice : invoices) {
-            getInvoiceItemsWithinTransaction(invoice, invoiceDao, context);
+            getInvoiceItemsWithinTransaction(invoice, entitySqlDaoWrapperFactory, context);
         }
     }
 
-    private void getInvoiceItemsWithinTransaction(final Invoice invoice, final InvoiceSqlDao transactional, final InternalTenantContext context) {
+    private void getInvoiceItemsWithinTransaction(final Invoice invoice, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
         final String invoiceId = invoice.getId().toString();
 
-        final InvoiceItemSqlDao transInvoiceItemSqlDao = transactional.become(InvoiceItemSqlDao.class);
+        final InvoiceItemSqlDao transInvoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
         final List<InvoiceItem> items = transInvoiceItemSqlDao.getInvoiceItemsByInvoice(invoiceId, context);
         invoice.addInvoiceItems(items);
     }
 
-    private void getInvoicePaymentsWithinTransaction(final List<Invoice> invoices, final InvoiceSqlDao invoiceDao, final InternalTenantContext context) {
+    private void getInvoicePaymentsWithinTransaction(final List<Invoice> invoices, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
         for (final Invoice invoice : invoices) {
-            getInvoicePaymentsWithinTransaction(invoice, invoiceDao, context);
+            getInvoicePaymentsWithinTransaction(invoice, entitySqlDaoWrapperFactory, context);
         }
     }
 
-    private void getInvoicePaymentsWithinTransaction(final Invoice invoice, final InvoiceSqlDao invoiceSqlDao, final InternalTenantContext context) {
-        final InvoicePaymentSqlDao invoicePaymentSqlDao = invoiceSqlDao.become(InvoicePaymentSqlDao.class);
+    private void getInvoicePaymentsWithinTransaction(final Invoice invoice, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
+        final InvoicePaymentSqlDao invoicePaymentSqlDao = entitySqlDaoWrapperFactory.become(InvoicePaymentSqlDao.class);
         final String invoiceId = invoice.getId().toString();
         final List<InvoicePayment> invoicePayments = invoicePaymentSqlDao.getPaymentsForInvoice(invoiceId, context);
         invoice.addPayments(invoicePayments);
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java
index ce7799f..066ff23 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java
@@ -53,10 +53,12 @@ import com.ning.billing.invoice.model.ItemAdjInvoiceItem;
 import com.ning.billing.invoice.model.RecurringInvoiceItem;
 import com.ning.billing.invoice.model.RefundAdjInvoiceItem;
 import com.ning.billing.invoice.model.RepairAdjInvoiceItem;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.callcontext.InternalTenantContextBinder;
 import com.ning.billing.util.dao.MapperBase;
+import com.ning.billing.util.entity.dao.Audited;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
 
 @ExternalizedSqlViaStringTemplate3()
@@ -81,10 +83,12 @@ public interface InvoiceItemSqlDao extends EntitySqlDao<InvoiceItem> {
 
     @Override
     @SqlUpdate
+    @Audited(ChangeType.INSERT)
     void create(@InvoiceItemBinder final InvoiceItem invoiceItem,
                 @InternalTenantContextBinder final InternalCallContext context);
 
     @SqlBatch(transactional = false)
+    @Audited(ChangeType.INSERT)
     void batchCreateFromTransaction(@InvoiceItemBinder final List<InvoiceItem> items,
                                     @InternalTenantContextBinder final InternalCallContext context);
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
index 0a1c533..ba17e33 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
@@ -38,8 +38,6 @@ import org.skife.jdbi.v2.sqlobject.SqlBatch;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
@@ -47,18 +45,19 @@ import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.invoice.api.InvoicePayment.InvoicePaymentType;
 import com.ning.billing.invoice.model.DefaultInvoicePayment;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.dao.AuditSqlDao;
 import com.ning.billing.util.dao.BinderBase;
 import com.ning.billing.util.dao.MapperBase;
 import com.ning.billing.util.dao.UuidMapper;
+import com.ning.billing.util.entity.dao.Audited;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
 
 @ExternalizedSqlViaStringTemplate3
 @RegisterMapper(InvoicePaymentSqlDao.InvoicePaymentMapper.class)
-public interface InvoicePaymentSqlDao extends EntitySqlDao<InvoicePayment>, Transactional<InvoicePaymentSqlDao>, AuditSqlDao, Transmogrifier {
+public interface InvoicePaymentSqlDao extends EntitySqlDao<InvoicePayment> {
 
     @SqlQuery
     List<Long> getRecordIds(@Bind("invoiceId") final String invoiceId,
@@ -74,10 +73,12 @@ public interface InvoicePaymentSqlDao extends EntitySqlDao<InvoicePayment>, Tran
 
     @Override
     @SqlUpdate
+    @Audited(ChangeType.INSERT)
     public void create(@InvoicePaymentBinder final InvoicePayment invoicePayment,
                        @InternalTenantContextBinder final InternalCallContext context);
 
     @SqlBatch(transactional = false)
+    @Audited(ChangeType.INSERT)
     void batchCreateFromTransaction(@InvoicePaymentBinder final List<InvoicePayment> items,
                                     @InternalTenantContextBinder final InternalCallContext context);
 
@@ -94,6 +95,7 @@ public interface InvoicePaymentSqlDao extends EntitySqlDao<InvoicePayment>, Tran
                                           @InternalTenantContextBinder final InternalTenantContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     void notifyOfPayment(@InvoicePaymentBinder final InvoicePayment invoicePayment,
                          @InternalTenantContextBinder final InternalCallContext context);
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
index 6358101..25a861b 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
@@ -38,29 +38,28 @@ import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
-import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.model.DefaultInvoice;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.dao.AuditSqlDao;
 import com.ning.billing.util.dao.MapperBase;
 import com.ning.billing.util.dao.UuidMapper;
+import com.ning.billing.util.entity.dao.Audited;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
 
 @ExternalizedSqlViaStringTemplate3()
-@RegisterMapper(InvoiceSqlDao.InvoiceMapper.class)
-public interface InvoiceSqlDao extends EntitySqlDao<Invoice>, AuditSqlDao, Transactional<InvoiceSqlDao>, Transmogrifier, CloseMe {
+@RegisterMapper({InvoiceSqlDao.InvoiceMapper.class, UuidMapper.class})
+public interface InvoiceSqlDao extends EntitySqlDao<Invoice> {
 
     @Override
     @SqlUpdate
+    @Audited(ChangeType.INSERT)
     void create(@InvoiceBinder Invoice invoice,
                 @InternalTenantContextBinder final InternalCallContext context);
 
@@ -82,7 +81,6 @@ public interface InvoiceSqlDao extends EntitySqlDao<Invoice>, AuditSqlDao, Trans
                                             @InternalTenantContextBinder final InternalTenantContext context);
 
     @SqlQuery
-    @RegisterMapper(UuidMapper.class)
     UUID getInvoiceIdByPaymentId(@Bind("paymentId") final String paymentId,
                                  @InternalTenantContextBinder final InternalTenantContext context);
 
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/TestDefaultInvoiceDao.java b/invoice/src/test/java/com/ning/billing/invoice/dao/TestDefaultInvoiceDao.java
index b1c6ddc..073c8e5 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/TestDefaultInvoiceDao.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/TestDefaultInvoiceDao.java
@@ -37,6 +37,7 @@ import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.invoice.notification.NextBillingDatePoster;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
 import com.ning.billing.util.svcsapi.bus.InternalBus;
 import com.ning.billing.util.tag.dao.MockTagDao;
 import com.ning.billing.util.tag.dao.MockTagDefinitionDao;
@@ -56,7 +57,7 @@ public class TestDefaultInvoiceDao extends InvoiceTestSuite {
         invoiceSqlDao = Mockito.mock(InvoiceSqlDao.class);
         Mockito.when(idbi.onDemand(InvoiceSqlDao.class)).thenReturn(invoiceSqlDao);
         Mockito.when(invoiceSqlDao.getById(Mockito.anyString(), Mockito.<InternalTenantContext>any())).thenReturn(Mockito.mock(Invoice.class));
-        Mockito.when(invoiceSqlDao.inTransaction(Mockito.<Transaction<Void, InvoiceSqlDao>>any())).thenAnswer(new Answer() {
+        Mockito.when(invoiceSqlDao.inTransaction(Mockito.<Transaction<Void, EntitySqlDao<Invoice>>>any())).thenAnswer(new Answer() {
             @Override
             public Object answer(final InvocationOnMock invocation) {
                 final Object[] args = invocation.getArguments();
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPayment.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPayment.java
index cde2b82..6427414 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPayment.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPayment.java
@@ -189,6 +189,16 @@ public class DefaultPayment extends EntityBase implements Payment {
                     }
 
                     @Override
+                    public DateTime getCreatedDate() {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    @Override
+                    public DateTime getUpdatedDate() {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    @Override
                     public String getGatewayErrorCode() {
                         return input.getGatewayErrorCode();
                     }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java
index 042d283..b08d704 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java
@@ -16,30 +16,28 @@
 
 package com.ning.billing.payment.dao;
 
-import java.math.BigDecimal;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.UUID;
 
 import org.skife.jdbi.v2.IDBI;
-import org.skife.jdbi.v2.Transaction;
-import org.skife.jdbi.v2.TransactionStatus;
 
 import com.ning.billing.payment.api.PaymentStatus;
 import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
 import com.ning.billing.payment.retry.PluginFailureRetryService.PluginFailureRetryServiceScheduler;
-import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.dao.EntityAudit;
-import com.ning.billing.util.dao.EntityHistory;
-import com.ning.billing.util.dao.TableName;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 
 import com.google.inject.Inject;
 
 public class AuditedPaymentDao implements PaymentDao {
 
+    private final EntitySqlDaoTransactionalJdbiWrapper transactionalSqlDao;
     private final PaymentSqlDao paymentSqlDao;
     private final PaymentAttemptSqlDao paymentAttemptSqlDao;
     private final PaymentMethodSqlDao paymentMethodSqlDao;
@@ -51,18 +49,20 @@ public class AuditedPaymentDao implements PaymentDao {
         this.paymentAttemptSqlDao = dbi.onDemand(PaymentAttemptSqlDao.class);
         this.paymentMethodSqlDao = dbi.onDemand(PaymentMethodSqlDao.class);
         this.refundSqlDao = dbi.onDemand(RefundSqlDao.class);
+        this.transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi);
     }
 
     @Override
     public PaymentAttemptModelDao insertNewAttemptForPayment(final UUID paymentId, final PaymentAttemptModelDao attempt, final InternalCallContext context) {
-
-        return paymentAttemptSqlDao.inTransaction(new Transaction<PaymentAttemptModelDao, PaymentAttemptSqlDao>() {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<PaymentAttemptModelDao>() {
             @Override
-            public PaymentAttemptModelDao inTransaction(final PaymentAttemptSqlDao transactional, final TransactionStatus status)
-                    throws Exception {
-                final PaymentAttemptModelDao savedAttempt = insertPaymentAttemptFromTransaction(attempt, context, transactional);
-                final PaymentSqlDao transPaymentSqlDao = transactional.become(PaymentSqlDao.class);
-                updatePaymentAmountFromTransaction(paymentId, savedAttempt.getRequestedAmount(), context, transPaymentSqlDao);
+            public PaymentAttemptModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final PaymentAttemptSqlDao transactional = entitySqlDaoWrapperFactory.become(PaymentAttemptSqlDao.class);
+                transactional.insertPaymentAttempt(attempt, context);
+                final PaymentAttemptModelDao savedAttempt = transactional.getPaymentAttempt(attempt.getId().toString(), context);
+
+                entitySqlDaoWrapperFactory.become(PaymentSqlDao.class).updatePaymentAmount(paymentId.toString(), savedAttempt.getRequestedAmount(), context);
+
                 return savedAttempt;
             }
         });
@@ -70,54 +70,23 @@ public class AuditedPaymentDao implements PaymentDao {
 
     @Override
     public PaymentModelDao insertPaymentWithAttempt(final PaymentModelDao payment, final PaymentAttemptModelDao attempt, final InternalCallContext context) {
-
-        return paymentSqlDao.inTransaction(new Transaction<PaymentModelDao, PaymentSqlDao>() {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<PaymentModelDao>() {
 
             @Override
-            public PaymentModelDao inTransaction(final PaymentSqlDao transactional,
-                                                 final TransactionStatus status) throws Exception {
-                final PaymentModelDao result = insertPaymentFromTransaction(payment, context, transactional);
-                final PaymentAttemptSqlDao transactionalAttempt = transactional.become(PaymentAttemptSqlDao.class);
-                insertPaymentAttemptFromTransaction(attempt, context, transactionalAttempt);
-                return result;
-            }
-        });
-    }
+            public PaymentModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final PaymentSqlDao transactional = entitySqlDaoWrapperFactory.become(PaymentSqlDao.class);
+                transactional.insertPayment(payment, context);
 
-    private PaymentModelDao insertPaymentFromTransaction(final PaymentModelDao payment, final InternalCallContext context, final PaymentSqlDao transactional) {
-        transactional.insertPayment(payment, context);
-        final PaymentModelDao savedPayment = transactional.getPayment(payment.getId().toString(), context);
-        final Long recordId = transactional.getRecordId(savedPayment.getId().toString(), context);
-        final EntityHistory<PaymentModelDao> history = new EntityHistory<PaymentModelDao>(savedPayment.getId(), recordId, savedPayment, ChangeType.INSERT);
-        transactional.insertHistoryFromTransaction(history, context);
-
-        final Long historyRecordId = transactional.getHistoryRecordId(recordId, context);
-        final EntityAudit audit = new EntityAudit(TableName.PAYMENT_HISTORY, historyRecordId, ChangeType.INSERT);
-        transactional.insertAuditFromTransaction(audit, context);
-        return savedPayment;
-    }
+                entitySqlDaoWrapperFactory.become(PaymentAttemptSqlDao.class).insertPaymentAttempt(attempt, context);
 
-    private PaymentAttemptModelDao insertPaymentAttemptFromTransaction(final PaymentAttemptModelDao attempt, final InternalCallContext context, final PaymentAttemptSqlDao transactional) {
-        transactional.insertPaymentAttempt(attempt, context);
-        final PaymentAttemptModelDao savedAttempt = transactional.getPaymentAttempt(attempt.getId().toString(), context);
-        final Long recordId = transactional.getRecordId(savedAttempt.getId().toString(), context);
-        final EntityHistory<PaymentAttemptModelDao> history = new EntityHistory<PaymentAttemptModelDao>(savedAttempt.getId(), recordId, savedAttempt, ChangeType.INSERT);
-        transactional.insertHistoryFromTransaction(history, context);
-        final Long historyRecordId = transactional.getHistoryRecordId(recordId, context);
-        final EntityAudit audit = new EntityAudit(TableName.PAYMENT_ATTEMPT_HISTORY, historyRecordId, ChangeType.INSERT);
-        transactional.insertAuditFromTransaction(audit, context);
-        return savedAttempt;
+                return transactional.getPayment(payment.getId().toString(), context);
+            }
+        });
     }
 
     @Override
     public PaymentAttemptModelDao getPaymentAttempt(final UUID attemptId, final InternalTenantContext context) {
-        return paymentAttemptSqlDao.inTransaction(new Transaction<PaymentAttemptModelDao, PaymentAttemptSqlDao>() {
-            @Override
-            public PaymentAttemptModelDao inTransaction(final PaymentAttemptSqlDao transactional, final TransactionStatus status)
-                    throws Exception {
-                return transactional.getPaymentAttempt(attemptId.toString(), context);
-            }
-        });
+        return paymentAttemptSqlDao.getPaymentAttempt(attemptId.toString(), context);
     }
 
     @Override
@@ -129,84 +98,41 @@ public class AuditedPaymentDao implements PaymentDao {
                                                   final String extSecondPaymentRefId,
                                                   final UUID attemptId,
                                                   final InternalCallContext context) {
-        paymentSqlDao.inTransaction(new Transaction<Void, PaymentSqlDao>() {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
 
             @Override
-            public Void inTransaction(final PaymentSqlDao transactional,
-                                      final TransactionStatus status) throws Exception {
-                updatePaymentStatusFromTransaction(paymentId, paymentStatus, extFirstPaymentRefId, extSecondPaymentRefId, context, transactional);
-                final PaymentAttemptSqlDao transPaymentAttemptSqlDao = transactional.become(PaymentAttemptSqlDao.class);
-                updatePaymentAttemptStatusFromTransaction(attemptId, paymentStatus, gatewayErrorCode, gatewayErrorMsg, context, transPaymentAttemptSqlDao);
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                entitySqlDaoWrapperFactory.become(PaymentSqlDao.class).updatePaymentStatusAndExtRef(paymentId.toString(), paymentStatus.toString(), extFirstPaymentRefId, extSecondPaymentRefId, context);
+                entitySqlDaoWrapperFactory.become(PaymentAttemptSqlDao.class).updatePaymentAttemptStatus(attemptId.toString(), paymentStatus.toString(), gatewayErrorCode, gatewayErrorMsg, context);
                 return null;
             }
         });
     }
 
-    private void updatePaymentAmountFromTransaction(final UUID paymentId, final BigDecimal amount, final InternalCallContext context, final PaymentSqlDao transactional) {
-        transactional.updatePaymentAmount(paymentId.toString(), amount, context);
-        final PaymentModelDao savedPayment = transactional.getPayment(paymentId.toString(), context);
-        final Long recordId = transactional.getRecordId(savedPayment.getId().toString(), context);
-        final EntityHistory<PaymentModelDao> history = new EntityHistory<PaymentModelDao>(savedPayment.getId(), recordId, savedPayment, ChangeType.UPDATE);
-        transactional.insertHistoryFromTransaction(history, context);
-        final Long historyRecordId = transactional.getHistoryRecordId(recordId, context);
-        final EntityAudit audit = new EntityAudit(TableName.PAYMENT_HISTORY, historyRecordId, ChangeType.UPDATE);
-        transactional.insertAuditFromTransaction(audit, context);
-    }
-
-    private void updatePaymentStatusFromTransaction(final UUID paymentId, final PaymentStatus paymentStatus, final String extFirstPaymentRefId,
-                                                    final String extSecondPaymentRefId, final InternalCallContext context, final PaymentSqlDao transactional) {
-        transactional.updatePaymentStatusAndExtRef(paymentId.toString(), paymentStatus.toString(), extFirstPaymentRefId, extSecondPaymentRefId, context);
-        final PaymentModelDao savedPayment = transactional.getPayment(paymentId.toString(), context);
-        final Long recordId = transactional.getRecordId(savedPayment.getId().toString(), context);
-        final EntityHistory<PaymentModelDao> history = new EntityHistory<PaymentModelDao>(savedPayment.getId(), recordId, savedPayment, ChangeType.UPDATE);
-        transactional.insertHistoryFromTransaction(history, context);
-        final Long historyRecordId = transactional.getHistoryRecordId(recordId, context);
-        final EntityAudit audit = new EntityAudit(TableName.PAYMENT_HISTORY, historyRecordId, ChangeType.UPDATE);
-        transactional.insertAuditFromTransaction(audit, context);
-    }
-
-    private void updatePaymentAttemptStatusFromTransaction(final UUID attemptId, final PaymentStatus processingStatus, final String gatewayErrorCode,
-                                                           final String gatewayErrorMsg, final InternalCallContext context, final PaymentAttemptSqlDao transactional) {
-        transactional.updatePaymentAttemptStatus(attemptId.toString(), processingStatus.toString(), gatewayErrorCode, gatewayErrorMsg, context);
-        final PaymentAttemptModelDao savedAttempt = transactional.getPaymentAttempt(attemptId.toString(), context);
-        final Long recordId = transactional.getRecordId(savedAttempt.getId().toString(), context);
-        final EntityHistory<PaymentAttemptModelDao> history = new EntityHistory<PaymentAttemptModelDao>(savedAttempt.getId(), recordId, savedAttempt, ChangeType.UPDATE);
-        transactional.insertHistoryFromTransaction(history, context);
-        final Long historyRecordId = transactional.getHistoryRecordId(recordId, context);
-        final EntityAudit audit = new EntityAudit(TableName.PAYMENT_ATTEMPT_HISTORY, historyRecordId, ChangeType.UPDATE);
-        transactional.insertAuditFromTransaction(audit, context);
-    }
-
     @Override
     public PaymentMethodModelDao insertPaymentMethod(final PaymentMethodModelDao paymentMethod, final InternalCallContext context) {
-        return paymentMethodSqlDao.inTransaction(new Transaction<PaymentMethodModelDao, PaymentMethodSqlDao>() {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<PaymentMethodModelDao>() {
             @Override
-            public PaymentMethodModelDao inTransaction(final PaymentMethodSqlDao transactional, final TransactionStatus status)
-                    throws Exception {
-                return insertPaymentMethodInTransaction(transactional, paymentMethod, context);
+            public PaymentMethodModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return insertPaymentMethodInTransaction(entitySqlDaoWrapperFactory, paymentMethod, context);
             }
         });
     }
 
-    private PaymentMethodModelDao insertPaymentMethodInTransaction(final PaymentMethodSqlDao transactional, final PaymentMethodModelDao paymentMethod, final InternalCallContext context) {
+    private PaymentMethodModelDao insertPaymentMethodInTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final PaymentMethodModelDao paymentMethod, final InternalCallContext context) {
+        final PaymentMethodSqlDao transactional = entitySqlDaoWrapperFactory.become(PaymentMethodSqlDao.class);
         transactional.insertPaymentMethod(paymentMethod, context);
-        final PaymentMethodModelDao savedPaymentMethod = transactional.getPaymentMethod(paymentMethod.getId().toString(), context);
-        final Long recordId = transactional.getRecordId(savedPaymentMethod.getId().toString(), context);
-        final EntityHistory<PaymentMethodModelDao> history = new EntityHistory<PaymentMethodModelDao>(savedPaymentMethod.getId(), recordId, savedPaymentMethod, ChangeType.INSERT);
-        transactional.insertHistoryFromTransaction(history, context);
-        final Long historyRecordId = transactional.getHistoryRecordId(recordId, context);
-        final EntityAudit audit = new EntityAudit(TableName.PAYMENT_METHOD_HISTORY, historyRecordId, ChangeType.INSERT);
-        transactional.insertAuditFromTransaction(audit, context);
-        return savedPaymentMethod;
+        return transactional.getPaymentMethod(paymentMethod.getId().toString(), context);
     }
 
     @Override
     public List<PaymentMethodModelDao> refreshPaymentMethods(final UUID accountId, final List<PaymentMethodModelDao> paymentMethods, final InternalCallContext context) {
-        return paymentMethodSqlDao.inTransaction(new Transaction<List<PaymentMethodModelDao>, PaymentMethodSqlDao>() {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<PaymentMethodModelDao>>() {
 
             @Override
-            public List<PaymentMethodModelDao> inTransaction(final PaymentMethodSqlDao transactional, final TransactionStatus status) throws Exception {
-                final List<PaymentMethodModelDao> existingPaymentMethods = getPaymentMethodsInTransaction(transactional, accountId, context);
+            public List<PaymentMethodModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final PaymentMethodSqlDao transactional = entitySqlDaoWrapperFactory.become(PaymentMethodSqlDao.class);
+                final List<PaymentMethodModelDao> existingPaymentMethods = transactional.getPaymentMethods(accountId.toString(), context);
 
                 final Set<String> externalPaymentIdProcessed = new HashSet<String>();
                 for (final PaymentMethodModelDao finalPaymentMethod : paymentMethods) {
@@ -220,7 +146,7 @@ public class AuditedPaymentDao implements PaymentDao {
                         } else if (existingPaymentMethod.equalsButActive(finalPaymentMethod)) {
                             // We already have it but its status has changed - update it accordingly
                             // Note - in the remote system, the payment method will always be active
-                            undeletedPaymentMethodInTransaction(transactional, existingPaymentMethod.getId(), context);
+                            undeletedPaymentMethodInTransaction(entitySqlDaoWrapperFactory, existingPaymentMethod.getId(), context);
                             isExistingPaymentMethod = true;
                             break;
                         }
@@ -228,7 +154,7 @@ public class AuditedPaymentDao implements PaymentDao {
                     }
 
                     if (!isExistingPaymentMethod) {
-                        insertPaymentMethodInTransaction(transactional, finalPaymentMethod, context);
+                        insertPaymentMethodInTransaction(entitySqlDaoWrapperFactory, finalPaymentMethod, context);
                     }
 
                     externalPaymentIdProcessed.add(finalPaymentMethod.getExternalId());
@@ -237,52 +163,35 @@ public class AuditedPaymentDao implements PaymentDao {
                 // Finally, mark as deleted the ones that don't exist in the specified list (remote system)
                 for (final PaymentMethodModelDao existingPaymentMethod : existingPaymentMethods) {
                     if (!externalPaymentIdProcessed.contains(existingPaymentMethod.getExternalId())) {
-                        deletedPaymentMethodInTransaction(transactional, existingPaymentMethod.getId(), context);
+                        deletedPaymentMethodInTransaction(entitySqlDaoWrapperFactory, existingPaymentMethod.getId(), context);
                     }
                 }
 
-                return getPaymentMethodsInTransaction(transactional, accountId, context);
+                return transactional.getPaymentMethods(accountId.toString(), context);
             }
         });
     }
 
     @Override
     public RefundModelDao insertRefund(final RefundModelDao refundInfo, final InternalCallContext context) {
-        return refundSqlDao.inTransaction(new Transaction<RefundModelDao, RefundSqlDao>() {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<RefundModelDao>() {
 
             @Override
-            public RefundModelDao inTransaction(RefundSqlDao transactional,
-                                                TransactionStatus status) throws Exception {
-
+            public RefundModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final RefundSqlDao transactional = entitySqlDaoWrapperFactory.become(RefundSqlDao.class);
                 transactional.insertRefund(refundInfo, context);
-                final RefundModelDao savedRefund = transactional.getRefund(refundInfo.getId().toString(), context);
-                final Long recordId = transactional.getRecordId(savedRefund.getId().toString(), context);
-                final EntityHistory<RefundModelDao> history = new EntityHistory<RefundModelDao>(savedRefund.getId(), recordId, savedRefund, ChangeType.INSERT);
-                transactional.insertHistoryFromTransaction(history, context);
-                final Long historyRecordId = transactional.getHistoryRecordId(recordId, context);
-                final EntityAudit audit = new EntityAudit(TableName.REFUND_HISTORY, historyRecordId, ChangeType.INSERT);
-                transactional.insertAuditFromTransaction(audit, context);
-                return savedRefund;
+                return transactional.getRefund(refundInfo.getId().toString(), context);
             }
         });
     }
 
     @Override
     public void updateRefundStatus(final UUID refundId, final RefundStatus refundStatus, final InternalCallContext context) {
-        refundSqlDao.inTransaction(new Transaction<Void, RefundSqlDao>() {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
 
             @Override
-            public Void inTransaction(RefundSqlDao transactional,
-                                      TransactionStatus status) throws Exception {
-                transactional.updateStatus(refundId.toString(), refundStatus.toString(), context);
-
-                final RefundModelDao savedRefund = transactional.getRefund(refundId.toString(), context);
-                final Long recordId = transactional.getRecordId(savedRefund.getId().toString(), context);
-                final EntityHistory<RefundModelDao> history = new EntityHistory<RefundModelDao>(savedRefund.getId(), recordId, savedRefund, ChangeType.UPDATE);
-                transactional.insertHistoryFromTransaction(history, context);
-                final Long historyRecordId = transactional.getHistoryRecordId(recordId, context);
-                final EntityAudit audit = new EntityAudit(TableName.REFUND_HISTORY, historyRecordId, ChangeType.UPDATE);
-                transactional.insertAuditFromTransaction(audit, context);
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                entitySqlDaoWrapperFactory.become(RefundSqlDao.class).updateStatus(refundId.toString(), refundStatus.toString(), context);
                 return null;
             }
         });
@@ -290,38 +199,17 @@ public class AuditedPaymentDao implements PaymentDao {
 
     @Override
     public RefundModelDao getRefund(final UUID refundId, final InternalTenantContext context) {
-        return refundSqlDao.inTransaction(new Transaction<RefundModelDao, RefundSqlDao>() {
-
-            @Override
-            public RefundModelDao inTransaction(RefundSqlDao transactional,
-                                                TransactionStatus status) throws Exception {
-                return transactional.getRefund(refundId.toString(), context);
-            }
-        });
+        return refundSqlDao.getRefund(refundId.toString(), context);
     }
 
     @Override
     public List<RefundModelDao> getRefundsForPayment(final UUID paymentId, final InternalTenantContext context) {
-        return refundSqlDao.inTransaction(new Transaction<List<RefundModelDao>, RefundSqlDao>() {
-
-            @Override
-            public List<RefundModelDao> inTransaction(RefundSqlDao transactional,
-                                                      TransactionStatus status) throws Exception {
-                return transactional.getRefundsForPayment(paymentId.toString(), context);
-            }
-        });
+        return refundSqlDao.getRefundsForPayment(paymentId.toString(), context);
     }
 
     @Override
     public List<RefundModelDao> getRefundsForAccount(final UUID accountId, final InternalTenantContext context) {
-        return refundSqlDao.inTransaction(new Transaction<List<RefundModelDao>, RefundSqlDao>() {
-
-            @Override
-            public List<RefundModelDao> inTransaction(RefundSqlDao transactional,
-                                                      TransactionStatus status) throws Exception {
-                return transactional.getRefundsForAccount(accountId.toString(), context);
-            }
-        });
+        return refundSqlDao.getRefundsForAccount(accountId.toString(), context);
     }
 
     @Override
@@ -340,29 +228,38 @@ public class AuditedPaymentDao implements PaymentDao {
 
     @Override
     public List<PaymentMethodModelDao> getPaymentMethods(final UUID accountId, final InternalTenantContext context) {
-        return getPaymentMethodsInTransaction(paymentMethodSqlDao, accountId, context);
-    }
-
-    private List<PaymentMethodModelDao> getPaymentMethodsInTransaction(final PaymentMethodSqlDao transactional, final UUID accountId, final InternalTenantContext context) {
-        return transactional.getPaymentMethods(accountId.toString(), context);
+        return paymentMethodSqlDao.getPaymentMethods(accountId.toString(), context);
     }
 
     @Override
     public void deletedPaymentMethod(final UUID paymentMethodId, final InternalCallContext context) {
-        deletedPaymentMethodInTransaction(paymentMethodSqlDao, paymentMethodId, context);
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                deletedPaymentMethodInTransaction(entitySqlDaoWrapperFactory, paymentMethodId, context);
+                return null;
+            }
+        });
     }
 
-    private void deletedPaymentMethodInTransaction(final PaymentMethodSqlDao transactional, final UUID paymentMethodId, final InternalCallContext context) {
-        transactional.markPaymentMethodAsDeleted(paymentMethodId.toString(), context);
+    private void deletedPaymentMethodInTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final UUID paymentMethodId, final InternalCallContext context) {
+        entitySqlDaoWrapperFactory.become(PaymentMethodSqlDao.class).markPaymentMethodAsDeleted(paymentMethodId.toString(), context);
     }
 
     @Override
     public void undeletedPaymentMethod(final UUID paymentMethodId, final InternalCallContext context) {
-        undeletedPaymentMethodInTransaction(paymentMethodSqlDao, paymentMethodId, context);
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                undeletedPaymentMethodInTransaction(entitySqlDaoWrapperFactory, paymentMethodId, context);
+                return null;
+            }
+        });
     }
 
-    private void undeletedPaymentMethodInTransaction(final PaymentMethodSqlDao transactional, final UUID paymentMethodId, final InternalCallContext context) {
-        transactional.unmarkPaymentMethodAsDeleted(paymentMethodId.toString(), context);
+    private void undeletedPaymentMethodInTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final UUID paymentMethodId, final InternalCallContext context) {
+        final PaymentMethodSqlDao paymentMethodSqlDao = entitySqlDaoWrapperFactory.become(PaymentMethodSqlDao.class);
+        paymentMethodSqlDao.unmarkPaymentMethodAsDeleted(paymentMethodId.toString(), context);
     }
 
     @Override
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptSqlDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptSqlDao.java
index 84d5ed5..146fa9c 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptSqlDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptSqlDao.java
@@ -31,29 +31,31 @@ import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
 import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
 import com.ning.billing.payment.api.PaymentStatus;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.callcontext.InternalTenantContextBinder;
 import com.ning.billing.util.dao.BinderBase;
 import com.ning.billing.util.dao.EntityHistory;
 import com.ning.billing.util.dao.MapperBase;
+import com.ning.billing.util.entity.dao.Audited;
 import com.ning.billing.util.entity.dao.UpdatableEntitySqlDao;
 
 @ExternalizedSqlViaStringTemplate3()
 @RegisterMapper(PaymentAttemptSqlDao.PaymentAttemptModelDaoMapper.class)
-public interface PaymentAttemptSqlDao extends Transactional<PaymentAttemptSqlDao>, UpdatableEntitySqlDao<PaymentAttemptModelDao>, Transmogrifier, CloseMe {
+public interface PaymentAttemptSqlDao extends UpdatableEntitySqlDao<PaymentAttemptModelDao>, CloseMe {
 
     @SqlUpdate
+    @Audited(ChangeType.INSERT)
     void insertPaymentAttempt(@Bind(binder = PaymentAttemptModelDaoBinder.class) final PaymentAttemptModelDao attempt,
                               @InternalTenantContextBinder final InternalCallContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     void updatePaymentAttemptStatus(@Bind("id") final String attemptId,
                                     @Bind("processingStatus") final String processingStatus,
                                     @Bind("gatewayErrorCode") final String gatewayErrorCode,
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodSqlDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodSqlDao.java
index 8ccadca..a328828 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodSqlDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodSqlDao.java
@@ -29,33 +29,35 @@ import org.skife.jdbi.v2.sqlobject.Binder;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
-import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.callcontext.InternalTenantContextBinder;
 import com.ning.billing.util.dao.BinderBase;
 import com.ning.billing.util.dao.EntityHistory;
 import com.ning.billing.util.dao.MapperBase;
+import com.ning.billing.util.entity.dao.Audited;
 import com.ning.billing.util.entity.dao.UpdatableEntitySqlDao;
 
 @ExternalizedSqlViaStringTemplate3()
 @RegisterMapper(PaymentMethodSqlDao.PaymentMethodDaoMapper.class)
-public interface PaymentMethodSqlDao extends Transactional<PaymentMethodSqlDao>, UpdatableEntitySqlDao<PaymentMethodModelDao>, Transmogrifier, CloseMe {
+public interface PaymentMethodSqlDao extends UpdatableEntitySqlDao<PaymentMethodModelDao> {
 
     @SqlUpdate
+    @Audited(ChangeType.INSERT)
     void insertPaymentMethod(@Bind(binder = PaymentMethodModelDaoBinder.class) final PaymentMethodModelDao paymentMethod,
                              @InternalTenantContextBinder final InternalCallContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     void markPaymentMethodAsDeleted(@Bind("id") final String paymentMethodId,
                                     @InternalTenantContextBinder final InternalCallContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     void unmarkPaymentMethodAsDeleted(@Bind("id") final String paymentMethodId,
                                       @InternalTenantContextBinder final InternalCallContext context);
 
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
index cd19c70..e6bda5b 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
@@ -30,31 +30,32 @@ import org.skife.jdbi.v2.sqlobject.Binder;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
-import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.payment.api.PaymentStatus;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.callcontext.InternalTenantContextBinder;
 import com.ning.billing.util.dao.BinderBase;
 import com.ning.billing.util.dao.EntityHistory;
 import com.ning.billing.util.dao.MapperBase;
+import com.ning.billing.util.entity.dao.Audited;
 import com.ning.billing.util.entity.dao.UpdatableEntitySqlDao;
 
 @ExternalizedSqlViaStringTemplate3()
 @RegisterMapper(PaymentSqlDao.PaymentModelDaoMapper.class)
-public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, UpdatableEntitySqlDao<PaymentModelDao>, Transmogrifier, CloseMe {
+public interface PaymentSqlDao extends UpdatableEntitySqlDao<PaymentModelDao> {
 
     @SqlUpdate
+    @Audited(ChangeType.INSERT)
     void insertPayment(@Bind(binder = PaymentModelDaoBinder.class) final PaymentModelDao paymentInfo,
                        @InternalTenantContextBinder final InternalCallContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     void updatePaymentStatusAndExtRef(@Bind("id") final String paymentId,
                                       @Bind("paymentStatus") final String paymentStatus,
                                       @Bind("extFirstPaymentRefId") final String extFirstPaymentRefId,
@@ -62,6 +63,7 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, UpdatableEn
                                       @InternalTenantContextBinder final InternalCallContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     void updatePaymentAmount(@Bind("id") final String paymentId,
                              @Bind("amount") final BigDecimal amount,
                              @InternalTenantContextBinder final InternalCallContext context);
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/RefundSqlDao.java b/payment/src/main/java/com/ning/billing/payment/dao/RefundSqlDao.java
index 2d21d2e..07ae04a 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/RefundSqlDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/RefundSqlDao.java
@@ -30,31 +30,32 @@ import org.skife.jdbi.v2.sqlobject.Binder;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
-import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.callcontext.InternalTenantContextBinder;
 import com.ning.billing.util.dao.BinderBase;
 import com.ning.billing.util.dao.EntityHistory;
 import com.ning.billing.util.dao.MapperBase;
+import com.ning.billing.util.entity.dao.Audited;
 import com.ning.billing.util.entity.dao.UpdatableEntitySqlDao;
 
 @ExternalizedSqlViaStringTemplate3()
 @RegisterMapper(RefundSqlDao.RefundModelDaoMapper.class)
-public interface RefundSqlDao extends Transactional<RefundSqlDao>, UpdatableEntitySqlDao<RefundModelDao>, Transmogrifier, CloseMe {
+public interface RefundSqlDao extends UpdatableEntitySqlDao<RefundModelDao> {
 
     @SqlUpdate
+    @Audited(ChangeType.INSERT)
     void insertRefund(@Bind(binder = RefundModelDaoBinder.class) final RefundModelDao refundInfo,
                       @InternalTenantContextBinder final InternalCallContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.UPDATE)
     void updateStatus(@Bind("id") final String refundId,
                       @Bind("refundStatus") final String status,
                       @InternalTenantContextBinder final InternalCallContext context);
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentAttemptSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
index c6edf85..0d2d50c 100644
--- a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
@@ -68,6 +68,32 @@ getRecordId() ::= <<
     ;
 >>
 
+getByRecordId() ::= <<
+    SELECT <paymentAttemptFields("pa.")>
+    , pa.created_date as effective_date
+    , p.account_id as account_id
+    , p.invoice_id as invoice_id
+      FROM payment_attempts pa join payments p
+     WHERE pa.record_id = :recordId
+     AND pa.payment_id = p.id
+     <AND_CHECK_TENANT("pa.")>
+     <AND_CHECK_TENANT("p.")>
+     ;
+>>
+
+getById() ::= <<
+    SELECT <paymentAttemptFields("pa.")>
+    , pa.created_date as effective_date
+    , p.account_id as account_id
+    , p.invoice_id as invoice_id
+      FROM payment_attempts pa join payments p
+     WHERE pa.id = :id
+     AND pa.payment_id = p.id
+     <AND_CHECK_TENANT("pa.")>
+     <AND_CHECK_TENANT("p.")>
+     ;
+>>
+
 getPaymentAttemptIdFromPaymentId() ::= <<
     SELECT id
     FROM payment_attempts
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentMethodSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentMethodSqlDao.sql.stg
index f7fcd4a..8de2123 100644
--- a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentMethodSqlDao.sql.stg
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentMethodSqlDao.sql.stg
@@ -68,6 +68,18 @@ getRecordId() ::= <<
     ;
 >>
 
+getByRecordId() ::= <<
+    SELECT <paymentMethodFields()>
+    FROM payment_methods
+    WHERE record_id = :recordId <AND_CHECK_TENANT()>;
+>>
+
+getById() ::= <<
+    SELECT <paymentMethodFields()>
+    FROM payment_methods
+    WHERE id = :id <AND_CHECK_TENANT()>;
+>>
+
 historyFields(prefix) ::= <<
     <prefix>record_id,
     <prefix>id,
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
index f2170b3..c789dd4 100644
--- a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
@@ -91,6 +91,17 @@ getRecordId() ::= <<
     ;
 >>
 
+getByRecordId() ::= <<
+    SELECT <paymentFields()>, record_id as payment_number
+    FROM payments
+    WHERE record_id = :recordId <AND_CHECK_TENANT()>;
+>>
+
+getById() ::= <<
+    SELECT <paymentFields()>, record_id as payment_number
+    FROM payments
+    WHERE id = :id <AND_CHECK_TENANT()>;
+>>
 
 historyFields(prefix) ::= <<
     <prefix>record_id,
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/RefundSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/RefundSqlDao.sql.stg
index e277a4e..295cb8c 100644
--- a/payment/src/main/resources/com/ning/billing/payment/dao/RefundSqlDao.sql.stg
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/RefundSqlDao.sql.stg
@@ -65,6 +65,18 @@ getRecordId() ::= <<
     ;
 >>
 
+getByRecordId() ::= <<
+    SELECT <refundFields()>
+    FROM refunds
+    WHERE record_id = :recordId <AND_CHECK_TENANT()>;
+>>
+
+getById() ::= <<
+    SELECT <refundFields()>
+    FROM refunds
+    WHERE id = :id <AND_CHECK_TENANT()>;
+>>
+
 historyFields(prefix) ::= <<
     <prefix>record_id,
     <prefix>id,
diff --git a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantSqlDao.java b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantSqlDao.java
index 8ce298b..451b60a 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantSqlDao.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantSqlDao.java
@@ -21,8 +21,6 @@ import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.customizers.Mapper;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 
 import com.ning.billing.tenant.api.Tenant;
@@ -33,7 +31,7 @@ import com.ning.billing.util.entity.dao.EntitySqlDao;
 
 @ExternalizedSqlViaStringTemplate3
 @RegisterMapper({UuidMapper.class, TenantMapper.class})
-public interface TenantSqlDao extends EntitySqlDao<Tenant>, Transactional<TenantSqlDao>, Transmogrifier {
+public interface TenantSqlDao extends EntitySqlDao<Tenant> {
 
     @SqlQuery
     public Tenant getByApiKey(@Bind("apiKey") final String apiKey);
diff --git a/util/src/main/java/com/ning/billing/util/dao/HistorySqlDao.java b/util/src/main/java/com/ning/billing/util/dao/HistorySqlDao.java
index 15a2ac5..8976541 100644
--- a/util/src/main/java/com/ning/billing/util/dao/HistorySqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/dao/HistorySqlDao.java
@@ -26,6 +26,7 @@ import com.ning.billing.util.callcontext.InternalTenantContextBinder;
 import com.ning.billing.util.entity.Entity;
 
 public interface HistorySqlDao<T extends Entity> {
+
     @SqlBatch(transactional = false)
     public void batchAddHistoryFromTransaction(List<EntityHistory<T>> histories,
                                                @InternalTenantContextBinder InternalCallContext context);
diff --git a/util/src/main/java/com/ning/billing/util/dao/TableName.java b/util/src/main/java/com/ning/billing/util/dao/TableName.java
index b0213f5..af23356 100644
--- a/util/src/main/java/com/ning/billing/util/dao/TableName.java
+++ b/util/src/main/java/com/ning/billing/util/dao/TableName.java
@@ -98,7 +98,7 @@ public enum TableName {
 
     public static TableName fromEntityClass(final Class<? extends Entity> entityClass) {
         for (final TableName tableName : values()) {
-            if (tableName.getEntityClass() != null && tableName.getEntityClass().equals(entityClass)) {
+            if (tableName.getEntityClass() != null && tableName.getEntityClass().isAssignableFrom(entityClass)) {
                 return tableName;
             }
         }
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/Audited.java b/util/src/main/java/com/ning/billing/util/entity/dao/Audited.java
new file mode 100644
index 0000000..dc4b578
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/Audited.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you 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.ning.billing.util.entity.dao;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.ning.billing.util.audit.ChangeType;
+
+/**
+ * The <code>Audited</code> annotation wraps a Sql dao method and
+ * create Audit and History entries as needed. Every r/w
+ * database operation on any Entity should have this annotation.
+ * <p/>
+ * To create a audit entries automatically for some method <code>updateChargedThroughDate</code>:
+ * <pre>
+ *         @Audited(type = ChangeType.UPDATE)
+ *         @SqlUpdate public void updateChargedThroughDate(@Bind("id") String id,
+ *                                                         @Bind("chargedThroughDate") Date chargedThroughDate,
+ *                                                         @InternalTenantContextBinder final InternalCallContext context);
+ * </pre>
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface Audited {
+
+    /**
+     * @return the type of operation
+     */
+    ChangeType value();
+}
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDao.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDao.java
index 9dd5589..b8aee42 100644
--- a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDao.java
@@ -22,14 +22,19 @@ import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
+import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.callcontext.InternalTenantContextBinder;
+import com.ning.billing.util.dao.AuditSqlDao;
+import com.ning.billing.util.dao.HistorySqlDao;
 import com.ning.billing.util.entity.Entity;
 import com.ning.billing.util.entity.EntityPersistenceException;
 
-public interface EntitySqlDao<T extends Entity> {
+public interface EntitySqlDao<T extends Entity> extends AuditSqlDao, HistorySqlDao<T>, Transmogrifier, Transactional<EntitySqlDao<T>>, CloseMe {
 
     @SqlUpdate
     public void create(@BindBean final T entity,
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java
new file mode 100644
index 0000000..9a4dd2a
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you 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.ning.billing.util.entity.dao;
+
+import org.skife.jdbi.v2.IDBI;
+import org.skife.jdbi.v2.Transaction;
+import org.skife.jdbi.v2.TransactionStatus;
+
+import com.ning.billing.util.entity.Entity;
+
+/**
+ * Transaction manager for EntitySqlDao queries
+ */
+public class EntitySqlDaoTransactionalJdbiWrapper {
+
+    private final IDBI dbi;
+
+    public EntitySqlDaoTransactionalJdbiWrapper(final IDBI dbi) {
+        this.dbi = dbi;
+    }
+
+    class JdbiTransaction<ReturnType, T extends Entity> implements Transaction<ReturnType, EntitySqlDao<T>> {
+
+        private final EntitySqlDaoTransactionWrapper<ReturnType> entitySqlDaoTransactionWrapper;
+
+        JdbiTransaction(final EntitySqlDaoTransactionWrapper<ReturnType> entitySqlDaoTransactionWrapper) {
+            this.entitySqlDaoTransactionWrapper = entitySqlDaoTransactionWrapper;
+        }
+
+        @Override
+        public ReturnType inTransaction(final EntitySqlDao<T> transactionalSqlDao, final TransactionStatus status) throws Exception {
+            final EntitySqlDaoWrapperFactory<EntitySqlDao> factoryEntitySqlDao = new EntitySqlDaoWrapperFactory<EntitySqlDao>(transactionalSqlDao);
+            return entitySqlDaoTransactionWrapper.inTransaction(factoryEntitySqlDao);
+        }
+    }
+
+    // To handle warnings only
+    interface InitialEntitySqlDao extends EntitySqlDao<Entity> {}
+
+    /**
+     * @param entitySqlDaoTransactionWrapper transaction to execute
+     * @param <ReturnType>                   object type to return from the transaction
+     * @return result from the transaction fo type ReturnType
+     */
+    public <ReturnType> ReturnType execute(final EntitySqlDaoTransactionWrapper<ReturnType> entitySqlDaoTransactionWrapper) {
+        final EntitySqlDao<Entity> entitySqlDao = dbi.onDemand(InitialEntitySqlDao.class);
+        return entitySqlDao.inTransaction(new JdbiTransaction<ReturnType, Entity>(entitySqlDaoTransactionWrapper));
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoTransactionWrapper.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoTransactionWrapper.java
new file mode 100644
index 0000000..1ee2883
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoTransactionWrapper.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you 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.ning.billing.util.entity.dao;
+
+/**
+ * Transaction closure for EntitySqlDao queries
+ *
+ * @param <ReturnType> object type to return from the transaction
+ */
+public interface EntitySqlDaoTransactionWrapper<ReturnType> {
+
+    /**
+     * @param entitySqlDaoWrapperFactory factory to create EntitySqlDao instances
+     * @return result from the transaction of type ReturnType
+     */
+    ReturnType inTransaction(EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception;
+}
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoWrapperFactory.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoWrapperFactory.java
new file mode 100644
index 0000000..11e94d7
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoWrapperFactory.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you 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.ning.billing.util.entity.dao;
+
+import java.lang.reflect.Proxy;
+
+import com.ning.billing.util.entity.Entity;
+
+/**
+ * Factory to create wrapped EntitySqlDao objects. During a transaction, make sure
+ * to create other EntitySqlDao objects via the #become call.
+ *
+ * @param <InitialSqlDao> EntitySqlDao type to create
+ * @see EntitySqlDaoWrapperInvocationHandler
+ */
+public class EntitySqlDaoWrapperFactory<InitialSqlDao extends EntitySqlDao> {
+
+    private final InitialSqlDao sqlDao;
+
+    public EntitySqlDaoWrapperFactory(final InitialSqlDao sqlDao) {
+        this.sqlDao = sqlDao;
+    }
+
+    /**
+     * Get an instance of a specified EntitySqlDao class, sharing the same database session as the
+     * initial sql dao class with which this wrapper factory was created.
+     *
+     * @param newSqlDaoClass the class to instantiate
+     * @param <NewSqlDao>    EntitySqlDao type to create
+     * @return instance of NewSqlDao
+     */
+    public <NewSqlDao extends EntitySqlDao<NewEntity>, NewEntity extends Entity> NewSqlDao become(final Class<NewSqlDao> newSqlDaoClass) {
+        return create(newSqlDaoClass, sqlDao.become(newSqlDaoClass));
+    }
+
+    private <NewSqlDao extends EntitySqlDao<NewEntity>, NewEntity extends Entity> NewSqlDao create(final Class<NewSqlDao> newSqlDaoClass, final NewSqlDao newSqlDao) {
+        final ClassLoader classLoader = newSqlDao.getClass().getClassLoader();
+        final Class[] interfacesToImplement = {newSqlDaoClass};
+        final EntitySqlDaoWrapperInvocationHandler<NewSqlDao, NewEntity> wrapperInvocationHandler = new EntitySqlDaoWrapperInvocationHandler<NewSqlDao, NewEntity>(newSqlDao);
+
+        final Object newSqlDaoObject = Proxy.newProxyInstance(classLoader, interfacesToImplement, wrapperInvocationHandler);
+        return newSqlDaoClass.cast(newSqlDaoObject);
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
new file mode 100644
index 0000000..218e285
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you 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.ning.billing.util.entity.dao;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.skife.jdbi.v2.exceptions.DBIException;
+import org.skife.jdbi.v2.sqlobject.Bind;
+
+import com.ning.billing.util.audit.ChangeType;
+import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.callcontext.InternalTenantContextBinder;
+import com.ning.billing.util.dao.EntityAudit;
+import com.ning.billing.util.dao.EntityHistory;
+import com.ning.billing.util.dao.TableName;
+import com.ning.billing.util.entity.Entity;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+
+/**
+ * Wraps an instance of EntitySqlDao, performing extra work around each method (Sql query)
+ *
+ * @param <T> EntitySqlDao type of the wrapped instance
+ */
+public class EntitySqlDaoWrapperInvocationHandler<T extends EntitySqlDao<U>, U extends Entity> implements InvocationHandler {
+
+    private final T sqlDao;
+
+    public EntitySqlDaoWrapperInvocationHandler(final T sqlDao) {
+        this.sqlDao = sqlDao;
+    }
+
+    @Override
+    public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
+        try {
+            return invokeSafely(proxy, method, args);
+        } catch (Throwable t) {
+            if (t.getCause() != null && t.getCause().getCause() != null && DBIException.class.isAssignableFrom(t.getCause().getClass())) {
+                // Likely the JDBC exception or a Billing exception we have thrown in the transaction
+                errorDuringTransaction(t.getCause().getCause());
+            } else if (t.getCause() != null) {
+                // t is likely not interesting (java.lang.reflect.InvocationTargetException)
+                errorDuringTransaction(t.getCause());
+            } else {
+                errorDuringTransaction(t);
+            }
+        }
+
+        // Never reached
+        return null;
+    }
+
+    // Nice method name to ease debugging while looking at log files
+    private void errorDuringTransaction(final Throwable t) throws Throwable {
+        // This is to avoid throwing an exception wrapped in an UndeclaredThrowableException
+        if (!(t instanceof RuntimeException)) {
+            throw new RuntimeException(t);
+        } else {
+            throw t;
+        }
+    }
+
+    private Object invokeSafely(final Object proxy, final Method method, final Object[] args) throws Throwable {
+        final Audited annotation = method.getAnnotation(Audited.class);
+
+        InternalCallContext context = null;
+        List<String> entityIds = null;
+        final Map<String, U> entities = new HashMap<String, U>();
+        final Map<String, Long> entityRecordIds = new HashMap<String, Long>();
+        if (annotation != null) {
+            // There will be some work required after the statement is executed,
+            // get the id before in case the change is a delete
+            context = retrieveContextFromArguments(method, args);
+            entityIds = retrieveEntityIdsFromArguments(method, args);
+            for (final String entityId : entityIds) {
+                entities.put(entityId, sqlDao.getById(entityId, context));
+                entityRecordIds.put(entityId, sqlDao.getRecordId(entityId, context));
+            }
+        }
+
+        // Real jdbc call
+        final Object obj = method.invoke(sqlDao, args);
+
+        // Update audit and history if needed
+        if (annotation != null) {
+            final ChangeType changeType = annotation.value();
+
+            for (final String entityId : entityIds) {
+                updateHistoryAndAudit(entityId, entities, entityRecordIds, changeType, context);
+
+            }
+        }
+
+        return obj;
+    }
+
+    private void updateHistoryAndAudit(final String entityId, final Map<String, U> entities, final Map<String, Long> entityRecordIds,
+                                       final ChangeType changeType, final InternalCallContext context) {
+        // Make sure to re-hydrate the object (especially needed for create calls)
+        final U reHydratedEntity = sqlDao.getById(entityId, context);
+        final Long reHydratedEntityRecordId = sqlDao.getRecordId(entityId, context);
+        final U entity = Objects.firstNonNull(reHydratedEntity, entities.get(entityId));
+        final Long entityRecordId = Objects.firstNonNull(reHydratedEntityRecordId, entityRecordIds.get(entityId));
+
+        final TableName tableName = retrieveTableNameFromEntity(entity);
+
+        // Note: audit entries point to the history record id
+        final Long historyRecordId;
+        if (tableName.getHistoryTableName() != null) {
+            historyRecordId = insertHistory(entityRecordId, entity, changeType, context);
+        } else {
+            historyRecordId = entityRecordId;
+        }
+
+        insertAudits(tableName, historyRecordId, changeType, context);
+    }
+
+    private List<String> retrieveEntityIdsFromArguments(final Method method, final Object[] args) {
+        final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
+
+        int i = -1;
+        for (final Object arg : args) {
+            i++;
+
+            // Assume the first argument of type Entity is our type of Entity (type U here)
+            // This is true for e.g. create calls
+            if (arg instanceof Entity) {
+                return ImmutableList.<String>of(((Entity) arg).getId().toString());
+            }
+
+            // For Batch calls, the first argument will be of type List<Entity>
+            if (arg instanceof Iterable) {
+                final Builder<String> entityIds = extractEntityIdsFromBatchArgument((Iterable) arg);
+                if (entityIds != null) {
+                    return entityIds.build();
+                }
+            }
+
+            // Otherwise, use the first String argument, annotated with @Bind("id")
+            // This is true for e.g. update calls
+            if (!(arg instanceof String)) {
+                continue;
+            }
+
+            for (final Annotation annotation : parameterAnnotations[i]) {
+                if (Bind.class.equals(annotation.annotationType()) && ("id").equals(((Bind) annotation).value())) {
+                    return ImmutableList.<String>of((String) arg);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    private Builder<String> extractEntityIdsFromBatchArgument(final Iterable arg) {
+        final Iterator iterator = arg.iterator();
+        final Builder<String> entityIds = new Builder<String>();
+        while (iterator.hasNext()) {
+            final Object object = iterator.next();
+            if (!(object instanceof Entity)) {
+                // No good - ignore
+                return null;
+            } else {
+                entityIds.add(((Entity) object).getId().toString());
+            }
+        }
+
+        return entityIds;
+    }
+
+    private InternalCallContext retrieveContextFromArguments(final Method method, final Object[] args) {
+        final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
+
+        int i = -1;
+        for (final Object arg : args) {
+            i++;
+            if (!(arg instanceof InternalCallContext)) {
+                continue;
+            }
+
+            for (final Annotation annotation : parameterAnnotations[i]) {
+                if (InternalTenantContextBinder.class.equals(annotation.annotationType())) {
+                    return (InternalCallContext) arg;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    private TableName retrieveTableNameFromEntity(final U entity) {
+        final TableName tableName = TableName.fromEntityClass(entity.getClass());
+        if (tableName == null) {
+            // Currently, this is only for EntitlementEvent which is not accessible from TableName (util)
+            // TODO what should we do about it?
+            return TableName.SUBSCRIPTION_EVENTS;
+        } else {
+            return tableName;
+        }
+    }
+
+    private Long insertHistory(final Long entityRecordId, final U entity, final ChangeType changeType, final InternalCallContext context) {
+        final EntityHistory<U> history = new EntityHistory<U>(entity.getId(), entityRecordId, entity, changeType);
+        sqlDao.addHistoryFromTransaction(history, context);
+        return sqlDao.getHistoryRecordId(entityRecordId, context);
+    }
+
+    private void insertAudits(final TableName tableName, final Long historyRecordId, final ChangeType changeType, final InternalCallContext context) {
+        final EntityAudit audit = new EntityAudit(tableName, historyRecordId, changeType);
+        sqlDao.insertAuditFromTransaction(audit, context);
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/UpdatableEntitySqlDao.java b/util/src/main/java/com/ning/billing/util/entity/dao/UpdatableEntitySqlDao.java
index 47e8e90..d9c2ca1 100644
--- a/util/src/main/java/com/ning/billing/util/entity/dao/UpdatableEntitySqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/UpdatableEntitySqlDao.java
@@ -20,12 +20,11 @@ import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.dao.AuditSqlDao;
 import com.ning.billing.util.dao.EntityHistory;
 import com.ning.billing.util.entity.Entity;
 
 // This interface needs to be extended by an interface that provides (externalized) sql and object binders and mappers
-public interface UpdatableEntitySqlDao<T extends Entity> extends EntitySqlDao<T>, AuditSqlDao {
+public interface UpdatableEntitySqlDao<T extends Entity> extends EntitySqlDao<T> {
 
     @SqlUpdate
     public void update(final T entity,
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java
index ad50c0e..26c24bb 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java
@@ -22,8 +22,6 @@ import java.util.List;
 import java.util.UUID;
 
 import org.skife.jdbi.v2.IDBI;
-import org.skife.jdbi.v2.Transaction;
-import org.skife.jdbi.v2.TransactionStatus;
 import org.skife.jdbi.v2.exceptions.TransactionFailedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -32,6 +30,10 @@ import com.ning.billing.ErrorCode;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.events.TagDefinitionInternalEvent;
 import com.ning.billing.util.svcsapi.bus.InternalBus;
 import com.ning.billing.util.tag.ControlTagType;
@@ -47,6 +49,7 @@ public class DefaultTagDefinitionDao implements TagDefinitionDao {
 
     private static final Logger log = LoggerFactory.getLogger(DefaultTagDefinitionDao.class);
 
+    private final EntitySqlDaoTransactionalJdbiWrapper transactionalSqlDao;
     private final TagDefinitionSqlDao tagDefinitionSqlDao;
     private final TagEventBuilder tagEventBuilder;
     private final InternalBus bus;
@@ -56,6 +59,7 @@ public class DefaultTagDefinitionDao implements TagDefinitionDao {
         this.tagEventBuilder = tagEventBuilder;
         this.bus = bus;
         this.tagDefinitionSqlDao = dbi.onDemand(TagDefinitionSqlDao.class);
+        this.transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi);
     }
 
     @Override
@@ -123,9 +127,11 @@ public class DefaultTagDefinitionDao implements TagDefinitionDao {
         }
 
         try {
-            return tagDefinitionSqlDao.inTransaction(new Transaction<TagDefinition, TagDefinitionSqlDao>() {
+            return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<TagDefinition>() {
                 @Override
-                public TagDefinition inTransaction(final TagDefinitionSqlDao tagDefinitionSqlDao, final TransactionStatus status) throws Exception {
+                public TagDefinition inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                    final TagDefinitionSqlDao tagDefinitionSqlDao = entitySqlDaoWrapperFactory.become(TagDefinitionSqlDao.class);
+
                     // Make sure the tag definition doesn't exist already
                     final TagDefinition existingDefinition = tagDefinitionSqlDao.getByName(definitionName, context);
                     if (existingDefinition != null) {
@@ -174,9 +180,11 @@ public class DefaultTagDefinitionDao implements TagDefinitionDao {
     @Override
     public void deleteById(final UUID definitionId, final InternalCallContext context) throws TagDefinitionApiException {
         try {
-            tagDefinitionSqlDao.inTransaction(new Transaction<Void, TagDefinitionSqlDao>() {
+            transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
                 @Override
-                public Void inTransaction(final TagDefinitionSqlDao tagDefinitionSqlDao, final TransactionStatus status) throws Exception {
+                public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                    final TagDefinitionSqlDao tagDefinitionSqlDao = entitySqlDaoWrapperFactory.become(TagDefinitionSqlDao.class);
+
                     // Make sure the tag definition exists
                     final TagDefinition tagDefinition = tagDefinitionSqlDao.getById(definitionId.toString(), context);
                     if (tagDefinition == null) {
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.java
index 3df4c49..b84f080 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.java
@@ -36,24 +36,26 @@ import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.callcontext.InternalTenantContextBinder;
+import com.ning.billing.util.dao.EntityHistory;
+import com.ning.billing.util.entity.dao.Audited;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
 import com.ning.billing.util.tag.DefaultTagDefinition;
 import com.ning.billing.util.tag.TagDefinition;
 
 @ExternalizedSqlViaStringTemplate3
 @RegisterMapper(TagDefinitionSqlDao.TagDefinitionMapper.class)
-public interface TagDefinitionSqlDao extends EntitySqlDao<TagDefinition>, Transactional<TagDefinitionSqlDao>, Transmogrifier {
+public interface TagDefinitionSqlDao extends EntitySqlDao<TagDefinition> {
 
     @Override
     @SqlUpdate
+    @Audited(ChangeType.INSERT)
     public void create(@TagDefinitionBinder final TagDefinition entity,
                        @InternalTenantContextBinder final InternalCallContext context);
 
@@ -62,6 +64,7 @@ public interface TagDefinitionSqlDao extends EntitySqlDao<TagDefinition>, Transa
                                    @InternalTenantContextBinder final InternalTenantContext context);
 
     @SqlUpdate
+    @Audited(ChangeType.DELETE)
     public void deleteTagDefinition(@Bind("id") final String definitionId,
                                     @InternalTenantContextBinder final InternalCallContext context);
 
@@ -73,6 +76,11 @@ public interface TagDefinitionSqlDao extends EntitySqlDao<TagDefinition>, Transa
     public List<TagDefinition> getByIds(@UUIDCollectionBinder final Collection<String> definitionIds,
                                         @InternalTenantContextBinder final InternalTenantContext context);
 
+    @Override
+    @SqlUpdate
+    public void addHistoryFromTransaction(@TagDefinitionHistoryBinder final EntityHistory<TagDefinition> tagDefinition,
+                                          @InternalTenantContextBinder final InternalCallContext context);
+
     public class TagDefinitionMapper implements ResultSetMapper<TagDefinition> {
 
         @Override
@@ -104,4 +112,29 @@ public interface TagDefinitionSqlDao extends EntitySqlDao<TagDefinition>, Transa
             }
         }
     }
+
+    @BindingAnnotation(TagDefinitionHistoryBinder.TagDefinitionHistoryBinderFactory.class)
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.PARAMETER})
+    public @interface TagDefinitionHistoryBinder {
+
+        public static class TagDefinitionHistoryBinderFactory implements BinderFactory {
+
+            @Override
+            public Binder<TagDefinitionHistoryBinder, EntityHistory<TagDefinition>> build(final Annotation annotation) {
+                return new Binder<TagDefinitionHistoryBinder, EntityHistory<TagDefinition>>() {
+                    @Override
+                    public void bind(final SQLStatement<?> q, final TagDefinitionHistoryBinder bind, final EntityHistory<TagDefinition> history) {
+                        q.bind("recordId", history.getValue());
+                        q.bind("changeType", history.getChangeType().toString());
+
+                        final TagDefinition tagDefinition = history.getEntity();
+                        q.bind("id", tagDefinition.getId().toString());
+                        q.bind("name", tagDefinition.getName());
+                        q.bind("description", tagDefinition.getDescription());
+                    }
+                };
+            }
+        }
+    }
 }
diff --git a/util/src/main/resources/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.sql.stg
index 9fb5bf2..0a869f5 100644
--- a/util/src/main/resources/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.sql.stg
@@ -57,6 +57,27 @@ getByName() ::= <<
   ;
 >>
 
+getRecordId() ::= <<
+    SELECT record_id
+    FROM tag_definitions
+    WHERE id = :id
+    <AND_CHECK_TENANT()>
+    ;
+>>
+
+getByRecordId() ::= <<
+    SELECT <fields()>
+    FROM tag_definitions
+    WHERE record_id = :recordId <AND_CHECK_TENANT()>
+    ;
+>>
+
+getHistoryRecordId() ::= <<
+    SELECT MAX(history_record_id)
+    FROM tag_definition_history
+    WHERE record_id = :recordId <AND_CHECK_TENANT()>;
+>>
+
 getById() ::= <<
   SELECT <fields()>
   FROM tag_definitions
@@ -71,4 +92,38 @@ getByIds(tag_definition_ids) ::= <<
   WHERE id IN (<tag_definition_ids: {id | :id_<i0>}; separator="," >)
   <AND_CHECK_TENANT()>
 >>
-;
+
+auditFields(prefix) ::= <<
+    <prefix>table_name,
+    <prefix>record_id,
+    <prefix>change_type,
+    <prefix>change_date,
+    <prefix>changed_by,
+    <prefix>reason_code,
+    <prefix>comments,
+    <prefix>user_token,
+    <prefix>account_record_id,
+    <prefix>tenant_record_id
+>>
+
+insertAuditFromTransaction() ::= <<
+    INSERT INTO audit_log(<auditFields()>)
+    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, :reasonCode, :comment, :userToken, :accountRecordId, :tenantRecordId);
+>>
+
+historyFields() ::= <<
+    record_id,
+    id,
+    name,
+    created_by,
+    description,
+    change_type,
+    updated_by,
+    date,
+    tenant_record_id
+>>
+
+addHistoryFromTransaction() ::= <<
+    INSERT INTO tag_definition_history(<historyFields()>)
+    VALUES (:recordId, :id, :name, :userName, :description, :changeType, :userName, :createdDate, :tenantRecordId);
+>>