killbill-aplcache

Merge branch 'audit-rework' of github.com:killbill/killbill

11/8/2012 11:53:33 PM

Changes

util/src/main/java/com/ning/billing/util/customfield/DefaultFieldStore.java 53(+0 -53)

Details

diff --git a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
index b1e3353..31eec47 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
@@ -62,7 +62,7 @@ public class DefaultAccountUserApi implements AccountUserApi {
 
         try {
             accountDao.create(account, internalCallContextFactory.createInternalCallContext(account.getId(), context));
-        } catch (EntityPersistenceException e) {
+        } catch (AccountApiException e) {
             throw new AccountApiException(e, ErrorCode.ACCOUNT_CREATION_FAILED);
         }
 
@@ -142,7 +142,7 @@ public class DefaultAccountUserApi implements AccountUserApi {
             for (final String cur : data.getAdditionalContactEmails()) {
                 addEmail(account.getId(), new DefaultAccountEmail(account.getId(), cur), migrationContext);
             }
-        } catch (EntityPersistenceException e) {
+        } catch (AccountApiException e) {
             throw new AccountApiException(e, ErrorCode.ACCOUNT_CREATION_FAILED);
         }
 
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountDao.java
index ee9edb0..4c2002e 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountDao.java
@@ -26,7 +26,7 @@ import com.ning.billing.util.entity.EntityPersistenceException;
 import com.ning.billing.util.entity.dao.EntityDao;
 import com.ning.billing.util.entity.dao.UpdatableEntityDao;
 
-public interface AccountDao extends EntityDao<Account> {
+public interface AccountDao extends EntityDao<Account, AccountApiException> {
 
     public Account getAccountByKey(String key, InternalTenantContext context);
 
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountEmailDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountEmailDao.java
index 2abcbbb..5026e62 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountEmailDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountEmailDao.java
@@ -19,12 +19,13 @@ package com.ning.billing.account.dao;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountEmail;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.entity.dao.EntityDao;
 
-public interface AccountEmailDao extends EntityDao<AccountEmail> {
+public interface AccountEmailDao extends EntityDao<AccountEmail, AccountApiException> {
 
     public List<AccountEmail> getEmails(UUID accountId, InternalTenantContext 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 e967067..4d3f0b3 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
@@ -16,7 +16,6 @@
 
 package com.ning.billing.account.dao;
 
-import java.sql.DataTruncation;
 import java.util.List;
 import java.util.UUID;
 
@@ -96,45 +95,36 @@ public class AuditedAccountDao implements AccountDao {
     }
 
     @Override
-    public void create(final Account account, final InternalCallContext context) throws EntityPersistenceException {
+    public void create(final Account account, final InternalCallContext context) throws AccountApiException {
         final String key = account.getExternalKey();
-        try {
-            transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
-                @Override
-                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);
-                    }
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws AccountApiException, InternalBus.EventBusException {
+                final AccountSqlDao transactionalDao = entitySqlDaoWrapperFactory.become(AccountSqlDao.class);
 
-                    transactionalDao.create(account, context);
+                final Account currentAccount = transactionalDao.getAccountByKey(key, context);
+                if (currentAccount != null) {
+                    throw new AccountApiException(ErrorCode.ACCOUNT_ALREADY_EXISTS, key);
+                }
 
-                    final Long recordId = accountSqlDao.getRecordId(account.getId().toString(), context);
-                    // We need to re-hydrate the context with the account record id
-                    final InternalCallContext rehydratedContext = internalCallContextFactory.createInternalCallContext(recordId, context);
-                    final AccountCreationInternalEvent creationEvent = new DefaultAccountCreationEvent(account,
-                                                                                                       rehydratedContext.getUserToken(),
-                                                                                                       context.getAccountRecordId(),
-                                                                                                       context.getTenantRecordId());
-                    try {
-                        eventBus.postFromTransaction(creationEvent, transactionalDao, rehydratedContext);
-                    } catch (final EventBusException e) {
-                        log.warn("Failed to post account creation event for account " + account.getId(), e);
-                    }
-                    return null;
+                transactionalDao.create(account, context);
+
+                final Long recordId = accountSqlDao.getRecordId(account.getId().toString(), context);
+                // We need to re-hydrate the context with the account record id
+                final InternalCallContext rehydratedContext = internalCallContextFactory.createInternalCallContext(recordId, context);
+                final AccountCreationInternalEvent creationEvent = new DefaultAccountCreationEvent(account,
+                                                                                                   rehydratedContext.getUserToken(),
+                                                                                                   context.getAccountRecordId(),
+                                                                                                   context.getTenantRecordId());
+                try {
+                    eventBus.postFromTransaction(creationEvent, entitySqlDaoWrapperFactory, rehydratedContext);
+                } catch (final EventBusException e) {
+                    log.warn("Failed to post account creation event for account " + account.getId(), e);
                 }
-            });
-        } catch (final RuntimeException re) {
-            if (re.getCause() instanceof EntityPersistenceException) {
-                throw (EntityPersistenceException) re.getCause();
-            } else if (re.getCause() instanceof DataTruncation) {
-                throw new EntityPersistenceException(ErrorCode.DATA_TRUNCATION, re.getCause().getMessage());
-            } else {
-                throw re;
+                return null;
             }
-        }
+        });
     }
 
     @Override
@@ -162,7 +152,7 @@ public class AuditedAccountDao implements AccountDao {
                                                                                              context.getTenantRecordId());
                 if (changeEvent.hasChanges()) {
                     try {
-                        eventBus.postFromTransaction(changeEvent, transactional, context);
+                        eventBus.postFromTransaction(changeEvent, entitySqlDaoWrapperFactory, context);
                     } catch (final EventBusException e) {
                         log.warn("Failed to post account change event for account " + accountId, e);
                     }
@@ -199,7 +189,7 @@ public class AuditedAccountDao implements AccountDao {
 
                     if (changeEvent.hasChanges()) {
                         try {
-                            eventBus.postFromTransaction(changeEvent, transactional, context);
+                            eventBus.postFromTransaction(changeEvent, entitySqlDaoWrapperFactory, context);
                         } catch (final EventBusException e) {
                             log.warn("Failed to post account change event for account " + accountId, e);
                         }
diff --git a/account/src/main/java/com/ning/billing/account/dao/AuditedAccountEmailDao.java b/account/src/main/java/com/ning/billing/account/dao/AuditedAccountEmailDao.java
index fd19c96..d3745b8 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AuditedAccountEmailDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AuditedAccountEmailDao.java
@@ -28,6 +28,7 @@ import org.skife.jdbi.v2.TransactionStatus;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 
 import com.ning.billing.ObjectType;
+import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountEmail;
 import com.ning.billing.account.api.DefaultAccountEmail;
 import com.ning.billing.util.callcontext.InternalCallContext;
@@ -52,7 +53,7 @@ public class AuditedAccountEmailDao extends AuditedCollectionDaoBase<AccountEmai
     }
 
     @Override
-    public void create(final AccountEmail entity, final InternalCallContext context) throws EntityPersistenceException {
+    public void create(final AccountEmail entity, final InternalCallContext context) throws AccountApiException {
         saveEmails(entity.getAccountId(), ImmutableList.<AccountEmail>of(entity), context);
     }
 
diff --git a/account/src/test/java/com/ning/billing/account/dao/MockAccountEmailDao.java b/account/src/test/java/com/ning/billing/account/dao/MockAccountEmailDao.java
index 8139bf0..1ddc50d 100644
--- a/account/src/test/java/com/ning/billing/account/dao/MockAccountEmailDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/MockAccountEmailDao.java
@@ -23,6 +23,7 @@ import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 
+import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountEmail;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
@@ -35,7 +36,7 @@ public class MockAccountEmailDao implements AccountEmailDao {
     private final Map<UUID, Set<AccountEmail>> emails = new ConcurrentHashMap<UUID, Set<AccountEmail>>();
 
     @Override
-    public void create(final AccountEmail entity, final InternalCallContext context) throws EntityPersistenceException {
+    public void create(final AccountEmail entity, final InternalCallContext context) throws AccountApiException {
         saveEmails(entity.getAccountId(), ImmutableList.<AccountEmail>of(entity), context);
     }
 
diff --git a/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java
index d7913c0..ea01b6a 100644
--- a/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java
@@ -39,6 +39,7 @@ import com.ning.billing.account.api.DefaultMutableAccountData;
 import com.ning.billing.account.api.MutableAccountData;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.mock.MockAccountBuilder;
+import com.ning.billing.util.api.CustomFieldApiException;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.clock.DefaultClock;
 import com.ning.billing.util.customfield.CustomField;
@@ -46,7 +47,10 @@ import com.ning.billing.util.customfield.StringCustomField;
 import com.ning.billing.util.customfield.dao.AuditedCustomFieldDao;
 import com.ning.billing.util.customfield.dao.CustomFieldDao;
 import com.ning.billing.util.entity.EntityPersistenceException;
+import com.ning.billing.util.tag.ControlTagType;
+import com.ning.billing.util.tag.DefaultControlTag;
 import com.ning.billing.util.tag.DefaultTagDefinition;
+import com.ning.billing.util.tag.DescriptiveTag;
 import com.ning.billing.util.tag.Tag;
 import com.ning.billing.util.tag.TagDefinition;
 import com.ning.billing.util.tag.dao.AuditedTagDao;
@@ -86,7 +90,7 @@ public class TestAccountDao extends AccountDaoTestBase {
     }
 
     @Test(groups = "slow")
-    public void testBasic() throws EntityPersistenceException {
+    public void testBasic() throws AccountApiException {
         final Account a = createTestAccount(5);
         accountDao.create(a, internalCallContext);
         final String key = a.getExternalKey();
@@ -106,7 +110,7 @@ public class TestAccountDao extends AccountDaoTestBase {
 
     // simple test to ensure long phone numbers can be stored
     @Test(groups = "slow")
-    public void testLongPhoneNumber() throws EntityPersistenceException {
+    public void testLongPhoneNumber() throws AccountApiException {
         final Account account = createTestAccount(1, "123456789012345678901234");
         accountDao.create(account, internalCallContext);
 
@@ -115,14 +119,14 @@ public class TestAccountDao extends AccountDaoTestBase {
     }
 
     // simple test to ensure excessively long phone numbers cannot be stored
-    @Test(groups = "slow", expectedExceptions = EntityPersistenceException.class)
-    public void testOverlyLongPhoneNumber() throws EntityPersistenceException {
+    @Test(groups = "slow", expectedExceptions = AccountApiException.class)
+    public void testOverlyLongPhoneNumber() throws AccountApiException {
         final Account account = createTestAccount(1, "12345678901234567890123456");
         accountDao.create(account, internalCallContext);
     }
 
     @Test(groups = "slow")
-    public void testGetById() throws EntityPersistenceException {
+    public void testGetById() throws AccountApiException {
         Account account = createTestAccount(1);
         final UUID id = account.getId();
         final String key = account.getExternalKey();
@@ -140,19 +144,19 @@ public class TestAccountDao extends AccountDaoTestBase {
     }
 
     @Test(groups = "slow")
-    public void testCustomFields() throws EntityPersistenceException {
+    public void testCustomFields() throws  CustomFieldApiException {
         final String fieldName = "testField1";
         final String fieldValue = "testField1_value";
 
         final UUID accountId = UUID.randomUUID();
-        final List<CustomField> customFields = new ArrayList<CustomField>();
-        customFields.add(new StringCustomField(fieldName, fieldValue));
-        final CustomFieldDao customFieldDao = new AuditedCustomFieldDao(dbi, new DefaultClock());
-        customFieldDao.saveEntities(accountId, ObjectType.ACCOUNT, customFields, internalCallContext);
 
-        final Map<String, CustomField> customFieldMap = customFieldDao.loadEntities(accountId, ObjectType.ACCOUNT, internalCallContext);
+        CustomField field = new StringCustomField(fieldName, fieldValue, ObjectType.ACCOUNT, accountId, internalCallContext.getCreatedDate());
+        final CustomFieldDao customFieldDao = new AuditedCustomFieldDao(dbi);
+        customFieldDao.create(field, internalCallContext);
+
+        final List<CustomField> customFieldMap = customFieldDao.getCustomFields(accountId, ObjectType.ACCOUNT, internalCallContext);
         assertEquals(customFieldMap.size(), 1);
-        final CustomField customField = customFieldMap.get(fieldName);
+        final CustomField customField = customFieldMap.get(0);
         assertEquals(customField.getName(), fieldName);
         assertEquals(customField.getValue(), fieldValue);
     }
@@ -161,20 +165,26 @@ public class TestAccountDao extends AccountDaoTestBase {
     public void testTags() throws EntityPersistenceException, TagApiException {
         final Account account = createTestAccount(1);
         final TagDefinition definition = new DefaultTagDefinition("Test Tag", "For testing only", false);
-        final TagDefinitionSqlDao tagDescriptionDao = dbi.onDemand(TagDefinitionSqlDao.class);
-        tagDescriptionDao.create(definition, internalCallContext);
+        final TagDefinitionSqlDao tagDefinitionDao = dbi.onDemand(TagDefinitionSqlDao.class);
+        tagDefinitionDao.create(definition, internalCallContext);
 
         final TagDao tagDao = new AuditedTagDao(dbi, tagEventBuilder, bus, new DefaultClock());
-        tagDao.insertTag(account.getId(), ObjectType.ACCOUNT, definition.getId(), internalCallContext);
 
-        final Map<String, Tag> tagMap = tagDao.getTags(account.getId(), ObjectType.ACCOUNT, internalCallContext);
-        assertEquals(tagMap.size(), 1);
 
-        assertEquals(tagMap.values().iterator().next().getTagDefinitionId(), definition.getId());
+        final TagDefinition tagDefinition = tagDefinitionDao.getById(definition.getId().toString(), internalCallContext);
+        final Tag tag = tagDefinition.isControlTag() ? new DefaultControlTag(ControlTagType.getTypeFromId(tagDefinition.getId()), ObjectType.ACCOUNT, account.getId(), internalCallContext.getCreatedDate()) :
+                        new DescriptiveTag(tagDefinition.getId(), ObjectType.ACCOUNT, account.getId(), internalCallContext.getCreatedDate());
+
+        tagDao.create(tag, internalCallContext);
+
+        final List<Tag> tags = tagDao.getTags(account.getId(), ObjectType.ACCOUNT, internalCallContext);
+        assertEquals(tags.size(), 1);
+
+        assertEquals(tags.get(0).getTagDefinitionId(), definition.getId());
     }
 
     @Test(groups = "slow")
-    public void testGetIdFromKey() throws EntityPersistenceException {
+    public void testGetIdFromKey() throws AccountApiException {
         final Account account = createTestAccount(1);
         accountDao.create(account, internalCallContext);
 
diff --git a/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java b/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java
index cc434c9..8449b6f 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java
@@ -353,7 +353,7 @@ public class DefaultAnalyticsUserApi implements AnalyticsUserApi {
     private void updateTags(final Account account, final InternalCallContext internalCallContext) {
         // Find the current state of tags from util
         final List<TagDefinition> tagDefinitions = tagInternalApi.getTagDefinitions(internalCallContext);
-        final Collection<String> utilTags = Collections2.transform(tagInternalApi.getTags(account.getId(), ObjectType.ACCOUNT, internalCallContext).values(),
+        final Collection<String> utilTags = Collections2.transform(tagInternalApi.getTags(account.getId(), ObjectType.ACCOUNT, internalCallContext),
                                                                    new Function<Tag, String>() {
                                                                        @Override
                                                                        public String apply(@Nullable final Tag input) {
diff --git a/api/src/main/java/com/ning/billing/ErrorCode.java b/api/src/main/java/com/ning/billing/ErrorCode.java
index 3f85b27..0230f96 100644
--- a/api/src/main/java/com/ning/billing/ErrorCode.java
+++ b/api/src/main/java/com/ning/billing/ErrorCode.java
@@ -175,6 +175,8 @@ public enum ErrorCode {
     */
     TAG_DOES_NOT_EXIST(3950, "The tag does not exist (name: %s)"),
     TAG_CANNOT_BE_REMOVED(3951, "The tag %s cannot be removed because %s"),
+    TAG_ALREADY_EXISTS(3952, "The tag %s already exists"),
+
 
     /*
     *
@@ -287,6 +289,9 @@ public enum ErrorCode {
     TENANT_UPDATE_FAILED(10005, "Tenant update failed."),
     TENANT_NO_SUCH_KEY(10006, "Tenant %s does not have a key %s"),
 
+
+    CUSTOM_FIELD_ALREADY_EXISTS(11000, "The custom field %s already exists"),
+
     __UNKNOWN_ERROR_CODE(-1, "Unknown ErrorCode");
 
     private final int code;
diff --git a/api/src/main/java/com/ning/billing/util/api/CustomFieldUserApi.java b/api/src/main/java/com/ning/billing/util/api/CustomFieldUserApi.java
index c233a44..bb56fba 100644
--- a/api/src/main/java/com/ning/billing/util/api/CustomFieldUserApi.java
+++ b/api/src/main/java/com/ning/billing/util/api/CustomFieldUserApi.java
@@ -20,6 +20,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import com.ning.billing.BillingExceptionBase;
 import com.ning.billing.ObjectType;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.TenantContext;
@@ -27,7 +28,7 @@ import com.ning.billing.util.customfield.CustomField;
 
 public interface CustomFieldUserApi {
 
-    Map<String, CustomField> getCustomFields(UUID objectId, ObjectType objectType, TenantContext context);
+    List<CustomField> getCustomFields(UUID objectId, ObjectType objectType, TenantContext context);
 
-    void saveCustomFields(UUID objectId, ObjectType objectType, List<CustomField> fields, CallContext context);
+    void addCustomFields(List<CustomField> fields, CallContext context) throws CustomFieldApiException;
 }
diff --git a/api/src/main/java/com/ning/billing/util/api/TagUserApi.java b/api/src/main/java/com/ning/billing/util/api/TagUserApi.java
index cde9535..99df733 100644
--- a/api/src/main/java/com/ning/billing/util/api/TagUserApi.java
+++ b/api/src/main/java/com/ning/billing/util/api/TagUserApi.java
@@ -71,5 +71,5 @@ public interface TagUserApi {
      * @param context    The tenant context
      * @return A map of tag, key being the tagId, and value the tag
      */
-    public Map<String, Tag> getTags(UUID objectId, ObjectType objectType, TenantContext context);
+    public List<Tag> getTags(UUID objectId, ObjectType objectType, TenantContext context);
 }
diff --git a/api/src/main/java/com/ning/billing/util/callcontext/CallContext.java b/api/src/main/java/com/ning/billing/util/callcontext/CallContext.java
index bb0270d..d14e062 100644
--- a/api/src/main/java/com/ning/billing/util/callcontext/CallContext.java
+++ b/api/src/main/java/com/ning/billing/util/callcontext/CallContext.java
@@ -35,7 +35,7 @@ public interface CallContext extends TenantContext {
 
     public String getReasonCode();
 
-    public String getComment();
+    public String getComments();
 
     public DateTime getCreatedDate();
 
diff --git a/api/src/main/java/com/ning/billing/util/customfield/CustomField.java b/api/src/main/java/com/ning/billing/util/customfield/CustomField.java
index 709be83..566dcd5 100644
--- a/api/src/main/java/com/ning/billing/util/customfield/CustomField.java
+++ b/api/src/main/java/com/ning/billing/util/customfield/CustomField.java
@@ -16,9 +16,17 @@
 
 package com.ning.billing.util.customfield;
 
+import java.util.UUID;
+
+import com.ning.billing.ObjectType;
 import com.ning.billing.util.entity.Entity;
 
 public interface CustomField extends Entity {
+
+    public UUID getObjectId();
+
+    public ObjectType getObjectType();
+
     public String getName();
 
     public String getValue();
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
index 14cde9d..8cc65be 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
@@ -17,6 +17,7 @@
 package com.ning.billing.beatrix.integration;
 
 import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
@@ -175,7 +176,7 @@ public class TestIntegrationWithAutoInvoiceOffTag extends TestIntegrationBase {
 
     private void add_AUTO_INVOICING_OFF_Tag(final UUID id, final ObjectType type) throws TagDefinitionApiException, TagApiException {
         tagApi.addTag(id, type, ControlTagType.AUTO_INVOICING_OFF.getId(), callContext);
-        final Map<String, Tag> tags = tagApi.getTags(id, type, callContext);
+        final List<Tag> tags = tagApi.getTags(id, type, callContext);
         assertEquals(tags.size(), 1);
     }
 
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java
index 4871538..0b4b925 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java
@@ -17,6 +17,7 @@ package com.ning.billing.beatrix.integration;
 
 import java.math.BigDecimal;
 import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
@@ -280,7 +281,7 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
 
     private void add_AUTO_PAY_OFF_Tag(final UUID id, final ObjectType type) throws TagDefinitionApiException, TagApiException {
         tagApi.addTag(id, type, ControlTagType.AUTO_PAY_OFF.getId(), callContext);
-        final Map<String, Tag> tags = tagApi.getTags(id, type, callContext);
+        final List<Tag> tags = tagApi.getTags(id, type, callContext);
         assertEquals(tags.size(), 1);
     }
 
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 333d56a..ec04732 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
@@ -217,15 +217,15 @@ public class AuditedEntitlementDao implements EntitlementDao {
             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);
+                cancelNextPhaseEventFromTransaction(subscriptionId, entitySqlDaoWrapperFactory, context);
                 transactional.insertEvent(nextPhase, context);
-                recordFutureNotificationFromTransaction(transactional,
+                recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory,
                                                         nextPhase.getEffectiveDate(),
                                                         new EntitlementNotificationKey(nextPhase.getId()),
                                                         context);
 
                 // Notify the Bus of the requested change
-                notifyBusOfRequestedChange(transactional, subscription, nextPhase, context);
+                notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, nextPhase, context);
 
                 return null;
             }
@@ -283,14 +283,14 @@ public class AuditedEntitlementDao implements EntitlementDao {
                 final EntitlementEventSqlDao eventsDaoFromSameTransaction = entitySqlDaoWrapperFactory.become(EntitlementEventSqlDao.class);
                 for (final EntitlementEvent cur : initialEvents) {
                     eventsDaoFromSameTransaction.insertEvent(cur, context);
-                    recordFutureNotificationFromTransaction(transactional,
+                    recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory,
                                                             cur.getEffectiveDate(),
                                                             new EntitlementNotificationKey(cur.getId()),
                                                             context);
                 }
                 // Notify the Bus of the latest requested change, if needed
                 if (initialEvents.size() > 0) {
-                    notifyBusOfRequestedChange(eventsDaoFromSameTransaction, subscription, initialEvents.get(initialEvents.size() - 1), context);
+                    notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, initialEvents.get(initialEvents.size() - 1), context);
                 }
                 return null;
             }
@@ -306,7 +306,7 @@ public class AuditedEntitlementDao implements EntitlementDao {
 
                 for (final EntitlementEvent cur : recreateEvents) {
                     transactional.insertEvent(cur, context);
-                    recordFutureNotificationFromTransaction(transactional,
+                    recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory,
                                                             cur.getEffectiveDate(),
                                                             new EntitlementNotificationKey(cur.getId()),
                                                             context);
@@ -314,7 +314,7 @@ public class AuditedEntitlementDao implements EntitlementDao {
                 }
 
                 // Notify the Bus of the latest requested change
-                notifyBusOfRequestedChange(transactional, subscription, recreateEvents.get(recreateEvents.size() - 1), context);
+                notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, recreateEvents.get(recreateEvents.size() - 1), context);
 
                 return null;
             }
@@ -326,8 +326,7 @@ public class AuditedEntitlementDao implements EntitlementDao {
         transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
             public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                final EntitlementEventSqlDao transactional = entitySqlDaoWrapperFactory.become(EntitlementEventSqlDao.class);
-                cancelSubscriptionFromTransaction(subscription, cancelEvent, transactional, context, seqId);
+                cancelSubscriptionFromTransaction(subscription, cancelEvent, entitySqlDaoWrapperFactory, context, seqId);
                 return null;
             }
         });
@@ -359,14 +358,14 @@ public class AuditedEntitlementDao implements EntitlementDao {
                     transactional.unactiveEvent(cancelledEventId, context);
                     for (final EntitlementEvent cur : uncancelEvents) {
                         transactional.insertEvent(cur, context);
-                        recordFutureNotificationFromTransaction(transactional,
+                        recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory,
                                                                 cur.getEffectiveDate(),
                                                                 new EntitlementNotificationKey(cur.getId()),
                                                                 context);
                     }
 
                     // Notify the Bus of the latest requested change
-                    notifyBusOfRequestedChange(transactional, subscription, uncancelEvents.get(uncancelEvents.size() - 1), context);
+                    notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, uncancelEvents.get(uncancelEvents.size() - 1), context);
                 }
 
                 return null;
@@ -382,11 +381,11 @@ public class AuditedEntitlementDao implements EntitlementDao {
                 final EntitlementEventSqlDao transactional = entitySqlDaoWrapperFactory.become(EntitlementEventSqlDao.class);
 
                 final UUID subscriptionId = subscription.getId();
-                cancelFutureEventsFromTransaction(subscriptionId, transactional, context);
+                cancelFutureEventsFromTransaction(subscriptionId, entitySqlDaoWrapperFactory, context);
 
                 for (final EntitlementEvent cur : changeEvents) {
                     transactional.insertEvent(cur, context);
-                    recordFutureNotificationFromTransaction(transactional,
+                    recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory,
                                                             cur.getEffectiveDate(),
                                                             new EntitlementNotificationKey(cur.getId()),
                                                             context);
@@ -394,43 +393,43 @@ public class AuditedEntitlementDao implements EntitlementDao {
 
                 // Notify the Bus of the latest requested change
                 final EntitlementEvent finalEvent = changeEvents.get(changeEvents.size() - 1);
-                notifyBusOfRequestedChange(transactional, subscription, finalEvent, context);
+                notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, finalEvent, context);
 
                 return null;
             }
         });
     }
 
-    private void cancelSubscriptionFromTransaction(final SubscriptionData subscription, final EntitlementEvent cancelEvent, final EntitlementEventSqlDao transactional, final InternalCallContext context, final int seqId) {
+    private void cancelSubscriptionFromTransaction(final SubscriptionData subscription, final EntitlementEvent cancelEvent, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalCallContext context, final int seqId) {
         final UUID subscriptionId = subscription.getId();
-        cancelFutureEventsFromTransaction(subscriptionId, transactional, context);
-        transactional.insertEvent(cancelEvent, context);
-        recordFutureNotificationFromTransaction(transactional,
+        cancelFutureEventsFromTransaction(subscriptionId, entitySqlDaoWrapperFactory, context);
+        entitySqlDaoWrapperFactory.become(EntitlementEventSqlDao.class).insertEvent(cancelEvent, context);
+        recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory,
                                                 cancelEvent.getEffectiveDate(),
                                                 new EntitlementNotificationKey(cancelEvent.getId(), seqId),
                                                 context);
 
         // Notify the Bus of the requested change
-        notifyBusOfRequestedChange(transactional, subscription, cancelEvent, context);
+        notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, cancelEvent, context);
     }
 
-    private void cancelNextPhaseEventFromTransaction(final UUID subscriptionId, final EntitlementEventSqlDao dao, final InternalCallContext context) {
-        cancelFutureEventFromTransaction(subscriptionId, dao, EventType.PHASE, null, context);
+    private void cancelNextPhaseEventFromTransaction(final UUID subscriptionId, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalCallContext context) {
+        cancelFutureEventFromTransaction(subscriptionId, entitySqlDaoWrapperFactory, EventType.PHASE, null, context);
     }
 
-    private void cancelFutureEventsFromTransaction(final UUID subscriptionId, final EntitlementEventSqlDao dao, final InternalCallContext context) {
+    private void cancelFutureEventsFromTransaction(final UUID subscriptionId, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalCallContext context) {
         final Date now = clock.getUTCNow().toDate();
-        final List<EntitlementEvent> events = dao.getFutureActiveEventForSubscription(subscriptionId.toString(), now, context);
+        final List<EntitlementEvent> events = entitySqlDaoWrapperFactory.become(EntitlementEventSqlDao.class).getFutureActiveEventForSubscription(subscriptionId.toString(), now, context);
         for (final EntitlementEvent cur : events) {
-            unactivateEventFromTransaction(cur, dao, context);
+            unactivateEventFromTransaction(cur, entitySqlDaoWrapperFactory, context);
         }
     }
 
-    private void cancelFutureEventFromTransaction(final UUID subscriptionId, final EntitlementEventSqlDao dao, final EventType type,
+    private void cancelFutureEventFromTransaction(final UUID subscriptionId, final EntitySqlDaoWrapperFactory<EntitySqlDao> dao, final EventType type,
                                                   @Nullable final ApiEventType apiType, final InternalCallContext context) {
         EntitlementEvent futureEvent = null;
         final Date now = clock.getUTCNow().toDate();
-        final List<EntitlementEvent> events = dao.getFutureActiveEventForSubscription(subscriptionId.toString(), now, context);
+        final List<EntitlementEvent> events = dao.become(EntitlementEventSqlDao.class).getFutureActiveEventForSubscription(subscriptionId.toString(), now, context);
         for (final EntitlementEvent cur : events) {
             if (cur.getType() == type &&
                 (apiType == null || apiType == ((ApiEvent) cur).getEventType())) {
@@ -444,10 +443,10 @@ public class AuditedEntitlementDao implements EntitlementDao {
         unactivateEventFromTransaction(futureEvent, dao, context);
     }
 
-    private void unactivateEventFromTransaction(final EntitlementEvent event, final EntitlementEventSqlDao dao, final InternalCallContext context) {
+    private void unactivateEventFromTransaction(final EntitlementEvent event, final EntitySqlDaoWrapperFactory<EntitySqlDao> dao, final InternalCallContext context) {
         if (event != null) {
             final String eventId = event.getId().toString();
-            dao.unactiveEvent(eventId, context);
+            dao.become(EntitlementEventSqlDao.class).unactiveEvent(eventId, context);
         }
     }
 
@@ -584,7 +583,7 @@ public class AuditedEntitlementDao implements EntitlementDao {
                     for (final EntitlementEvent event : cur.getNewEvents()) {
                         transEventDao.insertEvent(event, context);
                         if (event.getEffectiveDate().isAfter(clock.getUTCNow())) {
-                            recordFutureNotificationFromTransaction(transactional,
+                            recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory,
                                                                     event.getEffectiveDate(),
                                                                     new EntitlementNotificationKey(event.getId()),
                                                                     context);
@@ -596,7 +595,7 @@ public class AuditedEntitlementDao implements EntitlementDao {
                     // Note: we don't send a requested change event here, but a repair event
                     final RepairEntitlementInternalEvent busEvent = new DefaultRepairEntitlementEvent(context.getUserToken(), accountId, bundleId, clock.getUTCNow(),
                             context.getAccountRecordId(), context.getTenantRecordId());
-                    eventBus.postFromTransaction(busEvent, transactional, context);
+                    eventBus.postFromTransaction(busEvent, entitySqlDaoWrapperFactory, context);
                 } catch (EventBusException e) {
                     log.warn("Failed to post repair entitlement event for bundle " + bundleId, e);
                 }
@@ -617,7 +616,7 @@ public class AuditedEntitlementDao implements EntitlementDao {
 
                 // Cancel the subscriptions for the old bundle
                 for (final TransferCancelData cancel : transferCancelData) {
-                    cancelSubscriptionFromTransaction(cancel.getSubscription(), cancel.getCancelEvent(), transactional, context, 0);
+                    cancelSubscriptionFromTransaction(cancel.getSubscription(), cancel.getCancelEvent(), entitySqlDaoWrapperFactory, context, 0);
                 }
 
                 migrateBundleDataFromTransaction(bundleTransferData, transactional, entitySqlDaoWrapperFactory, context);
@@ -637,12 +636,12 @@ public class AuditedEntitlementDao implements EntitlementDao {
         return null;
     }
 
-    private void recordFutureNotificationFromTransaction(final Transmogrifier transactionalDao, final DateTime effectiveDate,
+    private void recordFutureNotificationFromTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final DateTime effectiveDate,
                                                          final NotificationKey notificationKey, final InternalCallContext context) {
         try {
             final NotificationQueue subscriptionEventQueue = notificationQueueService.getNotificationQueue(Engine.ENTITLEMENT_SERVICE_NAME,
                                                                                                            Engine.NOTIFICATION_QUEUE_NAME);
-            subscriptionEventQueue.recordFutureNotificationFromTransaction(transactionalDao, effectiveDate, null, notificationKey, context);
+            subscriptionEventQueue.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory, effectiveDate, null, notificationKey, context);
         } catch (NoSuchNotificationQueue e) {
             throw new RuntimeException(e);
         } catch (IOException e) {
@@ -650,10 +649,10 @@ public class AuditedEntitlementDao implements EntitlementDao {
         }
     }
 
-    private void notifyBusOfRequestedChange(final EntitlementEventSqlDao transactional, final SubscriptionData subscription,
+    private void notifyBusOfRequestedChange(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final SubscriptionData subscription,
                                             final EntitlementEvent nextEvent, final InternalCallContext context) {
         try {
-            eventBus.postFromTransaction(new DefaultRequestedSubscriptionEvent(subscription, nextEvent, context.getAccountRecordId(), context.getTenantRecordId()), transactional, context);
+            eventBus.postFromTransaction(new DefaultRequestedSubscriptionEvent(subscription, nextEvent, context.getAccountRecordId(), context.getTenantRecordId()), entitySqlDaoWrapperFactory, context);
         } catch (EventBusException e) {
             log.warn("Failed to post requested change event for subscription " + subscription.getId(), e);
         }
@@ -677,7 +676,7 @@ public class AuditedEntitlementDao implements EntitlementDao {
             final SubscriptionData subData = curSubscription.getData();
             for (final EntitlementEvent curEvent : curSubscription.getInitialEvents()) {
                 transactional.insertEvent(curEvent, context);
-                recordFutureNotificationFromTransaction(transactional,
+                recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory,
                                                         curEvent.getEffectiveDate(),
                                                         new EntitlementNotificationKey(curEvent.getId()),
                                                         context);
@@ -686,7 +685,7 @@ public class AuditedEntitlementDao implements EntitlementDao {
 
             // Notify the Bus of the latest requested change
             final EntitlementEvent finalEvent = curSubscription.getInitialEvents().get(curSubscription.getInitialEvents().size() - 1);
-            notifyBusOfRequestedChange(transactional, subData, finalEvent, context);
+            notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subData, finalEvent, context);
         }
 
         transBundleDao.insertBundle(bundleData, context);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
index 2567e37..2acd5d5 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
@@ -54,6 +54,8 @@ import com.ning.billing.entitlement.events.user.ApiEventType;
 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.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.notificationq.NotificationKey;
 import com.ning.billing.util.notificationq.NotificationQueue;
 import com.ning.billing.util.notificationq.NotificationQueueService;
@@ -406,7 +408,7 @@ public class MockEntitlementDaoMemory implements EntitlementDao {
         return null;
     }
 
-    private void recordFutureNotificationFromTransaction(final Transmogrifier transactionalDao, final DateTime effectiveDate,
+    private void recordFutureNotificationFromTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> transactionalDao, final DateTime effectiveDate,
                                                          final NotificationKey notificationKey, final InternalCallContext context) {
         try {
             final NotificationQueue subscriptionEventQueue = notificationQueueService.getNotificationQueue(Engine.ENTITLEMENT_SERVICE_NAME,
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
index 3ea248f..71e9036 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -268,8 +268,8 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
 
         // Check if this account has the MANUAL_PAY system tag
         boolean manualPay = false;
-        final Map<String, Tag> accountTags = tagApi.getTags(account.getId(), ObjectType.ACCOUNT, internalContext);
-        for (final Tag tag : accountTags.values()) {
+        final List<Tag> accountTags = tagApi.getTags(account.getId(), ObjectType.ACCOUNT, internalContext);
+        for (final Tag tag : accountTags) {
             if (ControlTagType.MANUAL_PAY.getId().equals(tag.getTagDefinitionId())) {
                 manualPay = true;
                 break;
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 8a10ec0..a245c73 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
@@ -209,7 +209,7 @@ public class AuditedInvoiceDao implements InvoiceDao {
                     transInvoiceItemSqlDao.batchCreateFromTransaction(invoiceItems, context);
 
                     final List<InvoiceItem> recurringInvoiceItems = invoice.getInvoiceItems(RecurringInvoiceItem.class);
-                    notifyOfFutureBillingEvents(transactional, invoice.getAccountId(), recurringInvoiceItems);
+                    notifyOfFutureBillingEvents(entitySqlDaoWrapperFactory, invoice.getAccountId(), recurringInvoiceItems);
 
                     final List<InvoicePayment> invoicePayments = invoice.getPayments();
                     final InvoicePaymentSqlDao invoicePaymentSqlDao = entitySqlDaoWrapperFactory.become(InvoicePaymentSqlDao.class);
@@ -382,7 +382,7 @@ public class AuditedInvoiceDao implements InvoiceDao {
                 }
 
                 // Notify the bus since the balance of the invoice changed
-                notifyBusOfInvoiceAdjustment(transactional, invoice.getId(), invoice.getAccountId(), context.getUserToken(), context);
+                notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, invoice.getId(), invoice.getAccountId(), context.getUserToken(), context);
 
                 return refund;
             }
@@ -517,7 +517,7 @@ public class AuditedInvoiceDao implements InvoiceDao {
 
                     // 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);
+                    notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, payment.getInvoiceId(), accountId, context.getUserToken(), context);
 
                     return chargeBack;
                 }
@@ -604,7 +604,7 @@ public class AuditedInvoiceDao implements InvoiceDao {
                 }
 
                 // Notify the bus since the balance of the invoice changed
-                notifyBusOfInvoiceAdjustment(transactional, invoiceId, accountId, context.getUserToken(), context);
+                notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, invoiceId, accountId, context.getUserToken(), context);
 
                 return externalCharge;
             }
@@ -637,7 +637,7 @@ public class AuditedInvoiceDao implements InvoiceDao {
                 insertItemAndAddCBAIfNeeded(entitySqlDaoWrapperFactory, credit, context);
 
                 // Notify the bus since the balance of the invoice changed
-                notifyBusOfInvoiceAdjustment(transactional, invoiceId, accountId, context.getUserToken(), context);
+                notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, invoiceId, accountId, context.getUserToken(), context);
 
                 return credit;
             }
@@ -655,7 +655,7 @@ public class AuditedInvoiceDao implements InvoiceDao {
                 final InvoiceItem invoiceItemAdjustment = createAdjustmentItem(entitySqlDaoWrapperFactory, invoiceId, invoiceItemId, positiveAdjAmount,
                                                                                currency, effectiveDate, context);
                 insertItemAndAddCBAIfNeeded(entitySqlDaoWrapperFactory, invoiceItemAdjustment, context);
-                notifyBusOfInvoiceAdjustment(transactional, invoiceId, accountId, context.getUserToken(), context);
+                notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, invoiceId, accountId, context.getUserToken(), context);
 
                 return invoiceItemAdjustment;
             }
@@ -894,7 +894,7 @@ public class AuditedInvoiceDao implements InvoiceDao {
         invoice.addPayments(invoicePayments);
     }
 
-    private void notifyOfFutureBillingEvents(final InvoiceSqlDao dao, final UUID accountId, final List<InvoiceItem> invoiceItems) {
+    private void notifyOfFutureBillingEvents(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final UUID accountId, final List<InvoiceItem> invoiceItems) {
 
         for (final InvoiceItem item : invoiceItems) {
             if (item.getInvoiceItemType() == InvoiceItemType.RECURRING) {
@@ -905,18 +905,18 @@ public class AuditedInvoiceDao implements InvoiceDao {
                     //
                     // We insert a future notification for each recurring subscription at the end of the service period  = new CTD of the subscription
                     //
-                    nextBillingDatePoster.insertNextBillingNotification(dao, accountId, recurringInvoiceItem.getSubscriptionId(),
+                    nextBillingDatePoster.insertNextBillingNotification(entitySqlDaoWrapperFactory, accountId, recurringInvoiceItem.getSubscriptionId(),
                             recurringInvoiceItem.getEndDate().toDateTimeAtCurrentTime());
                 }
             }
         }
     }
 
-    private void notifyBusOfInvoiceAdjustment(final Transmogrifier transactional, final UUID invoiceId, final UUID accountId,
+    private void notifyBusOfInvoiceAdjustment(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final UUID invoiceId, final UUID accountId,
                                               final UUID userToken, final InternalCallContext context) {
         try {
             eventBus.postFromTransaction(new DefaultInvoiceAdjustmentEvent(invoiceId, accountId, userToken, context.getAccountRecordId(), context.getTenantRecordId()),
-                    transactional, context);
+                                         entitySqlDaoWrapperFactory, context);
         } catch (EventBusException e) {
             log.warn("Failed to post adjustment event for invoice " + invoiceId, e);
         }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDatePoster.java b/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDatePoster.java
index 54448e3..15519a6 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDatePoster.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDatePoster.java
@@ -29,6 +29,8 @@ import com.ning.billing.util.callcontext.CallOrigin;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.notificationq.NotificationQueue;
 import com.ning.billing.util.notificationq.NotificationQueueService;
 import com.ning.billing.util.notificationq.NotificationQueueService.NoSuchNotificationQueue;
@@ -50,7 +52,7 @@ public class DefaultNextBillingDatePoster implements NextBillingDatePoster {
     }
 
     @Override
-    public void insertNextBillingNotification(final Transmogrifier transactionalDao, final UUID accountId,
+    public void insertNextBillingNotification(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final UUID accountId,
                                               final UUID subscriptionId, final DateTime futureNotificationTime) {
         final InternalCallContext context = createCallContext(accountId);
 
@@ -60,7 +62,7 @@ public class DefaultNextBillingDatePoster implements NextBillingDatePoster {
                                                                              DefaultNextBillingDateNotifier.NEXT_BILLING_DATE_NOTIFIER_QUEUE);
             log.info("Queuing next billing date notification. id: {}, timestamp: {}", subscriptionId.toString(), futureNotificationTime.toString());
 
-            nextBillingQueue.recordFutureNotificationFromTransaction(transactionalDao, futureNotificationTime, accountId,
+            nextBillingQueue.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory, futureNotificationTime, accountId,
                                                                      new NextBillingDateNotificationKey(subscriptionId), context);
         } catch (NoSuchNotificationQueue e) {
             log.error("Attempting to put items on a non-existent queue (NextBillingDateNotifier).", e);
diff --git a/invoice/src/main/java/com/ning/billing/invoice/notification/EmailInvoiceNotifier.java b/invoice/src/main/java/com/ning/billing/invoice/notification/EmailInvoiceNotifier.java
index befb7fd..10fed3b 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/notification/EmailInvoiceNotifier.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/notification/EmailInvoiceNotifier.java
@@ -77,8 +77,8 @@ public class EmailInvoiceNotifier implements InvoiceNotifier {
 
         // Check if this account has the MANUAL_PAY system tag
         boolean manualPay = false;
-        final Map<String, Tag> accountTags = tagUserApi.getTags(account.getId(), ObjectType.ACCOUNT, internalTenantContext);
-        for (final Tag tag : accountTags.values()) {
+        final List<Tag> accountTags = tagUserApi.getTags(account.getId(), ObjectType.ACCOUNT, internalTenantContext);
+        for (final Tag tag : accountTags) {
             if (ControlTagType.MANUAL_PAY.getId().equals(tag.getTagDefinitionId())) {
                 manualPay = true;
                 break;
diff --git a/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDatePoster.java b/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDatePoster.java
index 60f8ec4..41bbe43 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDatePoster.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDatePoster.java
@@ -21,9 +21,12 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
+
 public interface NextBillingDatePoster {
 
-    void insertNextBillingNotification(Transmogrifier transactionalDao, UUID accountId,
+    void insertNextBillingNotification(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, UUID accountId,
                                        UUID subscriptionId, DateTime futureNotificationTime);
 
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/user/TestDefaultInvoiceUserApi.java b/invoice/src/test/java/com/ning/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
index 97606ba..d349f62 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
@@ -17,6 +17,7 @@
 package com.ning.billing.invoice.api.user;
 
 import java.math.BigDecimal;
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
@@ -328,9 +329,9 @@ public class TestDefaultInvoiceUserApi extends InvoiceApiTestBase {
     public void testAddRemoveWrittenOffTag() throws InvoiceApiException, TagApiException {
         invoiceUserApi.tagInvoiceAsWrittenOff(invoiceId, callContext);
 
-        Map<String, Tag> tags = tagUserApi.getTags(invoiceId, ObjectType.INVOICE, tenantContext);
+        List<Tag> tags = tagUserApi.getTags(invoiceId, ObjectType.INVOICE, tenantContext);
         assertEquals(tags.size(), 1);
-        assertEquals(tags.values().iterator().next().getTagDefinitionId(), ControlTagType.WRITTEN_OFF.getId());
+        assertEquals(tags.get(0).getTagDefinitionId(), ControlTagType.WRITTEN_OFF.getId());
 
         invoiceUserApi.tagInvoiceAsNotWrittenOff(invoiceId, callContext);
         tags = tagUserApi.getTags(invoiceId, ObjectType.INVOICE, tenantContext);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/notification/MockNextBillingDatePoster.java b/invoice/src/test/java/com/ning/billing/invoice/notification/MockNextBillingDatePoster.java
index 5742247..67af45e 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/notification/MockNextBillingDatePoster.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/notification/MockNextBillingDatePoster.java
@@ -21,9 +21,12 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
+
 public class MockNextBillingDatePoster implements NextBillingDatePoster {
+
     @Override
-    public void insertNextBillingNotification(final Transmogrifier transactionalDao, final UUID accountId, final UUID subscriptionId, final DateTime futureNotificationTime) {
-        // do nothing
+    public void insertNextBillingNotification(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final UUID accountId, final UUID subscriptionId, final DateTime futureNotificationTime) {
     }
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java b/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java
index fe1b45d..591204d 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/notification/TestNextBillingDateNotifier.java
@@ -56,6 +56,10 @@ import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
 import com.ning.billing.util.email.templates.TemplateModule;
+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.glue.BusModule;
 import com.ning.billing.util.glue.BusModule.BusType;
 import com.ning.billing.util.glue.NotificationQueueModule;
@@ -81,6 +85,7 @@ public class TestNextBillingDateNotifier extends InvoiceTestSuiteWithEmbeddedDB 
     private InvoiceListenerMock listener;
     private NotificationQueueService notificationQueueService;
     private InternalCallContextFactory internalCallContextFactory;
+    private EntitySqlDaoTransactionalJdbiWrapper entitySqlDaoTransactionalJdbiWrapper;
 
     private static final class InvoiceListenerMock extends InvoiceListener {
         int eventCount = 0;
@@ -137,6 +142,7 @@ public class TestNextBillingDateNotifier extends InvoiceTestSuiteWithEmbeddedDB 
                     bind(IDBI.class).toInstance(dbi);
                 }
 
+
                 final InternalCallContextFactory internalCallContextFactory = new InternalCallContextFactory(helper.getDBI(), clock);
                 bind(InternalCallContextFactory.class).toInstance(internalCallContextFactory);
 
@@ -149,6 +155,9 @@ public class TestNextBillingDateNotifier extends InvoiceTestSuiteWithEmbeddedDB 
 
         clock = g.getInstance(Clock.class);
         final IDBI dbi = g.getInstance(IDBI.class);
+
+        entitySqlDaoTransactionalJdbiWrapper = new EntitySqlDaoTransactionalJdbiWrapper(dbi);
+
         dao = dbi.onDemand(DummySqlTest.class);
         eventBus = g.getInstance(InternalBus.class);
         notificationQueueService = g.getInstance(NotificationQueueService.class);
@@ -177,12 +186,10 @@ public class TestNextBillingDateNotifier extends InvoiceTestSuiteWithEmbeddedDB 
         notifier.initialize();
         notifier.start();
 
-        dao.inTransaction(new Transaction<Void, DummySqlTest>() {
+        entitySqlDaoTransactionalJdbiWrapper.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
-            public Void inTransaction(final DummySqlTest transactional,
-                                      final TransactionStatus status) throws Exception {
-
-                poster.insertNextBillingNotification(transactional, accountId, subscriptionId, readyTime);
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                poster.insertNextBillingNotification(entitySqlDaoWrapperFactory, accountId, subscriptionId, readyTime);
                 return null;
             }
         });
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
index 4777f64..a3b5ff1 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
@@ -76,6 +76,7 @@ import com.ning.billing.payment.api.PaymentApiException;
 import com.ning.billing.payment.api.PaymentMethod;
 import com.ning.billing.payment.api.Refund;
 import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.api.CustomFieldApiException;
 import com.ning.billing.util.api.CustomFieldUserApi;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
@@ -453,7 +454,7 @@ public class AccountResource extends JaxRsResourceBase {
                                        @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                        @HeaderParam(HDR_REASON) final String reason,
                                        @HeaderParam(HDR_COMMENT) final String comment,
-                                       @javax.ws.rs.core.Context final HttpServletRequest request) {
+                                       @javax.ws.rs.core.Context final HttpServletRequest request) throws CustomFieldApiException {
         return super.createCustomFields(UUID.fromString(id), customFields,
                                         context.createContext(createdBy, reason, comment, request));
     }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
index 3bca566..4c3de42 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
@@ -51,6 +51,7 @@ import com.ning.billing.jaxrs.json.SubscriptionJsonNoEvents;
 import com.ning.billing.jaxrs.util.Context;
 import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
 import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.api.CustomFieldApiException;
 import com.ning.billing.util.api.CustomFieldUserApi;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
@@ -149,7 +150,7 @@ public class BundleResource extends JaxRsResourceBase {
                                        @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                        @HeaderParam(HDR_REASON) final String reason,
                                        @HeaderParam(HDR_COMMENT) final String comment,
-                                       @javax.ws.rs.core.Context final HttpServletRequest request) {
+                                       @javax.ws.rs.core.Context final HttpServletRequest request) throws CustomFieldApiException{
         return super.createCustomFields(UUID.fromString(id), customFields,
                                         context.createContext(createdBy, reason, comment, request));
     }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
index 5e84036..952bf60 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
@@ -65,6 +65,7 @@ import com.ning.billing.payment.api.Payment;
 import com.ning.billing.payment.api.PaymentApi;
 import com.ning.billing.payment.api.PaymentApiException;
 import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.api.CustomFieldApiException;
 import com.ning.billing.util.api.CustomFieldUserApi;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
@@ -439,7 +440,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                                        @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                        @HeaderParam(HDR_REASON) final String reason,
                                        @HeaderParam(HDR_COMMENT) final String comment,
-                                       @javax.ws.rs.core.Context final HttpServletRequest request) {
+                                       @javax.ws.rs.core.Context final HttpServletRequest request) throws CustomFieldApiException {
         return super.createCustomFields(UUID.fromString(id), customFields,
                                         context.createContext(createdBy, reason, comment, request));
     }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java
index 4114e9e..88a579a 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java
@@ -20,7 +20,6 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -32,6 +31,7 @@ import org.joda.time.format.ISODateTimeFormat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.ning.billing.BillingExceptionBase;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.ObjectType;
 import com.ning.billing.jaxrs.json.CustomFieldJson;
@@ -39,6 +39,7 @@ import com.ning.billing.jaxrs.json.TagJson;
 import com.ning.billing.jaxrs.util.Context;
 import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
 import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.api.CustomFieldApiException;
 import com.ning.billing.util.api.CustomFieldUserApi;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
@@ -83,10 +84,10 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
     }
 
     protected Response getTags(final UUID id, final boolean withAudit, final TenantContext context) throws TagDefinitionApiException {
-        final Map<String, Tag> tags = tagUserApi.getTags(id, getObjectType(), context);
+        final List<Tag> tags = tagUserApi.getTags(id, getObjectType(), context);
         final Collection<UUID> tagIdList = (tags.size() == 0) ?
                                            Collections.<UUID>emptyList() :
-                                           Collections2.transform(tags.values(), new Function<Tag, UUID>() {
+                                           Collections2.transform(tags, new Function<Tag, UUID>() {
                                                @Override
                                                public UUID apply(final Tag input) {
                                                    return input.getTagDefinitionId();
@@ -153,10 +154,10 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
     }
 
     protected Response getCustomFields(final UUID id, final TenantContext context) {
-        final Map<String, CustomField> fields = customFieldUserApi.getCustomFields(id, getObjectType(), context);
+        final List<CustomField> fields = customFieldUserApi.getCustomFields(id, getObjectType(), context);
 
         final List<CustomFieldJson> result = new LinkedList<CustomFieldJson>();
-        for (final CustomField cur : fields.values()) {
+        for (final CustomField cur : fields) {
             result.add(new CustomFieldJson(cur));
         }
 
@@ -165,13 +166,13 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
 
     protected Response createCustomFields(final UUID id,
                                           final List<CustomFieldJson> customFields,
-                                          final CallContext context) {
+                                          final CallContext context) throws CustomFieldApiException {
         final LinkedList<CustomField> input = new LinkedList<CustomField>();
         for (final CustomFieldJson cur : customFields) {
-            input.add(new StringCustomField(cur.getName(), cur.getValue()));
+            input.add(new StringCustomField(cur.getName(), cur.getValue(), getObjectType(), id, context.getCreatedDate()));
         }
 
-        customFieldUserApi.saveCustomFields(id, getObjectType(), input, context);
+        customFieldUserApi.addCustomFields(input, context);
         return uriBuilder.buildResponse(this.getClass(), "createCustomFields", id);
     }
 
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
index 2b22ea1..b8f1c4e 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
@@ -57,6 +57,7 @@ import com.ning.billing.payment.api.PaymentApi;
 import com.ning.billing.payment.api.PaymentApiException;
 import com.ning.billing.payment.api.Refund;
 import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.api.CustomFieldApiException;
 import com.ning.billing.util.api.CustomFieldUserApi;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
@@ -206,7 +207,7 @@ public class PaymentResource extends JaxRsResourceBase {
                                        @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                        @HeaderParam(HDR_REASON) final String reason,
                                        @HeaderParam(HDR_COMMENT) final String comment,
-                                       @javax.ws.rs.core.Context final HttpServletRequest request) {
+                                       @javax.ws.rs.core.Context final HttpServletRequest request) throws CustomFieldApiException {
         return super.createCustomFields(UUID.fromString(id), customFields,
                                         context.createContext(createdBy, reason, comment, request));
     }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
index 590ec31..97b3c28 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
@@ -55,6 +55,7 @@ import com.ning.billing.jaxrs.util.Context;
 import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
 import com.ning.billing.jaxrs.util.KillbillEventHandler;
 import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.api.CustomFieldApiException;
 import com.ning.billing.util.api.CustomFieldUserApi;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
@@ -366,7 +367,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
                                        @HeaderParam(HDR_REASON) final String reason,
                                        @HeaderParam(HDR_COMMENT) final String comment,
                                        @javax.ws.rs.core.Context final UriInfo uriInfo,
-                                       @javax.ws.rs.core.Context final HttpServletRequest request) {
+                                       @javax.ws.rs.core.Context final HttpServletRequest request) throws CustomFieldApiException {
         return super.createCustomFields(UUID.fromString(id), customFields,
                                         context.createContext(createdBy, reason, comment, request));
     }
diff --git a/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAuditLogJson.java b/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAuditLogJson.java
index b9829e8..908daaf 100644
--- a/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAuditLogJson.java
+++ b/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAuditLogJson.java
@@ -89,7 +89,7 @@ public class TestAuditLogJson extends JaxrsTestSuite {
         Assert.assertNotNull(auditLogJson.getChangeDate());
         Assert.assertEquals(auditLogJson.getChangedBy(), callContext.getUserName());
         Assert.assertEquals(auditLogJson.getReasonCode(), callContext.getReasonCode());
-        Assert.assertEquals(auditLogJson.getComments(), callContext.getComment());
+        Assert.assertEquals(auditLogJson.getComments(), callContext.getComments());
         Assert.assertEquals(auditLogJson.getUserToken(), callContext.getUserToken().toString());
     }
 }
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultInternalBillingApi.java b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
index 591ff9e..2804a04 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
@@ -82,8 +82,8 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
             final Account account = accountApi.getAccountById(accountId, context);
 
             // Check to see if billing is off for the account
-            final Map<String, Tag> accountTags = tagApi.getTags(accountId, ObjectType.ACCOUNT, context);
-            for (final Tag cur : accountTags.values()) {
+            final List<Tag> accountTags = tagApi.getTags(accountId, ObjectType.ACCOUNT, context);
+            for (final Tag cur : accountTags) {
                 if (ControlTagType.AUTO_INVOICING_OFF.getId().equals(cur.getTagDefinitionId())) {
                     result.setAccountAutoInvoiceIsOff(true);
                     return result; // billing is off, we are done
@@ -116,10 +116,10 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
             final List<Subscription> subscriptions = entitlementApi.getSubscriptionsForBundle(bundle.getId(), context);
 
             //Check if billing is off for the bundle
-            final Map<String, Tag> bundleTags = tagApi.getTags(bundle.getId(), ObjectType.BUNDLE, context);
+            final List<Tag> bundleTags = tagApi.getTags(bundle.getId(), ObjectType.BUNDLE, context);
 
             boolean found_AUTO_INVOICING_OFF = false;
-            for (final Tag cur : bundleTags.values()) {
+            for (final Tag cur : bundleTags) {
                 if (ControlTagType.AUTO_INVOICING_OFF.getId().equals(cur.getTagDefinitionId())) {
                     found_AUTO_INVOICING_OFF = true;
                     break;
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java
index 3b628c6..2ab8a90 100644
--- a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java
@@ -276,10 +276,11 @@ public class TestBillingApi extends JunctionTestSuite {
 
         final Account account = createAccount(32);
 
-        final Map<String, Tag> tags = new HashMap<String, Tag>();
         final Tag aioTag = mock(Tag.class);
         when(aioTag.getTagDefinitionId()).thenReturn(ControlTagType.AUTO_INVOICING_OFF.getId());
-        tags.put(ControlTagType.AUTO_INVOICING_OFF.name(), aioTag);
+        final List<Tag> tags = new ArrayList<Tag>();
+        tags.add(aioTag);
+
         when(tagApi.getTags(account.getId(), ObjectType.ACCOUNT, internalCallContext)).thenReturn(tags);
         assertEquals(tagApi.getTags(account.getId(), ObjectType.ACCOUNT, internalCallContext), tags);
 
@@ -297,10 +298,10 @@ public class TestBillingApi extends JunctionTestSuite {
 
         final Account account = createAccount(32);
 
-        final Map<String, Tag> tags = new HashMap<String, Tag>();
+        final List<Tag> tags = new ArrayList<Tag>();
         final Tag aioTag = mock(Tag.class);
         when(aioTag.getTagDefinitionId()).thenReturn(ControlTagType.AUTO_INVOICING_OFF.getId());
-        tags.put(ControlTagType.AUTO_INVOICING_OFF.name(), aioTag);
+        tags.add(aioTag);
         when(tagApi.getTags(bunId, ObjectType.BUNDLE, internalCallContext)).thenReturn(tags);
 
         final BillingEventSet events = api.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), internalCallContext);
diff --git a/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java b/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java
index 8c73ea2..1282a03 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.payment.core;
 
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.Callable;
@@ -79,8 +80,8 @@ public abstract class ProcessorBase {
     }
 
     protected boolean isAccountAutoPayOff(final UUID accountId, final InternalTenantContext context) {
-        final Map<String, Tag> accountTags = tagInternalApi.getTags(accountId, ObjectType.ACCOUNT, context);
-        for (final Tag cur : accountTags.values()) {
+        final List<Tag> accountTags = tagInternalApi.getTags(accountId, ObjectType.ACCOUNT, context);
+        for (final Tag cur : accountTags) {
             if (ControlTagType.AUTO_PAY_OFF.getId().equals(cur.getTagDefinitionId())) {
                 return true;
             }
diff --git a/payment/src/main/java/com/ning/billing/payment/retry/BaseRetryService.java b/payment/src/main/java/com/ning/billing/payment/retry/BaseRetryService.java
index 6ec7286..e6b9a86 100644
--- a/payment/src/main/java/com/ning/billing/payment/retry/BaseRetryService.java
+++ b/payment/src/main/java/com/ning/billing/payment/retry/BaseRetryService.java
@@ -31,6 +31,8 @@ import com.ning.billing.util.callcontext.CallOrigin;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.notificationq.NotificationKey;
 import com.ning.billing.util.notificationq.NotificationQueue;
 import com.ning.billing.util.notificationq.NotificationQueueService;
@@ -106,8 +108,8 @@ public abstract class BaseRetryService implements RetryService {
             this.internalCallContextFactory = internalCallContextFactory;
         }
 
-        public boolean scheduleRetryFromTransaction(final UUID paymentId, final DateTime timeOfRetry, final Transmogrifier transactionalDao) {
-            return scheduleRetryInternal(paymentId, timeOfRetry, transactionalDao);
+        public boolean scheduleRetryFromTransaction(final UUID paymentId, final DateTime timeOfRetry, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) {
+            return scheduleRetryInternal(paymentId, timeOfRetry, entitySqlDaoWrapperFactory);
         }
 
         public boolean scheduleRetry(final UUID paymentId, final DateTime timeOfRetry) {
@@ -132,7 +134,7 @@ public abstract class BaseRetryService implements RetryService {
              */
         }
 
-        private boolean scheduleRetryInternal(final UUID paymentId, final DateTime timeOfRetry, final Transmogrifier transactionalDao) {
+        private boolean scheduleRetryInternal(final UUID paymentId, final DateTime timeOfRetry, final EntitySqlDaoWrapperFactory<EntitySqlDao> transactionalDao) {
             final InternalCallContext context = createCallContextFromPaymentId(paymentId);
 
             try {
diff --git a/payment/src/main/java/com/ning/billing/payment/retry/PluginFailureRetryService.java b/payment/src/main/java/com/ning/billing/payment/retry/PluginFailureRetryService.java
index a4610f2..150cf9c 100644
--- a/payment/src/main/java/com/ning/billing/payment/retry/PluginFailureRetryService.java
+++ b/payment/src/main/java/com/ning/billing/payment/retry/PluginFailureRetryService.java
@@ -28,6 +28,8 @@ import com.ning.billing.payment.core.PaymentProcessor;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.notificationq.NotificationQueueService;
 
 import com.google.inject.Inject;
@@ -82,7 +84,7 @@ public class PluginFailureRetryService extends BaseRetryService implements Retry
             return super.scheduleRetry(paymentId, nextRetryDate);
         }
 
-        public boolean scheduleRetryFromTransaction(final UUID paymentId, final int retryAttempt, final Transmogrifier transactionalDao) {
+        public boolean scheduleRetryFromTransaction(final UUID paymentId, final int retryAttempt, final EntitySqlDaoWrapperFactory<EntitySqlDao> transactionalDao) {
             final DateTime nextRetryDate = getNextRetryDate(retryAttempt);
             if (nextRetryDate == null) {
                 return false;
diff --git a/payment/src/test/java/com/ning/billing/payment/glue/PaymentTestModuleWithMocks.java b/payment/src/test/java/com/ning/billing/payment/glue/PaymentTestModuleWithMocks.java
index 8eb910c..a01dda5 100644
--- a/payment/src/test/java/com/ning/billing/payment/glue/PaymentTestModuleWithMocks.java
+++ b/payment/src/test/java/com/ning/billing/payment/glue/PaymentTestModuleWithMocks.java
@@ -45,6 +45,7 @@ import com.ning.billing.util.svcapi.account.AccountInternalApi;
 import com.ning.billing.util.svcapi.tag.TagInternalApi;
 import com.ning.billing.util.tag.Tag;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
 public class PaymentTestModuleWithMocks extends PaymentModule {
@@ -93,7 +94,7 @@ public class PaymentTestModuleWithMocks extends PaymentModule {
 
         final TagInternalApi tagUserApi = Mockito.mock(TagInternalApi.class);
         bind(TagInternalApi.class).toInstance(tagUserApi);
-        Mockito.when(tagUserApi.getTags(Mockito.<UUID>any(), Mockito.<ObjectType>any(), Mockito.<InternalTenantContext>any())).thenReturn(ImmutableMap.<String, Tag>of());
+        Mockito.when(tagUserApi.getTags(Mockito.<UUID>any(), Mockito.<ObjectType>any(), Mockito.<InternalTenantContext>any())).thenReturn(ImmutableList.<Tag>of());
 
         bind(GlobalLocker.class).to(MockGlobalLocker.class).asEagerSingleton();
     }
diff --git a/tenant/src/main/java/com/ning/billing/tenant/api/user/DefaultTenantUserApi.java b/tenant/src/main/java/com/ning/billing/tenant/api/user/DefaultTenantUserApi.java
index 5c00812..fd226b0 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/api/user/DefaultTenantUserApi.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/api/user/DefaultTenantUserApi.java
@@ -53,7 +53,7 @@ public class DefaultTenantUserApi implements TenantUserApi {
 
         try {
             tenantDao.create(tenant, internalCallContextFactory.createInternalCallContext(context));
-        } catch (final EntityPersistenceException e) {
+        } catch (final TenantApiException e) {
             throw new TenantApiException(e, ErrorCode.TENANT_CREATION_FAILED);
         }
 
diff --git a/tenant/src/main/java/com/ning/billing/tenant/dao/DefaultTenantDao.java b/tenant/src/main/java/com/ning/billing/tenant/dao/DefaultTenantDao.java
index f17d01b..b6892af 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/dao/DefaultTenantDao.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/dao/DefaultTenantDao.java
@@ -31,6 +31,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.ning.billing.tenant.api.Tenant;
+import com.ning.billing.tenant.api.TenantApiException;
 import com.ning.billing.tenant.api.TenantKV;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
@@ -66,7 +67,7 @@ public class DefaultTenantDao implements TenantDao {
     }
 
     @Override
-    public void create(final Tenant entity, final InternalCallContext context) throws EntityPersistenceException {
+    public void create(final Tenant entity, final InternalCallContext context) throws TenantApiException {
         // Create the salt and password
         final ByteSource salt = rng.nextBytes();
         // Hash the plain-text password with the random salt and multiple
diff --git a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantDao.java b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantDao.java
index 0d0add0..6b38750 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantDao.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantDao.java
@@ -19,11 +19,12 @@ package com.ning.billing.tenant.dao;
 import java.util.List;
 
 import com.ning.billing.tenant.api.Tenant;
+import com.ning.billing.tenant.api.TenantApiException;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.entity.dao.EntityDao;
 
-public interface TenantDao extends EntityDao<Tenant> {
+public interface TenantDao extends EntityDao<Tenant, TenantApiException> {
 
     public Tenant getTenantByApiKey(final String key);
 
diff --git a/usage/src/main/java/com/ning/billing/usage/timeline/samples/HalfFloat.java b/usage/src/main/java/com/ning/billing/usage/timeline/samples/HalfFloat.java
index 90f9e77..f058414 100644
--- a/usage/src/main/java/com/ning/billing/usage/timeline/samples/HalfFloat.java
+++ b/usage/src/main/java/com/ning/billing/usage/timeline/samples/HalfFloat.java
@@ -29,7 +29,7 @@ public class HalfFloat {
     }
 
     // These two static methods were pinched from http://stackoverflow.com/questions/6162651/half-precision-floating-point-in-java/6162687#6162687
-    // The last comment on that page is the author saying "I hereby commit these to the public domain"
+    // The last comments on that page is the author saying "I hereby commit these to the public domain"
 
     // Ignores the higher 16 bits
     public static float toFloat(final int hbits) {
diff --git a/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLog.java b/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLog.java
index 233cd32..e9e9bad 100644
--- a/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLog.java
+++ b/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLog.java
@@ -57,7 +57,7 @@ public class DefaultAuditLog extends EntityAudit implements AuditLog {
 
     @Override
     public String getComment() {
-        return callContext.getComment();
+        return callContext.getComments();
     }
 
     @Override
@@ -69,4 +69,32 @@ public class DefaultAuditLog extends EntityAudit implements AuditLog {
         sb.append('}');
         return sb.toString();
     }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        if (!super.equals(o)) {
+            return false;
+        }
+
+        final DefaultAuditLog that = (DefaultAuditLog) o;
+
+        if (callContext != null ? !callContext.equals(that.callContext) : that.callContext != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (callContext != null ? callContext.hashCode() : 0);
+        return result;
+    }
 }
diff --git a/util/src/main/java/com/ning/billing/util/bus/InMemoryInternalBus.java b/util/src/main/java/com/ning/billing/util/bus/InMemoryInternalBus.java
index 150c6d6..5770322 100644
--- a/util/src/main/java/com/ning/billing/util/bus/InMemoryInternalBus.java
+++ b/util/src/main/java/com/ning/billing/util/bus/InMemoryInternalBus.java
@@ -26,6 +26,8 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.events.BusInternalEvent;
 import com.ning.billing.util.svcsapi.bus.InternalBus;
 
@@ -97,7 +99,7 @@ public class InMemoryInternalBus implements InternalBus {
     }
 
     @Override
-    public void postFromTransaction(final BusInternalEvent event, final Transmogrifier dao, final InternalCallContext context) throws EventBusException {
+    public void postFromTransaction(final BusInternalEvent event, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalCallContext context) throws EventBusException {
         checkInitialized("postFromTransaction");
         delegate.post(event);
     }
diff --git a/util/src/main/java/com/ning/billing/util/bus/PersistentInternalBus.java b/util/src/main/java/com/ning/billing/util/bus/PersistentInternalBus.java
index c84d1b0..859fce8 100644
--- a/util/src/main/java/com/ning/billing/util/bus/PersistentInternalBus.java
+++ b/util/src/main/java/com/ning/billing/util/bus/PersistentInternalBus.java
@@ -37,6 +37,8 @@ import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.UserType;
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.events.BusInternalEvent;
 import com.ning.billing.util.queue.PersistentQueueBase;
 import com.ning.billing.util.svcsapi.bus.InternalBus;
@@ -171,9 +173,9 @@ public class PersistentInternalBus extends PersistentQueueBase implements Intern
     }
 
     @Override
-    public void postFromTransaction(final BusInternalEvent event, final Transmogrifier transmogrifier, final InternalCallContext context)
+    public void postFromTransaction(final BusInternalEvent event, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalCallContext context)
             throws EventBusException {
-        final PersistentBusSqlDao transactional = transmogrifier.become(PersistentBusSqlDao.class);
+        final PersistentBusSqlDao transactional = entitySqlDaoWrapperFactory.transmogrify(PersistentBusSqlDao.class);
         postFromTransaction(event, context, transactional);
     }
 
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/CallContextBase.java b/util/src/main/java/com/ning/billing/util/callcontext/CallContextBase.java
index c5dd11f..de3c833 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/CallContextBase.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/CallContextBase.java
@@ -28,7 +28,7 @@ public abstract class CallContextBase implements CallContext {
     protected final CallOrigin callOrigin;
     protected final UserType userType;
     protected final String reasonCode;
-    protected final String comment;
+    protected final String comments;
 
     public CallContextBase(@Nullable final UUID tenantId, final String userName, final CallOrigin callOrigin, final UserType userType) {
         this(tenantId, userName, callOrigin, userType, null);
@@ -45,7 +45,7 @@ public abstract class CallContextBase implements CallContext {
         this.callOrigin = callOrigin;
         this.userType = userType;
         this.reasonCode = reasonCode;
-        this.comment = comment;
+        this.comments = comment;
         this.userToken = userToken;
     }
 
@@ -75,8 +75,8 @@ public abstract class CallContextBase implements CallContext {
     }
 
     @Override
-    public String getComment() {
-        return comment;
+    public String getComments() {
+        return comments;
     }
 
     @Override
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContext.java b/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContext.java
index 0cb9858..e4dd038 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContext.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContext.java
@@ -75,7 +75,7 @@ public class DefaultCallContext extends CallContextBase {
         sb.append(", callOrigin=").append(callOrigin);
         sb.append(", userType=").append(userType);
         sb.append(", reasonCode='").append(reasonCode).append('\'');
-        sb.append(", comment='").append(comment).append('\'');
+        sb.append(", comments='").append(comments).append('\'');
         sb.append(", createdDate='").append(createdDate).append('\'');
         sb.append(", updatedDate='").append(createdDate).append('\'');
         sb.append('}');
@@ -96,7 +96,7 @@ public class DefaultCallContext extends CallContextBase {
         if (callOrigin != that.callOrigin) {
             return false;
         }
-        if (comment != null ? !comment.equals(that.comment) : that.comment != null) {
+        if (comments != null ? !comments.equals(that.comments) : that.comments != null) {
             return false;
         }
         if (reasonCode != null ? !reasonCode.equals(that.reasonCode) : that.reasonCode != null) {
@@ -125,7 +125,7 @@ public class DefaultCallContext extends CallContextBase {
         result = 31 * result + (callOrigin != null ? callOrigin.hashCode() : 0);
         result = 31 * result + (userType != null ? userType.hashCode() : 0);
         result = 31 * result + (reasonCode != null ? reasonCode.hashCode() : 0);
-        result = 31 * result + (comment != null ? comment.hashCode() : 0);
+        result = 31 * result + (comments != null ? comments.hashCode() : 0);
         result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
         return result;
     }
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContext.java b/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContext.java
index e79e5ca..891c295 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContext.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContext.java
@@ -33,7 +33,7 @@ public class InternalCallContext extends InternalTenantContext {
     private final CallOrigin callOrigin;
     private final UserType userType;
     private final String reasonCode;
-    private final String comment;
+    private final String comments;
     private final DateTime createdDate;
     private final DateTime updatedDate;
 
@@ -47,14 +47,14 @@ public class InternalCallContext extends InternalTenantContext {
         this.callOrigin = callOrigin;
         this.userType = userType;
         this.reasonCode = reasonCode;
-        this.comment = comment;
+        this.comments = comment;
         this.createdDate = createdDate;
         this.updatedDate = updatedDate;
     }
 
     public InternalCallContext(final Long tenantRecordId, @Nullable final Long accountRecordId, final CallContext callContext) {
         this(tenantRecordId, accountRecordId, callContext.getUserToken(), callContext.getUserName(), callContext.getCallOrigin(),
-             callContext.getUserType(), callContext.getReasonCode(), callContext.getComment(), callContext.getCreatedDate(),
+             callContext.getUserType(), callContext.getReasonCode(), callContext.getComments(), callContext.getCreatedDate(),
              callContext.getUpdatedDate());
     }
 
@@ -63,7 +63,7 @@ public class InternalCallContext extends InternalTenantContext {
     // Unfortunately not true as some APIs ae hidden in object-- e.g OverdueStateApplicator is doing subscription.cancelWithPolicy(polciy, context);
     //
     public CallContext toCallContext() {
-        return new DefaultCallContext(null, createdBy, callOrigin, userType, reasonCode, comment, userToken, createdDate, updatedDate);
+        return new DefaultCallContext(null, createdBy, callOrigin, userType, reasonCode, comments, userToken, createdDate, updatedDate);
     }
 
     public UUID getUserToken() {
@@ -90,8 +90,8 @@ public class InternalCallContext extends InternalTenantContext {
         return reasonCode;
     }
 
-    public String getComment() {
-        return comment;
+    public String getComments() {
+        return comments;
     }
 
     public DateTime getCreatedDate() {
@@ -112,7 +112,7 @@ public class InternalCallContext extends InternalTenantContext {
         sb.append(", callOrigin=").append(callOrigin);
         sb.append(", userType=").append(userType);
         sb.append(", reasonCode='").append(reasonCode).append('\'');
-        sb.append(", comment='").append(comment).append('\'');
+        sb.append(", comments='").append(comments).append('\'');
         sb.append(", createdDate=").append(createdDate);
         sb.append(", updatedDate=").append(updatedDate);
         sb.append('}');
@@ -136,7 +136,7 @@ public class InternalCallContext extends InternalTenantContext {
         if (callOrigin != that.callOrigin) {
             return false;
         }
-        if (comment != null ? !comment.equals(that.comment) : that.comment != null) {
+        if (comments != null ? !comments.equals(that.comments) : that.comments != null) {
             return false;
         }
         if (createdBy != null ? !createdBy.equals(that.createdBy) : that.createdBy != null) {
@@ -173,7 +173,7 @@ public class InternalCallContext extends InternalTenantContext {
         result = 31 * result + (callOrigin != null ? callOrigin.hashCode() : 0);
         result = 31 * result + (userType != null ? userType.hashCode() : 0);
         result = 31 * result + (reasonCode != null ? reasonCode.hashCode() : 0);
-        result = 31 * result + (comment != null ? comment.hashCode() : 0);
+        result = 31 * result + (comments != null ? comments.hashCode() : 0);
         result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
         result = 31 * result + (updatedDate != null ? updatedDate.hashCode() : 0);
         return result;
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContextFactory.java b/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContextFactory.java
index 89fb15b..8cc1954 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContextFactory.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContextFactory.java
@@ -91,7 +91,7 @@ public class InternalCallContextFactory {
         //                         "tenant of the pointed object (%s) and the context (%s) don't match!", tenantRecordIdFromObject, tenantRecordIdFromContext);
 
         return createInternalCallContext(objectId, objectType, context.getUserName(), context.getCallOrigin(),
-                                         context.getUserType(), context.getUserToken(), context.getReasonCode(), context.getComment(),
+                                         context.getUserType(), context.getUserToken(), context.getReasonCode(), context.getComments(),
                                          context.getCreatedDate(), context.getUpdatedDate());
     }
 
@@ -106,7 +106,7 @@ public class InternalCallContextFactory {
      */
     public InternalCallContext createInternalCallContext(final UUID accountId, final CallContext context) {
         return createInternalCallContext(accountId, ObjectType.ACCOUNT, context.getUserName(), context.getCallOrigin(),
-                                         context.getUserType(), context.getUserToken(), context.getReasonCode(), context.getComment(),
+                                         context.getUserType(), context.getUserToken(), context.getReasonCode(), context.getComments(),
                                          context.getCreatedDate(), context.getUpdatedDate());
     }
 
@@ -199,14 +199,14 @@ public class InternalCallContextFactory {
     // Used when we need to re-hydrate the context with the account_record_id (when creating the account)
     public InternalCallContext createInternalCallContext(final Long accountRecordId, final InternalCallContext context) {
         return new InternalCallContext(context.getTenantRecordId(), accountRecordId, context.getUserToken(), context.getCreatedBy(),
-                                       context.getCallOrigin(), context.getUserType(), context.getReasonCode(), context.getComment(),
+                                       context.getCallOrigin(), context.getUserType(), context.getReasonCode(), context.getComments(),
                                        context.getCreatedDate(), context.getUpdatedDate());
     }
 
     // Used when we need to re-hydrate the context with the tenant_record_id and account_record_id (when claiming bus events)
     public InternalCallContext createInternalCallContext(final Long tenantRecordId, final Long accountRecordId, final InternalCallContext context) {
         return new InternalCallContext(tenantRecordId, accountRecordId, context.getUserToken(), context.getCreatedBy(),
-                                       context.getCallOrigin(), context.getUserType(), context.getReasonCode(), context.getComment(),
+                                       context.getCallOrigin(), context.getUserType(), context.getReasonCode(), context.getComments(),
                                        context.getCreatedDate(), context.getUpdatedDate());
     }
 
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/InternalTenantContextBinder.java b/util/src/main/java/com/ning/billing/util/callcontext/InternalTenantContextBinder.java
index 499e907..efdddde 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/InternalTenantContextBinder.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/InternalTenantContextBinder.java
@@ -69,7 +69,7 @@ public @interface InternalTenantContextBinder {
                             q.bind("updatedDate", callContext.getUpdatedDate().toDate());
                         }
                         q.bind("reasonCode", callContext.getReasonCode());
-                        q.bind("comment", callContext.getComment());
+                        q.bind("comments", callContext.getComments());
                         q.bind("userToken", (callContext.getUserToken() != null) ? callContext.getUserToken().toString() : null);
                     }
                 }
diff --git a/util/src/main/java/com/ning/billing/util/customfield/api/DefaultCustomFieldUserApi.java b/util/src/main/java/com/ning/billing/util/customfield/api/DefaultCustomFieldUserApi.java
index dcd771a..3fa6bd1 100644
--- a/util/src/main/java/com/ning/billing/util/customfield/api/DefaultCustomFieldUserApi.java
+++ b/util/src/main/java/com/ning/billing/util/customfield/api/DefaultCustomFieldUserApi.java
@@ -17,15 +17,17 @@
 package com.ning.billing.util.customfield.api;
 
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
+import com.ning.billing.BillingExceptionBase;
 import com.ning.billing.ObjectType;
+import com.ning.billing.util.api.CustomFieldApiException;
 import com.ning.billing.util.api.CustomFieldUserApi;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.TenantContext;
 import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.customfield.StringCustomField;
 import com.ning.billing.util.customfield.dao.CustomFieldDao;
 
 import com.google.inject.Inject;
@@ -42,12 +44,15 @@ public class DefaultCustomFieldUserApi implements CustomFieldUserApi {
     }
 
     @Override
-    public Map<String, CustomField> getCustomFields(final UUID objectId, final ObjectType objectType, final TenantContext context) {
-        return customFieldDao.loadEntities(objectId, objectType, internalCallContextFactory.createInternalTenantContext(context));
+    public List<CustomField> getCustomFields(final UUID objectId, final ObjectType objectType, final TenantContext context) {
+        return customFieldDao.getCustomFields(objectId, objectType, internalCallContextFactory.createInternalTenantContext(context));
     }
 
     @Override
-    public void saveCustomFields(final UUID objectId, final ObjectType objectType, final List<CustomField> fields, final CallContext context) {
-        customFieldDao.saveEntities(objectId, objectType, fields, internalCallContextFactory.createInternalCallContext(objectId, objectType, context));
+    public void addCustomFields(final List<CustomField> fields, final CallContext context) throws CustomFieldApiException {
+        // TODO make it transactional
+        for (CustomField cur : fields) {
+            customFieldDao.create(cur, internalCallContextFactory.createInternalCallContext(context));
+        }
     }
 }
diff --git a/util/src/main/java/com/ning/billing/util/customfield/dao/AuditedCustomFieldDao.java b/util/src/main/java/com/ning/billing/util/customfield/dao/AuditedCustomFieldDao.java
index da14fa8..decb30a 100644
--- a/util/src/main/java/com/ning/billing/util/customfield/dao/AuditedCustomFieldDao.java
+++ b/util/src/main/java/com/ning/billing/util/customfield/dao/AuditedCustomFieldDao.java
@@ -16,106 +16,45 @@
 
 package com.ning.billing.util.customfield.dao;
 
+import java.util.List;
+import java.util.UUID;
+
 import org.skife.jdbi.v2.IDBI;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 
+import com.ning.billing.BillingExceptionBase;
+import com.ning.billing.ErrorCode;
+import com.ning.billing.ObjectType;
+import com.ning.billing.util.api.CustomFieldApiException;
+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.customfield.CustomField;
-import com.ning.billing.util.dao.AuditedCollectionDaoBase;
-import com.ning.billing.util.dao.TableName;
-import com.ning.billing.util.entity.collection.dao.UpdatableEntityCollectionSqlDao;
+import com.ning.billing.util.entity.dao.EntityDaoBase;
+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 AuditedCustomFieldDao extends AuditedCollectionDaoBase<CustomField, String> implements CustomFieldDao {
-
-    private final CustomFieldSqlDao dao;
+public class AuditedCustomFieldDao extends EntityDaoBase<CustomField, CustomFieldApiException> implements CustomFieldDao {
 
     @Inject
-    public AuditedCustomFieldDao(final IDBI dbi, final Clock clock) {
-        super(clock);
-        dao = dbi.onDemand(CustomFieldSqlDao.class);
-    }
-
-    @Override
-    protected TableName getTableName(final InternalTenantContext context) {
-        return TableName.CUSTOM_FIELD_HISTORY;
-    }
-
-    @Override
-    protected String getEquivalenceObjectFor(final CustomField obj) {
-        return obj.getName();
-    }
-
-    @Override
-    protected UpdatableEntityCollectionSqlDao<CustomField> transmogrifyDao(final Transmogrifier transactionalDao, InternalTenantContext context) {
-        return transactionalDao.become(CustomFieldSqlDao.class);
+    public AuditedCustomFieldDao(final IDBI dbi) {
+        super(new EntitySqlDaoTransactionalJdbiWrapper(dbi));
     }
 
     @Override
-    protected UpdatableEntityCollectionSqlDao<CustomField> getSqlDao(final InternalTenantContext context) {
-        return dao;
+    public List<CustomField> getCustomFields(final UUID objectId, final ObjectType objectType, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<CustomField>>() {
+            @Override
+            public List<CustomField> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(CustomFieldSqlDao.class).getCustomFieldsForObject(objectId, objectType, context);
+            }
+        });
     }
 
     @Override
-    protected String getKey(final CustomField entity, final InternalTenantContext context) {
-        return entity.getName();
+    protected CustomFieldApiException generateAlreadyExistsException(final CustomField entity, final InternalCallContext context) {
+        return new CustomFieldApiException(ErrorCode.CUSTOM_FIELD_ALREADY_EXISTS, entity.getId());
     }
-
-    //    @Override
-    //    public void saveEntitiesFromTransaction(Transmogrifier dao, UUID objectId, ObjectType objectType, List<CustomField> fields, CallContext context) {
-    //        CustomFieldSqlDao customFieldSqlDao = dao.become(CustomFieldSqlDao.class);
-    //
-    //        // get list of existing fields
-    //        List<CustomField> existingFields = customFieldSqlDao.load(objectId.toString(), objectType);
-    //        List<CustomField> fieldsToUpdate = new ArrayList<CustomField>();
-    //
-    //        // sort into fields to update (fieldsToUpdate), fields to add (fields), and fields to delete (existingFields)
-    //        Iterator<CustomField> fieldIterator = fields.iterator();
-    //        while (fieldIterator.hasNext()) {
-    //            CustomField field = fieldIterator.next();
-    //
-    //            Iterator<CustomField> existingFieldIterator = existingFields.iterator();
-    //            while (existingFieldIterator.hasNext()) {
-    //                CustomField existingField = existingFieldIterator.next();
-    //                if (field.getName().equals(existingField.getName())) {
-    //                    // if the tagStore match, remove from both lists
-    //                    fieldsToUpdate.add(field);
-    //                    fieldIterator.remove();
-    //                    existingFieldIterator.remove();
-    //                }
-    //            }
-    //        }
-    //
-    //        customFieldSqlDao.batchInsertFromTransaction(objectId.toString(), objectType, fields, context);
-    //        customFieldSqlDao.batchUpdateFromTransaction(objectId.toString(), objectType, fieldsToUpdate, context);
-    //
-    //        // get all custom fields (including those that are about to be deleted) from the database in order to get the record ids
-    //        List<Mapper> recordIds = customFieldSqlDao.getRecordIds(objectId.toString(), objectType);
-    //        Map<UUID, Long> recordIdMap = new HashMap<UUID, Long>();
-    //        for (Mapper recordId : recordIds) {
-    //            recordIdMap.put(recordId.getId(), recordId.getTargetRecordId());
-    //        }
-    //
-    //        customFieldSqlDao.batchDeleteFromTransaction(objectId.toString(), objectType, existingFields, context);
-    //
-    //        List<MappedEntity<CustomField>> fieldHistories = new ArrayList<MappedEntity<CustomField>>();
-    //        fieldHistories.addAll(convertToHistory(fields, recordIdMap, ChangeType.INSERT));
-    //        fieldHistories.addAll(convertToHistory(fieldsToUpdate, recordIdMap, ChangeType.UPDATE));
-    //        fieldHistories.addAll(convertToHistory(existingFields, recordIdMap, ChangeType.DELETE));
-    //
-    //        customFieldSqlDao.batchAddHistoryFromTransaction(objectId.toString(), objectType, fieldHistories, context);
-    //        customFieldSqlDao.batchInsertAuditLogFromTransaction(TableName.CUSTOM_FIELD_HISTORY, objectId.toString(), objectType, fieldHistories, context);
-    //    }
-    //
-    //    private List<MappedEntity<CustomField>> convertToHistory(List<CustomField> fields, Map<UUID, Long> recordIds, ChangeType changeType) {
-    //        List<MappedEntity<CustomField>> result = new ArrayList<MappedEntity<CustomField>>();
-    //
-    //        for (CustomField field : fields) {
-    //            result.add(new MappedEntity<CustomField>(recordIds.get(field.getId()), field, changeType));
-    //        }
-    //
-    //        return result;
-    //    }
 }
diff --git a/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldDao.java b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldDao.java
index 66cd734..89da9cd 100644
--- a/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldDao.java
+++ b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldDao.java
@@ -16,8 +16,18 @@
 
 package com.ning.billing.util.customfield.dao;
 
+import java.util.List;
+import java.util.UUID;
+
+import com.ning.billing.BillingExceptionBase;
+import com.ning.billing.ObjectType;
+import com.ning.billing.util.api.CustomFieldApiException;
+import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.customfield.CustomField;
-import com.ning.billing.util.dao.AuditedCollectionDao;
+import com.ning.billing.util.entity.dao.EntityDao;
+
+public interface CustomFieldDao extends EntityDao<CustomField, CustomFieldApiException> {
+
 
-public interface CustomFieldDao extends AuditedCollectionDao<CustomField> {
+    public List<CustomField> getCustomFields(final UUID objectId, final ObjectType objectType, final InternalTenantContext context);
 }
diff --git a/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldMapper.java b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldMapper.java
index 774367d..903ac71 100644
--- a/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldMapper.java
+++ b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldMapper.java
@@ -20,9 +20,11 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.UUID;
 
+import org.joda.time.DateTime;
 import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
+import com.ning.billing.ObjectType;
 import com.ning.billing.util.customfield.CustomField;
 import com.ning.billing.util.customfield.StringCustomField;
 import com.ning.billing.util.dao.MapperBase;
@@ -33,6 +35,9 @@ public class CustomFieldMapper extends MapperBase implements ResultSetMapper<Cus
         final UUID id = UUID.fromString(result.getString("id"));
         final String fieldName = result.getString("field_name");
         final String fieldValue = result.getString("field_value");
-        return new StringCustomField(id, fieldName, fieldValue);
+        final ObjectType objectType = ObjectType.valueOf(result.getString("object_type"));
+        final UUID objectId = getUUID(result, "object_id");
+        final DateTime createdDate = getDateTime(result, "created_date");
+        return new StringCustomField(id, fieldName, fieldValue, objectType, objectId, createdDate);
     }
 }
diff --git a/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldSqlDao.java b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldSqlDao.java
index de5e990..bd367d6 100644
--- a/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldSqlDao.java
@@ -18,8 +18,10 @@ package com.ning.billing.util.customfield.dao;
 
 import java.util.Collection;
 import java.util.List;
+import java.util.UUID;
 
 import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.SqlBatch;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
 import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
@@ -27,43 +29,18 @@ import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 
 import com.ning.billing.ObjectType;
 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.customfield.CustomField;
 import com.ning.billing.util.dao.EntityHistory;
 import com.ning.billing.util.dao.ObjectTypeBinder;
 import com.ning.billing.util.entity.collection.dao.UpdatableEntityCollectionSqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
 import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 
 @EntitySqlDaoStringTemplate
 @RegisterMapper(CustomFieldMapper.class)
-public interface CustomFieldSqlDao extends UpdatableEntityCollectionSqlDao<CustomField>,
-                                           Transactional<CustomFieldSqlDao>, Transmogrifier {
+public interface CustomFieldSqlDao extends EntitySqlDao<CustomField> {
 
-    @Override
-    @SqlBatch
-    public void insertFromTransaction(@Bind("objectId") final String objectId,
-                                      @ObjectTypeBinder final ObjectType objectType,
-                                      @CustomFieldBinder final Collection<CustomField> entities,
-                                      @InternalTenantContextBinder final InternalCallContext context);
-
-    @Override
-    @SqlBatch
-    public void updateFromTransaction(@Bind("objectId") final String objectId,
-                                      @ObjectTypeBinder final ObjectType objectType,
-                                      @CustomFieldBinder final Collection<CustomField> entities,
-                                      @InternalTenantContextBinder final InternalCallContext context);
-
-    @Override
-    @SqlBatch
-    public void deleteFromTransaction(@Bind("objectId") final String objectId,
-                                      @ObjectTypeBinder final ObjectType objectType,
-                                      @CustomFieldBinder final Collection<CustomField> entities,
-                                      @InternalTenantContextBinder final InternalCallContext context);
-
-    @Override
-    @SqlBatch
-    public void addHistoryFromTransaction(@Bind("objectId") final String objectId,
-                                          @ObjectTypeBinder final ObjectType objectType,
-                                          @CustomFieldHistoryBinder final List<EntityHistory<CustomField>> entities,
-                                          @InternalTenantContextBinder final InternalCallContext context);
+    List<CustomField> getCustomFieldsForObject(@Bind("objectId") UUID objectId, @Bind("objectType") ObjectType objectType, @BindBean InternalTenantContext internalTenantContext);
 }
diff --git a/util/src/main/java/com/ning/billing/util/customfield/StringCustomField.java b/util/src/main/java/com/ning/billing/util/customfield/StringCustomField.java
index 4a3bf51..179c706 100644
--- a/util/src/main/java/com/ning/billing/util/customfield/StringCustomField.java
+++ b/util/src/main/java/com/ning/billing/util/customfield/StringCustomField.java
@@ -18,23 +18,31 @@ package com.ning.billing.util.customfield;
 
 import java.util.UUID;
 
+import org.joda.time.DateTime;
+
+import com.ning.billing.ObjectType;
 import com.ning.billing.util.entity.Entity;
 import com.ning.billing.util.entity.EntityBase;
 
 public class StringCustomField extends EntityBase implements CustomField {
+
+
     private final String name;
     private String value;
+    private final UUID objectId;
+    private final ObjectType objectType;
 
-    public StringCustomField(final String name, final String value) {
-        super();
-        this.name = name;
-        this.value = value;
+    public StringCustomField(final String name, final String value, final ObjectType objectType, final UUID objectId, final DateTime createdDate) {
+        this(UUID.randomUUID(), name, value, objectType, objectId, createdDate);
     }
 
-    public StringCustomField(final UUID id, final String name, final String value) {
-        super(id);
+    public StringCustomField(final UUID id, final String name, final String value, final ObjectType objectType, final UUID objectId, final DateTime createdDate) {
+        super(id, createdDate, createdDate);
         this.name = name;
         this.value = value;
+        this.objectId = objectId;
+        this.objectType = objectType;
+
     }
 
     @Override
@@ -47,6 +55,14 @@ public class StringCustomField extends EntityBase implements CustomField {
         return value;
     }
 
+    public ObjectType getObjectType() {
+        return objectType;
+    }
+
+    public UUID getObjectId() {
+        return objectId;
+    }
+
     @Override
     public void setValue(final String value) {
         this.value = value;
diff --git a/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDao.java b/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDao.java
index d03f29d..7be6b09 100644
--- a/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDao.java
+++ b/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDao.java
@@ -36,5 +36,4 @@ public interface AuditedCollectionDao<T extends Entity> {
 
     Map<String, T> loadEntities(UUID objectId, ObjectType objectType, InternalTenantContext context);
 
-    Map<String, T> loadEntitiesFromTransaction(Transmogrifier dao, UUID objectId, ObjectType objectType, InternalTenantContext context);
 }
diff --git a/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java b/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java
index 9f30dc1..ffbfe94 100644
--- a/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java
+++ b/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java
@@ -163,12 +163,6 @@ public abstract class AuditedCollectionDaoBase<T extends Entity, V> implements A
         return getMap(thisDao, objectId, objectType, context);
     }
 
-    @Override
-    public Map<String, T> loadEntitiesFromTransaction(final Transmogrifier dao, final UUID objectId, final ObjectType objectType, final InternalTenantContext context) {
-        final UpdatableEntityCollectionSqlDao<T> thisDao = transmogrifyDao(dao, context);
-        return getMap(thisDao, objectId, objectType, context);
-    }
-
     private Map<String, T> getMap(final UpdatableEntityCollectionSqlDao<T> dao, final UUID objectId, final ObjectType objectType, final InternalTenantContext context) {
         final List<T> entities = dao.load(objectId.toString(), objectType, context);
         final Map<String, T> results = new HashMap<String, T>();
diff --git a/util/src/main/java/com/ning/billing/util/dao/EntityAudit.java b/util/src/main/java/com/ning/billing/util/dao/EntityAudit.java
index a76d208..a41b882 100644
--- a/util/src/main/java/com/ning/billing/util/dao/EntityAudit.java
+++ b/util/src/main/java/com/ning/billing/util/dao/EntityAudit.java
@@ -60,4 +60,39 @@ public class EntityAudit extends EntityBase {
                ", changeType=" + changeType +
                '}';
     }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        if (!super.equals(o)) {
+            return false;
+        }
+
+        final EntityAudit that = (EntityAudit) o;
+
+        if (changeType != that.changeType) {
+            return false;
+        }
+        if (tableName != that.tableName) {
+            return false;
+        }
+        if (targetRecordId != null ? !targetRecordId.equals(that.targetRecordId) : that.targetRecordId != null) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (tableName != null ? tableName.hashCode() : 0);
+        result = 31 * result + (targetRecordId != null ? targetRecordId.hashCode() : 0);
+        result = 31 * result + (changeType != null ? changeType.hashCode() : 0);
+        return result;
+    }
 }
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntityDao.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntityDao.java
index 370f0e9..3470485 100644
--- a/util/src/main/java/com/ning/billing/util/entity/dao/EntityDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntityDao.java
@@ -19,14 +19,15 @@ package com.ning.billing.util.entity.dao;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.BillingExceptionBase;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.entity.Entity;
 import com.ning.billing.util.entity.EntityPersistenceException;
 
-public interface EntityDao<T extends Entity> {
+public interface EntityDao<T extends Entity, U extends BillingExceptionBase> {
 
-    public void create(T entity, InternalCallContext context) throws EntityPersistenceException;
+    public void create(T entity, InternalCallContext context) throws U;
 
     public Long getRecordId(UUID id, InternalTenantContext context);
 
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntityDaoBase.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntityDaoBase.java
new file mode 100644
index 0000000..e38bb40
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntityDaoBase.java
@@ -0,0 +1,116 @@
+/*
+ * 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.util.List;
+import java.util.UUID;
+
+import com.ning.billing.BillingExceptionBase;
+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.entity.Entity;
+import com.ning.billing.util.entity.EntityPersistenceException;
+
+public abstract class EntityDaoBase<T extends Entity, U extends BillingExceptionBase> implements EntityDao<T, U> {
+
+    protected final EntitySqlDaoTransactionalJdbiWrapper transactionalSqlDao;
+
+    public EntityDaoBase(final EntitySqlDaoTransactionalJdbiWrapper transactionalSqlDao) {
+        this.transactionalSqlDao = transactionalSqlDao;
+    }
+
+    @Override
+    public void create(final T entity, final InternalCallContext context) throws U {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+
+                if (getById(entity.getId(), context) != null) {
+                    throw generateAlreadyExistsException(entity, context);
+                }
+                final EntitySqlDao<T> transactional = entitySqlDaoWrapperFactory.become(EntitySqlDao.class);
+                transactional.create(entity, context);
+
+                postBusEventFromTransaction(entity, entity, ChangeType.INSERT, entitySqlDaoWrapperFactory, context);
+                return null;
+            }
+        });
+    }
+
+    protected void postBusEventFromTransaction(T entity, T savedEntity, ChangeType changeType, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalCallContext context)
+        throws BillingExceptionBase {
+    }
+
+    protected abstract U generateAlreadyExistsException(final T entity, final InternalCallContext context);
+
+    @Override
+    public Long getRecordId(final UUID id, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Long>() {
+
+            @Override
+            public Long inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(EntitySqlDao.class).getRecordId(id.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public T getByRecordId(final Long recordId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<T>() {
+
+            @Override
+            public T inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return (T) entitySqlDaoWrapperFactory.become(EntitySqlDao.class).getByRecordId(recordId, context);
+            }
+        });
+    }
+
+    @Override
+    public T getById(final UUID id, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<T>() {
+
+            @Override
+            public T inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return (T) entitySqlDaoWrapperFactory.become(EntitySqlDao.class).getById(id.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public List<T> get(final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<T>>() {
+
+            @Override
+            public List<T> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(EntitySqlDao.class).get(context);
+            }
+        });
+    }
+
+    @Override
+    public void test(final InternalTenantContext context) {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                entitySqlDaoWrapperFactory.become(EntitySqlDao.class).test(context);
+                return null;
+            }
+        });
+    }
+}
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 5ddcc53..fa99851 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
@@ -35,6 +35,7 @@ import com.ning.billing.util.dao.HistorySqlDao;
 import com.ning.billing.util.entity.Entity;
 import com.ning.billing.util.entity.EntityPersistenceException;
 
+@EntitySqlDaoStringTemplate
 public interface EntitySqlDao<T extends Entity> extends AuditSqlDao, HistorySqlDao<T>, Transmogrifier, Transactional<EntitySqlDao<T>>, CloseMe {
 
     @SqlUpdate
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
index 11e94d7..03fdf7f 100644
--- 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
@@ -18,6 +18,8 @@ package com.ning.billing.util.entity.dao;
 
 import java.lang.reflect.Proxy;
 
+import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
+
 import com.ning.billing.util.entity.Entity;
 
 /**
@@ -47,6 +49,10 @@ public class EntitySqlDaoWrapperFactory<InitialSqlDao extends EntitySqlDao> {
         return create(newSqlDaoClass, sqlDao.become(newSqlDaoClass));
     }
 
+    public <SelfType> SelfType transmogrify(final Class<SelfType> newTransactionalClass) {
+        return sqlDao.become(newTransactionalClass);
+    }
+
     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};
diff --git a/util/src/main/java/com/ning/billing/util/entity/EntityBase.java b/util/src/main/java/com/ning/billing/util/entity/EntityBase.java
index 4babfbe..628a078 100644
--- a/util/src/main/java/com/ning/billing/util/entity/EntityBase.java
+++ b/util/src/main/java/com/ning/billing/util/entity/EntityBase.java
@@ -62,4 +62,36 @@ public abstract class EntityBase implements Entity {
     public DateTime getUpdatedDate() {
         return updatedDate;
     }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final EntityBase that = (EntityBase) o;
+
+        if (createdDate != null ? !createdDate.equals(that.createdDate) : that.createdDate != null) {
+            return false;
+        }
+        if (id != null ? !id.equals(that.id) : that.id != null) {
+            return false;
+        }
+        if (updatedDate != null ? !updatedDate.equals(that.updatedDate) : that.updatedDate != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = id != null ? id.hashCode() : 0;
+        result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
+        result = 31 * result + (updatedDate != null ? updatedDate.hashCode() : 0);
+        return result;
+    }
 }
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotificationQueue.java b/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotificationQueue.java
index b20c1a7..6f58a2e 100644
--- a/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotificationQueue.java
+++ b/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotificationQueue.java
@@ -36,6 +36,8 @@ import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.UserType;
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueHandler;
 import com.ning.billing.util.notificationq.dao.NotificationSqlDao;
 
@@ -89,12 +91,12 @@ public class DefaultNotificationQueue extends NotificationQueueBase {
     }
 
     @Override
-    public void recordFutureNotificationFromTransaction(final Transmogrifier transactionalDao,
+    public void recordFutureNotificationFromTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> transactionalDao,
                                                         final DateTime futureNotificationTime,
                                                         final UUID accountId,
                                                         final NotificationKey notificationKey,
                                                         final InternalCallContext context) throws IOException {
-        final NotificationSqlDao transactionalNotificationDao = transactionalDao.become(NotificationSqlDao.class);
+        final NotificationSqlDao transactionalNotificationDao = transactionalDao.transmogrify(NotificationSqlDao.class);
         recordFutureNotificationInternal(futureNotificationTime, accountId, notificationKey, transactionalNotificationDao, context);
     }
 
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueue.java b/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueue.java
index 1444425..e592674 100644
--- a/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueue.java
+++ b/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueue.java
@@ -24,6 +24,9 @@ import org.joda.time.DateTime;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 
 import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.entity.dao.EntityDao;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.queue.QueueLifecycle;
 
 public interface NotificationQueue extends QueueLifecycle {
@@ -47,7 +50,7 @@ public interface NotificationQueue extends QueueLifecycle {
      * @param futureNotificationTime the time at which the notification is ready
      * @param notificationKey        the key for that notification
      */
-    public void recordFutureNotificationFromTransaction(final Transmogrifier transactionalDao,
+    public void recordFutureNotificationFromTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> transactionalDao,
                                                         final DateTime futureNotificationTime,
                                                         final UUID accountId,
                                                         final NotificationKey notificationKey,
diff --git a/util/src/main/java/com/ning/billing/util/svcapi/tag/DefaultTagInternalApi.java b/util/src/main/java/com/ning/billing/util/svcapi/tag/DefaultTagInternalApi.java
index 6bbc8eb..5216eba 100644
--- a/util/src/main/java/com/ning/billing/util/svcapi/tag/DefaultTagInternalApi.java
+++ b/util/src/main/java/com/ning/billing/util/svcapi/tag/DefaultTagInternalApi.java
@@ -25,6 +25,9 @@ import com.ning.billing.ObjectType;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.tag.ControlTagType;
+import com.ning.billing.util.tag.DefaultControlTag;
+import com.ning.billing.util.tag.DescriptiveTag;
 import com.ning.billing.util.tag.Tag;
 import com.ning.billing.util.tag.TagDefinition;
 import com.ning.billing.util.tag.dao.TagDao;
@@ -48,8 +51,7 @@ public class DefaultTagInternalApi implements TagInternalApi {
     }
 
     @Override
-    public Map<String, Tag> getTags(UUID objectId, ObjectType objectType,
-            InternalTenantContext context) {
+    public List<Tag> getTags(UUID objectId, ObjectType objectType, InternalTenantContext context) {
         return tagDao.getTags(objectId, objectType, context);
     }
 
@@ -57,7 +59,11 @@ public class DefaultTagInternalApi implements TagInternalApi {
     public void addTag(UUID objectId, ObjectType objectType,
             UUID tagDefinitionId, InternalCallContext context)
             throws TagApiException {
-        tagDao.insertTag(objectId, objectType, tagDefinitionId, context);
+
+        final TagDefinition tagDefinition = tagDefinitionDao.getById(tagDefinitionId, context);
+        final Tag tag = tagDefinition.isControlTag() ? new DefaultControlTag(ControlTagType.getTypeFromId(tagDefinition.getId()), ObjectType.ACCOUNT, objectId, context.getCreatedDate()) :
+                        new DescriptiveTag(tagDefinition.getId(), ObjectType.ACCOUNT, objectId, context.getCreatedDate());
+        tagDao.create(tag, context);
 
     }
 
diff --git a/util/src/main/java/com/ning/billing/util/svcapi/tag/TagInternalApi.java b/util/src/main/java/com/ning/billing/util/svcapi/tag/TagInternalApi.java
index 3d6a7c2..40f191e 100644
--- a/util/src/main/java/com/ning/billing/util/svcapi/tag/TagInternalApi.java
+++ b/util/src/main/java/com/ning/billing/util/svcapi/tag/TagInternalApi.java
@@ -39,7 +39,7 @@ public interface TagInternalApi {
      * @param context    call context
      * @return mapping tag id -> tag
      */
-    public Map<String, Tag> getTags(UUID objectId, ObjectType objectType, InternalTenantContext context);
+    public List<Tag> getTags(UUID objectId, ObjectType objectType, InternalTenantContext context);
 
     public void addTag(UUID objectId, ObjectType objectType, UUID tagDefinitionId, InternalCallContext context) throws TagApiException;
 
diff --git a/util/src/main/java/com/ning/billing/util/svcsapi/bus/InternalBus.java b/util/src/main/java/com/ning/billing/util/svcsapi/bus/InternalBus.java
index c445c8d..e952fa0 100644
--- a/util/src/main/java/com/ning/billing/util/svcsapi/bus/InternalBus.java
+++ b/util/src/main/java/com/ning/billing/util/svcsapi/bus/InternalBus.java
@@ -19,6 +19,8 @@ package com.ning.billing.util.svcsapi.bus;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 
 import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.events.BusInternalEvent;
 
 import com.google.common.eventbus.Subscribe;
@@ -91,9 +93,11 @@ public interface InternalBus {
      * Guarantees that the event is persisted on disk from within the same transaction
      *
      * @param event   to be posted
-     * @param dao     a valid DAO object obtained through the DBI.onDemand() API.
+     * @param transactional     a valid transactional object
      * @param context call context. account record id and tenant id are expected to be populated
      * @throws EventBusException if bus not been started yet
      */
-    public void postFromTransaction(BusInternalEvent event, Transmogrifier dao, InternalCallContext context) throws EventBusException;
+    public void postFromTransaction(BusInternalEvent event, EntitySqlDaoWrapperFactory<EntitySqlDao> transactional, InternalCallContext context) throws EventBusException;
+
+
 }
diff --git a/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java b/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java
index 52cd460..7ff9a13 100644
--- a/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java
+++ b/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java
@@ -26,8 +26,12 @@ import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.api.TagUserApi;
 import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.billing.util.tag.ControlTagType;
+import com.ning.billing.util.tag.DefaultControlTag;
+import com.ning.billing.util.tag.DescriptiveTag;
 import com.ning.billing.util.tag.Tag;
 import com.ning.billing.util.tag.TagDefinition;
 import com.ning.billing.util.tag.dao.TagDao;
@@ -77,15 +81,19 @@ public class DefaultTagUserApi implements TagUserApi {
 
     @Override
     public void addTags(final UUID objectId, final ObjectType objectType, final Collection<UUID> tagDefinitionIds, final CallContext context) throws TagApiException {
-        // TODO: consider making this batch
         for (final UUID tagDefinitionId : tagDefinitionIds) {
-            tagDao.insertTag(objectId, objectType, tagDefinitionId, internalCallContextFactory.createInternalCallContext(objectId, objectType, context));
+           addTag(objectId, objectType, tagDefinitionId,context);
         }
     }
 
     @Override
     public void addTag(final UUID objectId, final ObjectType objectType, final UUID tagDefinitionId, final CallContext context) throws TagApiException {
-        tagDao.insertTag(objectId, objectType, tagDefinitionId, internalCallContextFactory.createInternalCallContext(objectId, objectType, context));
+        final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(objectId, objectType, context);
+
+        final TagDefinition tagDefinition = tagDefinitionDao.getById(tagDefinitionId, internalContext);
+        final Tag tag = tagDefinition.isControlTag() ? new DefaultControlTag(ControlTagType.getTypeFromId(tagDefinition.getId()), objectType, objectId, context.getCreatedDate()) :
+                        new DescriptiveTag(tagDefinition.getId(), objectType, objectId, context.getCreatedDate());
+        tagDao.create(tag, internalContext);
     }
 
     @Override
@@ -102,7 +110,7 @@ public class DefaultTagUserApi implements TagUserApi {
     }
 
     @Override
-    public Map<String, Tag> getTags(final UUID objectId, final ObjectType objectType, final TenantContext context) {
+    public List<Tag> getTags(final UUID objectId, final ObjectType objectType, final TenantContext context) {
         return tagDao.getTags(objectId, objectType, internalCallContextFactory.createInternalTenantContext(context));
     }
 
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/AuditedTagDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/AuditedTagDao.java
index 06469ab..1cec626 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/AuditedTagDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/AuditedTagDao.java
@@ -25,12 +25,15 @@ import org.skife.jdbi.v2.IDBI;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.ning.billing.BillingExceptionBase;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.ObjectType;
 import com.ning.billing.util.api.TagApiException;
+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.entity.dao.EntityDaoBase;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
 import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
 import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
@@ -47,11 +50,10 @@ import com.ning.billing.util.tag.api.user.TagEventBuilder;
 
 import com.google.inject.Inject;
 
-public class AuditedTagDao implements TagDao {
+public class AuditedTagDao extends EntityDaoBase<Tag, TagApiException> implements TagDao {
 
     private static final Logger log = LoggerFactory.getLogger(AuditedTagDao.class);
 
-    private final EntitySqlDaoTransactionalJdbiWrapper transactionalSqlDao;
     private final TagSqlDao tagSqlDao;
     private final TagEventBuilder tagEventBuilder;
     private final InternalBus bus;
@@ -59,15 +61,56 @@ public class AuditedTagDao implements TagDao {
 
     @Inject
     public AuditedTagDao(final IDBI dbi, final TagEventBuilder tagEventBuilder, final InternalBus bus, final Clock clock) {
+        super(new EntitySqlDaoTransactionalJdbiWrapper(dbi));
         this.clock = clock;
         this.tagEventBuilder = tagEventBuilder;
         this.bus = bus;
         this.tagSqlDao = dbi.onDemand(TagSqlDao.class);
-        this.transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi);
     }
 
-    protected Tag getEquivalenceObjectFor(final Tag obj) {
-        return obj;
+
+    @Override
+    public List<Tag> getTags(final UUID objectId, final ObjectType objectType, final InternalTenantContext internalTenantContext) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<Tag>>() {
+            @Override
+            public List<Tag> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(TagSqlDao.class).getTagsForObject(objectId, objectType, internalTenantContext);
+            }
+        });
+    }
+
+    @Override
+    protected void postBusEventFromTransaction(Tag tag, Tag savedTag, ChangeType changeType, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalCallContext context)
+            throws BillingExceptionBase {
+
+        final TagInternalEvent tagEvent;
+        final TagDefinition tagDefinition = getTagDefinitionFromTransaction(tag.getTagDefinitionId(), entitySqlDaoWrapperFactory, context);
+        switch(changeType) {
+            case INSERT:
+                tagEvent = (tagDefinition.isControlTag()) ?
+                           tagEventBuilder.newControlTagCreationEvent(tag.getId(), tag.getObjectId(), tag.getObjectType(), tagDefinition, context) :
+                           tagEventBuilder.newUserTagCreationEvent(tag.getId(), tag.getObjectId(), tag.getObjectType(), tagDefinition, context);
+                break;
+            case DELETE:
+                tagEvent = (tagDefinition.isControlTag()) ?
+                           tagEventBuilder.newControlTagDeletionEvent(tag.getId(), tag.getObjectId(), tag.getObjectType(), tagDefinition, context) :
+                           tagEventBuilder.newUserTagDeletionEvent(tag.getId(), tag.getObjectId(), tag.getObjectType(), tagDefinition, context);
+                break;
+            default:
+                return;
+        }
+
+        try {
+            bus.postFromTransaction(tagEvent, entitySqlDaoWrapperFactory, context);
+        } catch (InternalBus.EventBusException e) {
+            log.warn("Failed to post tag event for tag " + tag.getId().toString(), e);
+        }
+    }
+
+
+    @Override
+    protected TagApiException generateAlreadyExistsException(final Tag entity, final InternalCallContext context) {
+        return new TagApiException(ErrorCode.TAG_ALREADY_EXISTS, entity.getId());
     }
 
     private TagDefinition getTagDefinitionFromTransaction(final UUID tagDefinitionId, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) throws TagApiException {
@@ -89,52 +132,6 @@ public class AuditedTagDao implements TagDao {
         return tagDefintion;
     }
 
-    @Override
-    public Tag getTagById(final UUID tagId, final InternalTenantContext context) {
-        return tagSqlDao.getById(tagId.toString(), context);
-    }
-
-    @Override
-    public Map<String, Tag> getTags(final UUID objectId, final ObjectType objectType, final InternalTenantContext internalTenantContext) {
-        List<Tag> tags = tagSqlDao.getTagsForObject(objectId, objectType, internalTenantContext);
-        Map<String, Tag> result = new HashMap<String, Tag>();
-        for (Tag cur : tags) {
-            result.put(cur.getId().toString(), cur);
-        }
-        return result;
-    }
-
-    @Override
-    public void insertTag(final UUID objectId, final ObjectType objectType, final UUID tagDefinitionId, final InternalCallContext context)
-            throws TagApiException {
-        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
-            @Override
-            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-
-                final TagDefinition tagDefinition = getTagDefinitionFromTransaction(tagDefinitionId, entitySqlDaoWrapperFactory, context);
-
-                final Tag tag = tagDefinition.isControlTag() ? new DefaultControlTag(ControlTagType.getTypeFromId(tagDefinition.getId()), objectType, objectId, clock.getUTCNow()) :
-                                new DescriptiveTag(tagDefinition.getId(), objectType, objectId, clock.getUTCNow());
-
-                // Create the tag
-                entitySqlDaoWrapperFactory.become(TagSqlDao.class).create(tag, context);
-
-                // Post an event to the Bus
-                final TagInternalEvent tagEvent;
-                if (tagDefinition.isControlTag()) {
-                    tagEvent = tagEventBuilder.newControlTagCreationEvent(tag.getId(), objectId, objectType, tagDefinition, context);
-                } else {
-                    tagEvent = tagEventBuilder.newUserTagCreationEvent(tag.getId(), objectId, objectType, tagDefinition, context);
-                }
-                try {
-                    bus.postFromTransaction(tagEvent, tagSqlDao, context);
-                } catch (InternalBus.EventBusException e) {
-                    log.warn("Failed to post tag creation event for tag " + tag.getId().toString(), e);
-                }
-                return null;
-            }
-        });
-    }
 
     @Override
     public void deleteTag(final UUID objectId, final ObjectType objectType, final UUID tagDefinitionId, final InternalCallContext context) throws TagApiException {
@@ -157,23 +154,10 @@ public class AuditedTagDao implements TagDao {
                 if (tag == null) {
                     throw new TagApiException(ErrorCode.TAG_DOES_NOT_EXIST, tagDefinition.getName());
                 }
-
                 // Delete the tag
                 transactional.deleteById(tag.getId(), context);
 
-                // Post an event to the Bus
-                final TagInternalEvent tagEvent;
-                if (tagDefinition.isControlTag()) {
-                    tagEvent = tagEventBuilder.newControlTagDeletionEvent(tag.getId(), objectId, objectType, tagDefinition, context);
-                } else {
-                    tagEvent = tagEventBuilder.newUserTagDeletionEvent(tag.getId(), objectId, objectType, tagDefinition, context);
-                }
-                try {
-                    // TODO ETX
-                    bus.postFromTransaction(tagEvent, tagSqlDao, context);
-                } catch (InternalBus.EventBusException e) {
-                    log.warn("Failed to post tag deletion event for tag " + tag.getId().toString(), e);
-                }
+                postBusEventFromTransaction(tag, tag, ChangeType.DELETE, entitySqlDaoWrapperFactory, context);
                 return null;
             }
         });
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 26c24bb..e2a3ad5 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
@@ -26,15 +26,19 @@ import org.skife.jdbi.v2.exceptions.TransactionFailedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.ning.billing.BillingExceptionBase;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.util.api.TagDefinitionApiException;
+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.entity.dao.EntityDaoBase;
 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.events.TagInternalEvent;
 import com.ning.billing.util.svcsapi.bus.InternalBus;
 import com.ning.billing.util.tag.ControlTagType;
 import com.ning.billing.util.tag.DefaultTagDefinition;
@@ -45,21 +49,20 @@ import com.google.common.base.Function;
 import com.google.common.collect.Collections2;
 import com.google.inject.Inject;
 
-public class DefaultTagDefinitionDao implements TagDefinitionDao {
+public class DefaultTagDefinitionDao extends EntityDaoBase<TagDefinition, TagDefinitionApiException> 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;
 
     @Inject
     public DefaultTagDefinitionDao(final IDBI dbi, final TagEventBuilder tagEventBuilder, final InternalBus bus) {
+        super(new EntitySqlDaoTransactionalJdbiWrapper(dbi));
         this.tagEventBuilder = tagEventBuilder;
         this.bus = bus;
         this.tagDefinitionSqlDao = dbi.onDemand(TagDefinitionSqlDao.class);
-        this.transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi);
     }
 
     @Override
@@ -150,7 +153,7 @@ public class DefaultTagDefinitionDao implements TagDefinitionDao {
                         tagDefinitionEvent = tagEventBuilder.newUserTagDefinitionCreationEvent(tagDefinition.getId(), tagDefinition, context);
                     }
                     try {
-                        bus.postFromTransaction(tagDefinitionEvent, tagDefinitionSqlDao, context);
+                        bus.postFromTransaction(tagDefinitionEvent, entitySqlDaoWrapperFactory, context);
                     } catch (InternalBus.EventBusException e) {
                         log.warn("Failed to post tag definition creation event for tag " + tagDefinition.getId(), e);
                     }
@@ -199,19 +202,7 @@ public class DefaultTagDefinitionDao implements TagDefinitionDao {
                     // Delete it
                     tagDefinitionSqlDao.deleteTagDefinition(definitionId.toString(), context);
 
-                    // Post an event to the Bus
-                    final TagDefinitionInternalEvent tagDefinitionEvent;
-                    if (tagDefinition.isControlTag()) {
-                        tagDefinitionEvent = tagEventBuilder.newControlTagDefinitionDeletionEvent(tagDefinition.getId(), tagDefinition, context);
-                    } else {
-                        tagDefinitionEvent = tagEventBuilder.newUserTagDefinitionDeletionEvent(tagDefinition.getId(), tagDefinition, context);
-                    }
-                    try {
-                        bus.postFromTransaction(tagDefinitionEvent, tagDefinitionSqlDao, context);
-                    } catch (InternalBus.EventBusException e) {
-                        log.warn("Failed to post tag definition deletion event for tag " + tagDefinition.getId(), e);
-                    }
-
+                    postBusEventFromTransaction(tagDefinition, tagDefinition, ChangeType.DELETE, entitySqlDaoWrapperFactory, context);
                     return null;
                 }
             });
@@ -223,4 +214,37 @@ public class DefaultTagDefinitionDao implements TagDefinitionDao {
             }
         }
     }
+
+    protected void postBusEventFromTransaction(TagDefinition tagDefinition, TagDefinition savedTagDefinition, ChangeType changeType, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalCallContext context)
+            throws BillingExceptionBase {
+
+
+        final TagDefinitionInternalEvent tagDefinitionEvent;
+        switch(changeType) {
+            case INSERT:
+                tagDefinitionEvent = (tagDefinition.isControlTag()) ?
+                                     tagEventBuilder.newControlTagDefinitionCreationEvent(tagDefinition.getId(), tagDefinition, context) :
+                                     tagEventBuilder.newUserTagDefinitionCreationEvent(tagDefinition.getId(), tagDefinition, context);
+
+                break;
+            case DELETE:
+                tagDefinitionEvent = (tagDefinition.isControlTag()) ?
+                                     tagEventBuilder.newControlTagDefinitionDeletionEvent(tagDefinition.getId(), tagDefinition, context) :
+                                     tagEventBuilder.newUserTagDefinitionDeletionEvent(tagDefinition.getId(), tagDefinition, context);
+                break;
+            default:
+                return;
+        }
+
+        try {
+            bus.postFromTransaction(tagDefinitionEvent, entitySqlDaoWrapperFactory, context);
+        } catch (InternalBus.EventBusException e) {
+            log.warn("Failed to post tag definition event for tag " + tagDefinition.getId().toString(), e);
+        }
+    }
+
+    @Override
+    protected TagDefinitionApiException generateAlreadyExistsException(final TagDefinition entity, final InternalCallContext context) {
+        return new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_ALREADY_EXISTS, entity.getId());
+    }
 }
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java
index bb82f20..f1e5c3f 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.util.tag.dao;
 
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
@@ -28,11 +29,11 @@ import com.ning.billing.util.tag.Tag;
 
 public interface TagDao {
 
-    void insertTag(UUID objectId, ObjectType objectType, UUID tagDefinition, InternalCallContext context) throws TagApiException;
+    void create(Tag tag, InternalCallContext context) throws TagApiException;
 
     void deleteTag(UUID objectId, ObjectType objectType, UUID tagDefinition, InternalCallContext context) throws TagApiException;
 
-    Tag getTagById(UUID tagId, InternalTenantContext context);
+    Tag getById(UUID tagId, InternalTenantContext context);
 
-    Map<String,Tag> getTags(UUID objectId, ObjectType objectType, InternalTenantContext internalTenantContext);
+    List<Tag> getTags(UUID objectId, ObjectType objectType, InternalTenantContext internalTenantContext);
 }
diff --git a/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg
index bbfc58d..1fca412 100644
--- a/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg
@@ -227,7 +227,7 @@ auditTableValues() ::= <<
 , :changeType
 , :createdBy
 , :reasonCode
-, :comment
+, :comments
 , :userToken
 , :createdDate
 <if(accountRecordIdField(""))>, <accountRecordIdValue()><endif>
diff --git a/util/src/test/java/com/ning/billing/util/audit/dao/TestDefaultAuditDao.java b/util/src/test/java/com/ning/billing/util/audit/dao/TestDefaultAuditDao.java
index c4d06e2..3706e15 100644
--- a/util/src/test/java/com/ning/billing/util/audit/dao/TestDefaultAuditDao.java
+++ b/util/src/test/java/com/ning/billing/util/audit/dao/TestDefaultAuditDao.java
@@ -41,6 +41,9 @@ import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.dao.TableName;
 import com.ning.billing.util.glue.AuditModule;
 import com.ning.billing.util.svcsapi.bus.InternalBus;
+import com.ning.billing.util.tag.ControlTagType;
+import com.ning.billing.util.tag.DefaultControlTag;
+import com.ning.billing.util.tag.DescriptiveTag;
 import com.ning.billing.util.tag.MockTagStoreModuleSql;
 import com.ning.billing.util.tag.Tag;
 import com.ning.billing.util.tag.TagDefinition;
@@ -116,12 +119,16 @@ public class TestDefaultAuditDao extends UtilTestSuiteWithEmbeddedDB {
 
         // Create a tag
         final UUID objectId = UUID.randomUUID();
-        tagDao.insertTag(objectId, ObjectType.ACCOUNT, tagDefinition.getId(), internalCallContext);
-        final Map<String, Tag> tags = tagDao.getTags(objectId, ObjectType.ACCOUNT, internalCallContext);
+
+        final Tag tag = tagDefinition.isControlTag() ? new DefaultControlTag(ControlTagType.getTypeFromId(tagDefinition.getId()), ObjectType.ACCOUNT, objectId, clock.getUTCNow()) :
+                                                       new DescriptiveTag(tagDefinition.getId(), ObjectType.ACCOUNT, objectId, clock.getUTCNow());
+
+        tagDao.create(tag, internalCallContext);
+        final List<Tag> tags = tagDao.getTags(objectId, ObjectType.ACCOUNT, internalCallContext);
         Assert.assertEquals(tags.size(), 1);
-        final Tag tag = tags.values().iterator().next();
-        Assert.assertEquals(tag.getTagDefinitionId(), tagDefinition.getId());
-        tagId = tag.getId();
+        final Tag savedTag = tags.get(0);
+        Assert.assertEquals(savedTag.getTagDefinitionId(), tagDefinition.getId());
+        tagId = savedTag.getId();
     }
 
     private void verifyAuditLogsForTag(final List<AuditLog> auditLogs, final AuditLevel level) {
@@ -133,7 +140,7 @@ public class TestDefaultAuditDao extends UtilTestSuiteWithEmbeddedDB {
         Assert.assertEquals(auditLogs.size(), 1);
         Assert.assertEquals(auditLogs.get(0).getUserToken(), internalCallContext.getUserToken().toString());
         Assert.assertEquals(auditLogs.get(0).getChangeType(), ChangeType.INSERT);
-        Assert.assertEquals(auditLogs.get(0).getComment(), internalCallContext.getComment());
+        Assert.assertEquals(auditLogs.get(0).getComment(), internalCallContext.getComments());
         Assert.assertEquals(auditLogs.get(0).getReasonCode(), internalCallContext.getReasonCode());
         Assert.assertEquals(auditLogs.get(0).getUserName(), internalCallContext.getCreatedBy());
         Assert.assertNotNull(auditLogs.get(0).getCreatedDate());
diff --git a/util/src/test/java/com/ning/billing/util/callcontext/TestCallContext.java b/util/src/test/java/com/ning/billing/util/callcontext/TestCallContext.java
index 99d1870..d094414 100644
--- a/util/src/test/java/com/ning/billing/util/callcontext/TestCallContext.java
+++ b/util/src/test/java/com/ning/billing/util/callcontext/TestCallContext.java
@@ -63,7 +63,7 @@ public class TestCallContext implements CallContext {
     }
 
     @Override
-    public String getComment() {
+    public String getComments() {
         return null;
     }
 
diff --git a/util/src/test/java/com/ning/billing/util/callcontext/TestDefaultCallContext.java b/util/src/test/java/com/ning/billing/util/callcontext/TestDefaultCallContext.java
index 8d79422..860d537 100644
--- a/util/src/test/java/com/ning/billing/util/callcontext/TestDefaultCallContext.java
+++ b/util/src/test/java/com/ning/billing/util/callcontext/TestDefaultCallContext.java
@@ -43,7 +43,7 @@ public class TestDefaultCallContext extends UtilTestSuite {
         Assert.assertEquals(callContext.getTenantId(), tenantId);
         Assert.assertEquals(callContext.getCreatedDate(), createdDate);
         Assert.assertNull(callContext.getCallOrigin());
-        Assert.assertEquals(callContext.getComment(), comment);
+        Assert.assertEquals(callContext.getComments(), comment);
         Assert.assertEquals(callContext.getReasonCode(), reasonCode);
         Assert.assertEquals(callContext.getUserName(), userName);
         Assert.assertEquals(callContext.getUpdatedDate(), createdDate);
diff --git a/util/src/test/java/com/ning/billing/util/callcontext/TestInternalCallContextFactory.java b/util/src/test/java/com/ning/billing/util/callcontext/TestInternalCallContextFactory.java
index 3b4c06d..04f4f7c 100644
--- a/util/src/test/java/com/ning/billing/util/callcontext/TestInternalCallContextFactory.java
+++ b/util/src/test/java/com/ning/billing/util/callcontext/TestInternalCallContextFactory.java
@@ -96,7 +96,7 @@ public class TestInternalCallContextFactory extends UtilTestSuiteWithEmbeddedDB 
 
     private void verifyInternalCallContext(final InternalCallContext context) {
         Assert.assertEquals(context.getCallOrigin(), callContext.getCallOrigin());
-        Assert.assertEquals(context.getComment(), callContext.getComment());
+        Assert.assertEquals(context.getComments(), callContext.getComments());
         Assert.assertEquals(context.getCreatedDate(), callContext.getCreatedDate());
         Assert.assertEquals(context.getReasonCode(), callContext.getReasonCode());
         Assert.assertEquals(context.getUpdatedDate(), callContext.getUpdatedDate());
diff --git a/util/src/test/java/com/ning/billing/util/customfield/api/TestDefaultCustomFieldUserApi.java b/util/src/test/java/com/ning/billing/util/customfield/api/TestDefaultCustomFieldUserApi.java
index ad9e6a4..40f9599 100644
--- a/util/src/test/java/com/ning/billing/util/customfield/api/TestDefaultCustomFieldUserApi.java
+++ b/util/src/test/java/com/ning/billing/util/customfield/api/TestDefaultCustomFieldUserApi.java
@@ -46,7 +46,7 @@ public class TestDefaultCustomFieldUserApi extends UtilTestSuiteWithEmbeddedDB {
     @BeforeMethod(groups = "slow")
     public void setUp() throws Exception {
         final InternalCallContextFactory internalCallContextFactory = new InternalCallContextFactory(getMysqlTestingHelper().getDBI(), new ClockMock());
-        final CustomFieldDao customFieldDao = new AuditedCustomFieldDao(getMysqlTestingHelper().getDBI(), new DefaultClock());
+        final CustomFieldDao customFieldDao = new AuditedCustomFieldDao(getMysqlTestingHelper().getDBI());
         customFieldUserApi = new DefaultCustomFieldUserApi(internalCallContextFactory, customFieldDao);
     }
 
@@ -66,13 +66,13 @@ public class TestDefaultCustomFieldUserApi extends UtilTestSuiteWithEmbeddedDB {
             }
         });
 
-        final CustomField customField = new StringCustomField(UUID.randomUUID().toString().substring(1, 4), UUID.randomUUID().toString().substring(1, 4));
-        customFieldUserApi.saveCustomFields(accountId, ObjectType.ACCOUNT, ImmutableList.<CustomField>of(customField), callContext);
+        final CustomField customField = new StringCustomField(UUID.randomUUID().toString().substring(1, 4), UUID.randomUUID().toString().substring(1, 4), ObjectType.ACCOUNT, accountId, callContext.getCreatedDate());
+        customFieldUserApi.addCustomFields(ImmutableList.<CustomField>of(customField), callContext);
 
         // Verify the field was saved
-        final Map<String, CustomField> customFields = customFieldUserApi.getCustomFields(accountId, ObjectType.ACCOUNT, callContext);
-        Assert.assertEquals(customFields.keySet().size(), 1);
-        Assert.assertEquals(customFields.get(customField.getName()), customField);
+        final List<CustomField> customFields = customFieldUserApi.getCustomFields(accountId, ObjectType.ACCOUNT, callContext);
+        Assert.assertEquals(customFields.size(), 1);
+        Assert.assertEquals(customFields.get(0), customField);
         // Verify the account_record_id was populated
         getMysqlTestingHelper().getDBI().withHandle(new HandleCallback<Void>() {
             @Override
diff --git a/util/src/test/java/com/ning/billing/util/customfield/dao/MockCustomFieldDao.java b/util/src/test/java/com/ning/billing/util/customfield/dao/MockCustomFieldDao.java
index b1e703f..730e012 100644
--- a/util/src/test/java/com/ning/billing/util/customfield/dao/MockCustomFieldDao.java
+++ b/util/src/test/java/com/ning/billing/util/customfield/dao/MockCustomFieldDao.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.util.customfield.dao;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -23,41 +24,26 @@ import java.util.UUID;
 
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 
+import com.ning.billing.BillingExceptionBase;
 import com.ning.billing.ObjectType;
+import com.ning.billing.util.api.CustomFieldApiException;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.entity.Entity;
+import com.ning.billing.util.entity.dao.MockEntityDaoBase;
 
-public class MockCustomFieldDao implements CustomFieldDao {
-
-    private final Map<UUID, List<CustomField>> fields = new HashMap<UUID, List<CustomField>>();
-
-    @Override
-    public void saveEntitiesFromTransaction(final Transmogrifier transactionalDao, final UUID objectId, final ObjectType objectType,
-                                            final List<CustomField> entities, final InternalCallContext context) {
-        fields.put(objectId, entities);
-    }
-
-    @Override
-    public void saveEntities(final UUID objectId, final ObjectType objectType, final List<CustomField> entities, final InternalCallContext context) {
-        fields.put(objectId, entities);
-    }
-
-    @Override
-    public Map<String, CustomField> loadEntities(final UUID objectId, final ObjectType objectType, final InternalTenantContext context) {
-        return getMap(fields.get(objectId));
-    }
+public class MockCustomFieldDao extends MockEntityDaoBase<CustomField, CustomFieldApiException> implements CustomFieldDao {
 
     @Override
-    public Map<String, CustomField> loadEntitiesFromTransaction(final Transmogrifier dao, final UUID objectId, final ObjectType objectType, final InternalTenantContext context) {
-        return getMap(fields.get(objectId));
-    }
-
-    private Map<String, CustomField> getMap(final List<CustomField> customFields) {
-        final Map<String, CustomField> map = new HashMap<String, CustomField>();
-        for (final CustomField customField : customFields) {
-            map.put(customField.getName(), customField);
+    public List<CustomField> getCustomFields(final UUID objectId, final ObjectType objectType, final InternalTenantContext context) {
+        List<CustomField> result =  new ArrayList<CustomField>();
+        List<CustomField> all = get(context);
+        for (CustomField cur : all) {
+            if (cur.getObjectId().equals(objectId) && cur.getObjectType() == objectType) {
+                result.add(cur);
+            }
         }
-        return map;
+        return result;
     }
 }
diff --git a/util/src/test/java/com/ning/billing/util/customfield/TestFieldStore.java b/util/src/test/java/com/ning/billing/util/customfield/TestFieldStore.java
index 3f10449..78522e4 100644
--- a/util/src/test/java/com/ning/billing/util/customfield/TestFieldStore.java
+++ b/util/src/test/java/com/ning/billing/util/customfield/TestFieldStore.java
@@ -29,6 +29,7 @@ import com.ning.billing.KillbillTestSuiteWithEmbeddedDB;
 import com.ning.billing.ObjectType;
 import com.ning.billing.dbi.MysqlTestingHelper;
 import com.ning.billing.util.UtilTestSuiteWithEmbeddedDB;
+import com.ning.billing.util.api.CustomFieldApiException;
 import com.ning.billing.util.clock.DefaultClock;
 import com.ning.billing.util.customfield.dao.AuditedCustomFieldDao;
 import com.ning.billing.util.customfield.dao.CustomFieldDao;
@@ -47,7 +48,7 @@ public class TestFieldStore extends UtilTestSuiteWithEmbeddedDB {
     protected void setup() throws IOException {
         try {
             dbi = helper.getDBI();
-            customFieldDao = new AuditedCustomFieldDao(dbi, new DefaultClock());
+            customFieldDao = new AuditedCustomFieldDao(dbi);
         } catch (Throwable t) {
             log.error("Setup failed", t);
             fail(t.toString());
@@ -55,33 +56,19 @@ public class TestFieldStore extends UtilTestSuiteWithEmbeddedDB {
     }
 
     @Test(groups = "slow")
-    public void testFieldStore() {
+    public void testCreateCustomField() throws CustomFieldApiException {
         final UUID id = UUID.randomUUID();
         final ObjectType objectType = ObjectType.ACCOUNT;
 
-        final FieldStore fieldStore1 = new DefaultFieldStore(id, objectType);
 
         final String fieldName = "TestField1";
         String fieldValue = "Kitty Hawk";
-        fieldStore1.setValue(fieldName, fieldValue);
 
-        final CustomFieldSqlDao customFieldSqlDao = dbi.onDemand(CustomFieldSqlDao.class);
-        customFieldDao.saveEntitiesFromTransaction(customFieldSqlDao, id, objectType, fieldStore1.getEntityList(), internalCallContext);
-
-        final FieldStore fieldStore2 = DefaultFieldStore.create(id, objectType);
-        fieldStore2.add(customFieldSqlDao.load(id.toString(), objectType, internalCallContext));
-
-        assertEquals(fieldStore2.getValue(fieldName), fieldValue);
+        CustomField field = new StringCustomField(fieldName, fieldValue, objectType, id, internalCallContext.getCreatedDate());
+        customFieldDao.create(field, internalCallContext);
 
         fieldValue = "Cape Canaveral";
-        fieldStore2.setValue(fieldName, fieldValue);
-        assertEquals(fieldStore2.getValue(fieldName), fieldValue);
-        customFieldDao.saveEntitiesFromTransaction(customFieldSqlDao, id, objectType, fieldStore2.getEntityList(), internalCallContext);
-
-        final FieldStore fieldStore3 = DefaultFieldStore.create(id, objectType);
-        assertEquals(fieldStore3.getValue(fieldName), null);
-        fieldStore3.add(customFieldSqlDao.load(id.toString(), objectType, internalCallContext));
-
-        assertEquals(fieldStore3.getValue(fieldName), fieldValue);
+        CustomField field2 = new StringCustomField(fieldName, fieldValue, objectType, id, internalCallContext.getCreatedDate());
+        customFieldDao.create(field2 ,internalCallContext);
     }
 }
diff --git a/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritance.java b/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritance.java
index d89138c..62ed6c5 100644
--- a/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritance.java
+++ b/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritance.java
@@ -93,7 +93,7 @@ public class TestStringTemplateInheritance extends UtilTestSuite {
                                                                               "and t.tenant_record_id = :tenantRecordId\n" +
                                                                               ";");
         Assert.assertEquals(kombucha.getInstanceOf("getHistoryRecordId").toString(), "select\n" +
-                                                                                     "  max(t.history_record_id)\n" +
+                                                                                     "  max(t.record_id)\n" +
                                                                                      "from kombucha t\n" +
                                                                                      "where t.record_id = :recordId\n" +
                                                                                      "and t.tenant_record_id = :tenantRecordId\n" +
@@ -134,8 +134,9 @@ public class TestStringTemplateInheritance extends UtilTestSuite {
                                                                        "limit 1\n" +
                                                                        ";");
         Assert.assertEquals(kombucha.getInstanceOf("addHistoryFromTransaction").toString(), "insert into kombucha_history (\n" +
-                                                                                            "  record_id\n" +
-                                                                                            ", id\n" +
+                                                                                            "  id\n" +
+                                                                                            ", target_record_id\n" +
+                                                                                            ", change_type\n" +
                                                                                             ", tea\n" +
                                                                                             ", mushroom\n" +
                                                                                             ", sugar\n" +
@@ -143,8 +144,9 @@ public class TestStringTemplateInheritance extends UtilTestSuite {
                                                                                             ", tenant_record_id\n" +
                                                                                             ")\n" +
                                                                                             "values (\n" +
-                                                                                            "  :recordId\n" +
-                                                                                            ", :id\n" +
+                                                                                            "  :id\n" +
+                                                                                            ", :targetRecordId\n" +
+                                                                                            ", :changeType\n" +
                                                                                             ",   :tea\n" +
                                                                                             ", :mushroom\n" +
                                                                                             ", :sugar\n" +
@@ -152,27 +154,30 @@ public class TestStringTemplateInheritance extends UtilTestSuite {
                                                                                             ", :tenantRecordId\n" +
                                                                                             ")\n" +
                                                                                             ";");
+
         Assert.assertEquals(kombucha.getInstanceOf("insertAuditFromTransaction").toString(), "insert into audit_log (\n" +
-                                                                                             "table_name\n" +
-                                                                                             ", record_id\n" +
+                                                                                             "id\n" +
+                                                                                             ", table_name\n" +
+                                                                                             ", target_record_id\n" +
                                                                                              ", change_type\n" +
-                                                                                             ", change_date\n" +
-                                                                                             ", changed_by\n" +
+                                                                                             ", created_by\n" +
                                                                                              ", reason_code\n" +
                                                                                              ", comments\n" +
                                                                                              ", user_token\n" +
+                                                                                             ", created_date\n" +
                                                                                              ", account_record_id\n" +
                                                                                              ", tenant_record_id\n" +
                                                                                              ")\n" +
                                                                                              "values (\n" +
-                                                                                             "  :table_name\n" +
-                                                                                             ", :record_id\n" +
-                                                                                             ", :change_type\n" +
-                                                                                             ", :change_date\n" +
-                                                                                             ", :changed_by\n" +
-                                                                                             ", :reason_code\n" +
+                                                                                             "  :id\n" +
+                                                                                             ", :tableName\n" +
+                                                                                             ", :targetRecordId\n" +
+                                                                                             ", :changeType\n" +
+                                                                                             ", :createdBy\n" +
+                                                                                             ", :reasonCode\n" +
                                                                                              ", :comments\n" +
-                                                                                             ", :user_token\n" +
+                                                                                             ", :userToken\n" +
+                                                                                             ", :createdDate\n" +
                                                                                              ", :accountRecordId\n" +
                                                                                              ", :tenantRecordId\n" +
                                                                                              ")\n" +
diff --git a/util/src/test/java/com/ning/billing/util/entity/dao/MockEntityDaoBase.java b/util/src/test/java/com/ning/billing/util/entity/dao/MockEntityDaoBase.java
new file mode 100644
index 0000000..53b8d91
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/entity/dao/MockEntityDaoBase.java
@@ -0,0 +1,79 @@
+/*
+ * 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.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.ning.billing.BillingExceptionBase;
+import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.entity.Entity;
+
+import com.google.common.collect.ImmutableMap;
+
+public class MockEntityDaoBase<T extends Entity, U extends BillingExceptionBase> implements EntityDao<T, U> {
+
+
+    protected final static AtomicLong autoIncrement = new AtomicLong(1);
+
+    protected final Map<UUID, Map<Long, T>> entities = new HashMap<UUID, Map<Long, T>>();
+
+
+    @Override
+    public void create(final T entity, final InternalCallContext context) throws U {
+        entities.put(entity.getId(), ImmutableMap.<Long, T>of(autoIncrement.incrementAndGet(), entity));
+    }
+
+    @Override
+    public Long getRecordId(final UUID id, final InternalTenantContext context) {
+        return entities.get(id).keySet().iterator().next();
+    }
+
+    @Override
+    public T getByRecordId(final Long recordId, final InternalTenantContext context) {
+        for (Map<Long, T> cur : entities.values()) {
+            if (cur.keySet().iterator().next().equals(recordId)) {
+                cur.values().iterator().next();
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public T getById(final UUID id, final InternalTenantContext context) {
+        return entities.get(id).values().iterator().next();
+    }
+
+    @Override
+    public List<T> get(final InternalTenantContext context) {
+        List<T> result = new ArrayList<T>();
+        for (Map<Long, T> cur : entities.values()) {
+             result.add(cur.values().iterator().next());
+        }
+        return result;
+    }
+
+    @Override
+    public void test(final InternalTenantContext context) {
+    }
+}
diff --git a/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueue.java b/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueue.java
index 2a59bfd..ec917f2 100644
--- a/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueue.java
+++ b/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueue.java
@@ -29,6 +29,8 @@ import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import com.ning.billing.util.config.NotificationConfig;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueHandler;
 import com.ning.billing.util.queue.PersistentQueueEntryLifecycle.PersistentQueueEntryLifecycleState;
 
@@ -65,7 +67,7 @@ public class MockNotificationQueue extends NotificationQueueBase implements Noti
     }
 
     @Override
-    public void recordFutureNotificationFromTransaction(final Transmogrifier transactionalDao, final DateTime futureNotificationTime,
+    public void recordFutureNotificationFromTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> transactionalDao, final DateTime futureNotificationTime,
                                                         final UUID accountId, final NotificationKey notificationKey, final InternalCallContext context) throws IOException {
         recordFutureNotification(futureNotificationTime, accountId, notificationKey, context);
     }
diff --git a/util/src/test/java/com/ning/billing/util/notificationq/TestNotificationQueue.java b/util/src/test/java/com/ning/billing/util/notificationq/TestNotificationQueue.java
index ee19da5..fd7da4e 100644
--- a/util/src/test/java/com/ning/billing/util/notificationq/TestNotificationQueue.java
+++ b/util/src/test/java/com/ning/billing/util/notificationq/TestNotificationQueue.java
@@ -44,6 +44,10 @@ import com.ning.billing.util.UtilTestSuiteWithEmbeddedDB;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
+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.io.IOUtils;
 import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueHandler;
 import com.ning.billing.util.notificationq.dao.NotificationSqlDao;
@@ -64,8 +68,11 @@ import static org.testng.Assert.assertEquals;
 public class TestNotificationQueue extends UtilTestSuiteWithEmbeddedDB {
     private final Logger log = LoggerFactory.getLogger(TestNotificationQueue.class);
 
+
     private static final UUID accountId = UUID.randomUUID();
 
+    private EntitySqlDaoTransactionalJdbiWrapper entitySqlDaoTransactionalJdbiWrapper;
+
     @Inject
     private IDBI dbi;
 
@@ -103,6 +110,7 @@ public class TestNotificationQueue extends UtilTestSuiteWithEmbeddedDB {
         final String testDdl = IOUtils.toString(NotificationSqlDao.class.getResourceAsStream("/com/ning/billing/util/ddl_test.sql"));
         helper.initDb(testDdl);
         dao = dbi.onDemand(DummySqlTest.class);
+        entitySqlDaoTransactionalJdbiWrapper = new EntitySqlDaoTransactionalJdbiWrapper(dbi);
     }
 
     @BeforeTest(groups = "slow")
@@ -159,13 +167,12 @@ public class TestNotificationQueue extends UtilTestSuiteWithEmbeddedDB {
         expectedNotifications.put(notificationKey, Boolean.FALSE);
 
         // Insert dummy to be processed in 2 sec'
-        dao.inTransaction(new Transaction<Void, DummySqlTest>() {
+        entitySqlDaoTransactionalJdbiWrapper.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
-            public Void inTransaction(final DummySqlTest transactional,
-                                      final TransactionStatus status) throws Exception {
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
 
-                transactional.insertDummy(obj);
-                queue.recordFutureNotificationFromTransaction(transactional, readyTime, accountId, notificationKey, internalCallContext);
+                entitySqlDaoWrapperFactory.transmogrify(DummySqlTest.class).insertDummy(obj);
+                queue.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory, readyTime, accountId, notificationKey, internalCallContext);
                 log.info("Posted key: " + notificationKey);
 
                 return null;
@@ -219,18 +226,18 @@ public class TestNotificationQueue extends UtilTestSuiteWithEmbeddedDB {
             final NotificationKey notificationKey = new TestNotificationKey(key.toString());
             expectedNotifications.put(notificationKey, Boolean.FALSE);
 
-            dao.inTransaction(new Transaction<Void, DummySqlTest>() {
+            entitySqlDaoTransactionalJdbiWrapper.execute(new EntitySqlDaoTransactionWrapper<Void>() {
                 @Override
-                public Void inTransaction(final DummySqlTest transactional,
-                                          final TransactionStatus status) throws Exception {
+                public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
 
-                    transactional.insertDummy(obj);
-                    queue.recordFutureNotificationFromTransaction(transactional, now.plus((currentIteration + 1) * nextReadyTimeIncrementMs),
+                    entitySqlDaoWrapperFactory.transmogrify(DummySqlTest.class).insertDummy(obj);
+                    queue.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory, now.plus((currentIteration + 1) * nextReadyTimeIncrementMs),
                                                                   accountId, notificationKey, internalCallContext);
                     return null;
                 }
             });
 
+
             // Move time in the future after the notification effectiveDate
             if (i == 0) {
                 ((ClockMock) clock).setDeltaFromReality(nextReadyTimeIncrementMs);
@@ -330,17 +337,15 @@ public class TestNotificationQueue extends UtilTestSuiteWithEmbeddedDB {
         expectedNotificationsFred.put(notificationKeyBarney, Boolean.FALSE);
 
         // Insert dummy to be processed in 2 sec'
-        dao.inTransaction(new Transaction<Void, DummySqlTest>() {
+        entitySqlDaoTransactionalJdbiWrapper.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
-            public Void inTransaction(final DummySqlTest transactional,
-                                      final TransactionStatus status) throws Exception {
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                entitySqlDaoWrapperFactory.transmogrify(DummySqlTest.class).insertDummy(obj);
 
-                transactional.insertDummy(obj);
-                queueFred.recordFutureNotificationFromTransaction(transactional, readyTime, accountId, notificationKeyFred, internalCallContext);
+                queueFred.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory, readyTime, accountId, notificationKeyFred, internalCallContext);
                 log.info("posted key: " + notificationKeyFred.toString());
-                queueBarney.recordFutureNotificationFromTransaction(transactional, readyTime, accountId, notificationKeyBarney, internalCallContext);
+                queueBarney.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory, readyTime, accountId, notificationKeyBarney, internalCallContext);
                 log.info("posted key: " + notificationKeyBarney.toString());
-
                 return null;
             }
         });
@@ -408,14 +413,12 @@ public class TestNotificationQueue extends UtilTestSuiteWithEmbeddedDB {
 
         // add 3 events
 
-        dao.inTransaction(new Transaction<Void, DummySqlTest>() {
+        entitySqlDaoTransactionalJdbiWrapper.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
-            public Void inTransaction(final DummySqlTest transactional,
-                                      final TransactionStatus status) throws Exception {
-
-                queue.recordFutureNotificationFromTransaction(transactional, start.plus(nextReadyTimeIncrementMs), accountId, notificationKey, internalCallContext);
-                queue.recordFutureNotificationFromTransaction(transactional, start.plus(2 * nextReadyTimeIncrementMs), accountId, notificationKey, internalCallContext);
-                queue.recordFutureNotificationFromTransaction(transactional, start.plus(3 * nextReadyTimeIncrementMs), accountId, notificationKey2, internalCallContext);
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                queue.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory, start.plus(nextReadyTimeIncrementMs), accountId, notificationKey, internalCallContext);
+                queue.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory, start.plus(2 * nextReadyTimeIncrementMs), accountId, notificationKey, internalCallContext);
+                queue.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory, start.plus(3 * nextReadyTimeIncrementMs), accountId, notificationKey2, internalCallContext);
                 return null;
             }
         });
diff --git a/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java b/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
index ec103f2..4b08d00 100644
--- a/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
+++ b/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
@@ -29,6 +29,7 @@ import org.joda.time.DateTime;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 
 import com.ning.billing.ObjectType;
+import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.tag.Tag;
@@ -37,59 +38,12 @@ public class MockTagDao implements TagDao {
 
     private final Map<UUID, List<Tag>> tagStore = new HashMap<UUID, List<Tag>>();
 
-
-    private Map<String, Tag> getMap(@Nullable final List<Tag> tags) {
-        final Map<String, Tag> map = new HashMap<String, Tag>();
-        if (tags != null) {
-            for (final Tag tag : tags) {
-                map.put(tag.getTagDefinitionId().toString(), tag);
-            }
-        }
-        return map;
-    }
-
     @Override
-    public void insertTag(final UUID objectId, final ObjectType objectType,
-                          final UUID tagDefinitionId, final InternalCallContext context) {
-        final Tag tag = new Tag() {
-            private final UUID id = UUID.randomUUID();
-
-            @Override
-            public UUID getTagDefinitionId() {
-                return tagDefinitionId;
-            }
-
-            @Override
-            public ObjectType getObjectType() {
-                return objectType;
-            }
-
-            @Override
-            public UUID getObjectId() {
-                return objectId;
-            }
-
-            @Override
-            public UUID getId() {
-                return id;
-            }
-
-            @Override
-            public DateTime getCreatedDate() {
-                throw new UnsupportedOperationException();
-            }
-
-            @Override
-            public DateTime getUpdatedDate() {
-                throw new UnsupportedOperationException();
-            }
-        };
-
-        if (tagStore.get(objectId) == null) {
-            tagStore.put(objectId, new ArrayList<Tag>());
+    public void create(final Tag tag, final InternalCallContext context) throws TagApiException {
+        if (tagStore.get(tag.getObjectId()) == null) {
+            tagStore.put(tag.getObjectId(), new ArrayList<Tag>());
         }
-
-        tagStore.get(objectId).add(tag);
+        tagStore.get(tag.getObjectId()).add(tag);
     }
 
     @Override
@@ -108,12 +62,12 @@ public class MockTagDao implements TagDao {
     }
 
     @Override
-    public Tag getTagById(final UUID tagId, final InternalTenantContext context) {
+    public Tag getById(final UUID tagId, final InternalTenantContext context) {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public Map<String, Tag> getTags(final UUID objectId, final ObjectType objectType, final InternalTenantContext internalTenantContext) {
+    public List<Tag> getTags(final UUID objectId, final ObjectType objectType, final InternalTenantContext internalTenantContext) {
         throw new UnsupportedOperationException();
     }
 }
diff --git a/util/src/test/java/com/ning/billing/util/tag/dao/TestAuditedTagDao.java b/util/src/test/java/com/ning/billing/util/tag/dao/TestAuditedTagDao.java
index 054b065..4556034 100644
--- a/util/src/test/java/com/ning/billing/util/tag/dao/TestAuditedTagDao.java
+++ b/util/src/test/java/com/ning/billing/util/tag/dao/TestAuditedTagDao.java
@@ -38,6 +38,8 @@ import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.events.BusInternalEvent;
 import com.ning.billing.util.events.TagInternalEvent;
 import com.ning.billing.util.tag.ControlTagType;
+import com.ning.billing.util.tag.DefaultControlTag;
+import com.ning.billing.util.tag.DescriptiveTag;
 import com.ning.billing.util.tag.MockTagStoreModuleSql;
 import com.ning.billing.util.tag.Tag;
 import com.ning.billing.util.tag.TagDefinition;
@@ -165,12 +167,14 @@ public class TestAuditedTagDao extends UtilTestSuiteWithEmbeddedDB {
         Assert.assertEquals(createdTagDefinition.getDescription(), description);
 
         // Make sure we can create a tag
-        tagDao.insertTag(objectId, objectType, createdTagDefinition.getId(), internalCallContext);
+        final Tag tag = createdTagDefinition.isControlTag() ? new DefaultControlTag(ControlTagType.getTypeFromId(createdTagDefinition.getId()), ObjectType.ACCOUNT, objectId, internalCallContext.getCreatedDate()) :
+                        new DescriptiveTag(createdTagDefinition.getId(), ObjectType.ACCOUNT, objectId, internalCallContext.getCreatedDate());
+        tagDao.create(tag, internalCallContext);
 
         // Make sure we can retrieve it via the DAO
-        final Map<String, Tag> foundTags = tagDao.getTags(objectId, objectType, internalCallContext);
-        Assert.assertEquals(foundTags.keySet().size(), 1);
-        Assert.assertEquals(foundTags.values().iterator().next().getTagDefinitionId(), createdTagDefinition.getId());
+        final List<Tag> foundTags = tagDao.getTags(objectId, objectType, internalCallContext);
+        Assert.assertEquals(foundTags.size(), 1);
+        Assert.assertEquals(foundTags.get(0).getTagDefinitionId(), createdTagDefinition.getId());
 
         // Verify we caught an event on the bus -  we got 2 total (one for the tag definition, one for the tag)
         Assert.assertEquals(eventsListener.getEvents().size(), 2);
@@ -188,7 +192,7 @@ public class TestAuditedTagDao extends UtilTestSuiteWithEmbeddedDB {
         tagDao.deleteTag(objectId, objectType, createdTagDefinition.getId(), internalCallContext);
 
         // Make sure the tag is deleted
-        Assert.assertEquals(tagDao.getTags(objectId, objectType, internalCallContext).keySet().size(), 0);
+        Assert.assertEquals(tagDao.getTags(objectId, objectType, internalCallContext).size(), 0);
 
         // Verify we caught an event on the bus
         Assert.assertEquals(eventsListener.getEvents().size(), 3);
diff --git a/util/src/test/java/com/ning/billing/util/tag/TestTagStore.java b/util/src/test/java/com/ning/billing/util/tag/TestTagStore.java
index ddf9bd8..2e3c386 100644
--- a/util/src/test/java/com/ning/billing/util/tag/TestTagStore.java
+++ b/util/src/test/java/com/ning/billing/util/tag/TestTagStore.java
@@ -93,11 +93,11 @@ public class TestTagStore extends UtilTestSuiteWithEmbeddedDB {
     @Test(groups = "slow")
     public void testTagCreationAndRetrieval() throws TagApiException {
         final UUID accountId = UUID.randomUUID();
-        final Tag tag = new DescriptiveTag(testTagDefinition.getId(), UUID.randomUUID(), ObjectType.ACCOUNT, accountId, clock.getUTCNow());
+        final Tag tag = new DescriptiveTag(testTagDefinition.getId(), ObjectType.ACCOUNT, accountId, clock.getUTCNow());
 
-        tagDao.insertTag(accountId, ObjectType.ACCOUNT, tag.getTagDefinitionId(), internalCallContext);
+        tagDao.create(tag, internalCallContext);
 
-        final Tag savedTag = tagDao.getTagById(tag.getId(), internalCallContext);
+        final Tag savedTag = tagDao.getById(tag.getId(), internalCallContext);
         assertEquals(savedTag.getTagDefinitionId(), tag.getTagDefinitionId());
         assertEquals(savedTag.getId(), tag.getId());
     }
@@ -109,9 +109,9 @@ public class TestTagStore extends UtilTestSuiteWithEmbeddedDB {
 
         final ControlTag tag = new DefaultControlTag(ControlTagType.AUTO_INVOICING_OFF, ObjectType.ACCOUNT, accountId, clock.getUTCNow());
 
-        tagDao.insertTag(accountId, ObjectType.ACCOUNT, tag.getTagDefinitionId(), internalCallContext);
+        tagDao.create(tag, internalCallContext);
 
-        final Tag savedTag = tagDao.getTagById(tag.getId(), internalCallContext);
+        final Tag savedTag = tagDao.getById(tag.getId(), internalCallContext);
         assertEquals(savedTag.getTagDefinitionId(), tag.getTagDefinitionId());
         assertEquals(savedTag.getId(), tag.getId());
     }
@@ -145,8 +145,9 @@ public class TestTagStore extends UtilTestSuiteWithEmbeddedDB {
         assertNotNull(tagDefinition);
 
         final UUID objectId = UUID.randomUUID();
-
-        tagDao.insertTag(tagDefinition.getId(), ObjectType.ACCOUNT, objectId, internalCallContext);
+        final Tag tag = tagDefinition.isControlTag() ? new DefaultControlTag(ControlTagType.getTypeFromId(tagDefinition.getId()), ObjectType.ACCOUNT, objectId, internalCallContext.getCreatedDate()) :
+                        new DescriptiveTag(tagDefinition.getId(), ObjectType.ACCOUNT, objectId, internalCallContext.getCreatedDate());
+        tagDao.create(tag, internalCallContext);
 
         tagDefinitionDao.deleteById(tagDefinition.getId(), internalCallContext);
     }
@@ -165,8 +166,11 @@ public class TestTagStore extends UtilTestSuiteWithEmbeddedDB {
 
         final UUID objectId = UUID.randomUUID();
 
-        tagDao.insertTag(tagDefinition.getId(), ObjectType.ACCOUNT, objectId, internalCallContext);
 
+        final Tag tag = tagDefinition.isControlTag() ? new DefaultControlTag(ControlTagType.getTypeFromId(tagDefinition.getId()), ObjectType.ACCOUNT, objectId, internalCallContext.getCreatedDate()) :
+                        new DescriptiveTag(tagDefinition.getId(), ObjectType.ACCOUNT, objectId, internalCallContext.getCreatedDate());
+
+        tagDao.create(tag, internalCallContext);
         tagDao.deleteTag(objectId, ObjectType.ACCOUNT, tagDefinition.getId(), internalCallContext);
 
         try {