killbill-aplcache

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

11/9/2012 7:11:26 PM

Changes

Details

diff --git a/account/src/main/java/com/ning/billing/account/api/DefaultAccountEmail.java b/account/src/main/java/com/ning/billing/account/api/DefaultAccountEmail.java
index e6bdb24..b738270 100644
--- a/account/src/main/java/com/ning/billing/account/api/DefaultAccountEmail.java
+++ b/account/src/main/java/com/ning/billing/account/api/DefaultAccountEmail.java
@@ -18,10 +18,10 @@ package com.ning.billing.account.api;
 
 import java.util.UUID;
 
-import com.ning.billing.util.entity.Entity;
 import com.ning.billing.util.entity.EntityBase;
 
 public class DefaultAccountEmail extends EntityBase implements AccountEmail {
+
     private final UUID accountId;
     private final String email;
 
diff --git a/account/src/main/java/com/ning/billing/account/api/DefaultAccountService.java b/account/src/main/java/com/ning/billing/account/api/DefaultAccountService.java
index 9b0cfc1..d630f70 100644
--- a/account/src/main/java/com/ning/billing/account/api/DefaultAccountService.java
+++ b/account/src/main/java/com/ning/billing/account/api/DefaultAccountService.java
@@ -16,7 +16,6 @@
 
 package com.ning.billing.account.api;
 
-
 public class DefaultAccountService implements AccountService {
 
     private static final String ACCOUNT_SERVICE_NAME = "account-service";
@@ -26,7 +25,7 @@ public class DefaultAccountService implements AccountService {
         return ACCOUNT_SERVICE_NAME;
     }
 
-//    @LifecycleHandlerType(LifecycleLevel.INIT_SERVICE)
-//    public void initialize() {
-//    }
+    //    @LifecycleHandlerType(LifecycleLevel.INIT_SERVICE)
+    //    public void initialize() {
+    //    }
 }
diff --git a/account/src/main/java/com/ning/billing/account/api/DefaultBillCycleDay.java b/account/src/main/java/com/ning/billing/account/api/DefaultBillCycleDay.java
index e5587be..faa33e5 100644
--- a/account/src/main/java/com/ning/billing/account/api/DefaultBillCycleDay.java
+++ b/account/src/main/java/com/ning/billing/account/api/DefaultBillCycleDay.java
@@ -61,13 +61,21 @@ public class DefaultBillCycleDay implements BillCycleDay {
 
     @Override
     public boolean equals(final Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
 
         final DefaultBillCycleDay that = (DefaultBillCycleDay) o;
 
-        if (dayOfMonthLocal != that.dayOfMonthLocal) return false;
-        if (dayOfMonthUTC != that.dayOfMonthUTC) return false;
+        if (dayOfMonthLocal != that.dayOfMonthLocal) {
+            return false;
+        }
+        if (dayOfMonthUTC != that.dayOfMonthUTC) {
+            return false;
+        }
 
         return true;
     }
diff --git a/account/src/main/java/com/ning/billing/account/api/DefaultChangedField.java b/account/src/main/java/com/ning/billing/account/api/DefaultChangedField.java
index 0b762fd..09a971e 100644
--- a/account/src/main/java/com/ning/billing/account/api/DefaultChangedField.java
+++ b/account/src/main/java/com/ning/billing/account/api/DefaultChangedField.java
@@ -47,7 +47,6 @@ public class DefaultChangedField implements ChangedField {
         this(fieldName, oldValue, newValue, new DateTime());
     }
 
-
     @Override
     public String getFieldName() {
         return fieldName;
@@ -73,13 +72,13 @@ public class DefaultChangedField implements ChangedField {
         final int prime = 31;
         int result = 1;
         result = prime * result
-                + ((changeDate == null) ? 0 : changeDate.hashCode());
+                 + ((changeDate == null) ? 0 : changeDate.hashCode());
         result = prime * result
-                + ((fieldName == null) ? 0 : fieldName.hashCode());
+                 + ((fieldName == null) ? 0 : fieldName.hashCode());
         result = prime * result
-                + ((newValue == null) ? 0 : newValue.hashCode());
+                 + ((newValue == null) ? 0 : newValue.hashCode());
         result = prime * result
-                + ((oldValue == null) ? 0 : oldValue.hashCode());
+                 + ((oldValue == null) ? 0 : oldValue.hashCode());
         return result;
     }
 
diff --git a/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java b/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java
index 7a84076..d75cc23 100644
--- a/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java
+++ b/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java
@@ -31,7 +31,6 @@ import com.ning.billing.account.dao.AccountDao;
 import com.ning.billing.account.dao.AccountEmailDao;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.entity.EntityPersistenceException;
 import com.ning.billing.util.svcapi.account.AccountInternalApi;
 
 public class DefaultAccountInternalApi implements AccountInternalApi {
@@ -46,8 +45,7 @@ public class DefaultAccountInternalApi implements AccountInternalApi {
     }
 
     @Override
-    public Account getAccountById(final UUID accountId, final InternalTenantContext context)
-            throws AccountApiException {
+    public Account getAccountById(final UUID accountId, final InternalTenantContext context) throws AccountApiException {
         final Account account = accountDao.getById(accountId, context);
         if (account == null) {
             throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID, accountId);
@@ -56,21 +54,16 @@ public class DefaultAccountInternalApi implements AccountInternalApi {
     }
 
     @Override
-    public Account getAccountByRecordId(final Long recordId,
-                                        final InternalTenantContext context) throws AccountApiException {
+    public Account getAccountByRecordId(final Long recordId, final InternalTenantContext context) throws AccountApiException {
         return accountDao.getByRecordId(recordId, context);
     }
 
     @Override
     public void updateAccount(final String externalKey, final AccountData accountData,
                               final InternalCallContext context) throws AccountApiException {
-        try {
-            final Account account = getAccountByKey(externalKey, context);
-            final Account updatedAccount = new DefaultAccount(account.getId(), accountData);
-            accountDao.update(updatedAccount, context);
-        } catch (EntityPersistenceException e) {
-            throw new AccountApiException(e, ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID);
-        }
+        final Account account = getAccountByKey(externalKey, context);
+        final Account updatedAccount = new DefaultAccount(account.getId(), accountData);
+        accountDao.update(updatedAccount, context);
     }
 
     @Override
@@ -80,8 +73,7 @@ public class DefaultAccountInternalApi implements AccountInternalApi {
     }
 
     @Override
-    public Account getAccountByKey(final String key, final InternalTenantContext context)
-            throws AccountApiException {
+    public Account getAccountByKey(final String key, final InternalTenantContext context) throws AccountApiException {
         final Account account = accountDao.getAccountByKey(key, context);
         if (account == null) {
             throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_KEY, key);
@@ -90,18 +82,13 @@ public class DefaultAccountInternalApi implements AccountInternalApi {
     }
 
     @Override
-    public void removePaymentMethod(final UUID accountId, final InternalCallContext context)
-            throws AccountApiException {
+    public void removePaymentMethod(final UUID accountId, final InternalCallContext context) throws AccountApiException {
         updatePaymentMethod(accountId, null, context);
     }
 
     @Override
     public void updatePaymentMethod(final UUID accountId, final UUID paymentMethodId,
                                     final InternalCallContext context) throws AccountApiException {
-        try {
-            accountDao.updatePaymentMethod(accountId, paymentMethodId, context);
-        } catch (final EntityPersistenceException e) {
-            throw new AccountApiException(e, e.getCode(), e.getMessage());
-        }
+        accountDao.updatePaymentMethod(accountId, paymentMethodId, context);
     }
 }
diff --git a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountChangeEvent.java b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountChangeEvent.java
index 39cc93d..2d8ed85 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountChangeEvent.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountChangeEvent.java
@@ -21,9 +21,9 @@ import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.account.api.Account;
-import com.ning.billing.util.events.ChangedField;
 import com.ning.billing.account.api.DefaultChangedField;
 import com.ning.billing.util.events.AccountChangeInternalEvent;
+import com.ning.billing.util.events.ChangedField;
 import com.ning.billing.util.events.DefaultBusInternalEvent;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
@@ -50,7 +50,7 @@ public class DefaultAccountChangeEvent extends DefaultBusInternalEvent implement
     }
 
     public DefaultAccountChangeEvent(final UUID id, final UUID userToken, final Account oldData, final Account newData,
-            final Long accountRecordId, final Long tenantRecordId) {
+                                     final Long accountRecordId, final Long tenantRecordId) {
         super(userToken, accountRecordId, tenantRecordId);
         this.accountId = id;
         this.userToken = userToken;
diff --git a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java
index ba2210f..6980388 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java
@@ -32,7 +32,7 @@ import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
 
-public class DefaultAccountCreationEvent  extends DefaultBusInternalEvent implements AccountCreationInternalEvent {
+public class DefaultAccountCreationEvent extends DefaultBusInternalEvent implements AccountCreationInternalEvent {
 
     private final UUID id;
     private final AccountData data;
@@ -70,7 +70,6 @@ public class DefaultAccountCreationEvent  extends DefaultBusInternalEvent implem
         return data;
     }
 
-
     @Override
     public int hashCode() {
         final int prime = 31;
@@ -109,8 +108,8 @@ public class DefaultAccountCreationEvent  extends DefaultBusInternalEvent implem
         return true;
     }
 
-
     public static class DefaultAccountData implements AccountData {
+
         private final String externalKey;
         private final String name;
         private final Integer firstNameLength;
@@ -303,41 +302,41 @@ public class DefaultAccountCreationEvent  extends DefaultBusInternalEvent implem
             final int prime = 31;
             int result = 1;
             result = prime * result
-                    + ((address1 == null) ? 0 : address1.hashCode());
+                     + ((address1 == null) ? 0 : address1.hashCode());
             result = prime * result
-                    + ((address2 == null) ? 0 : address2.hashCode());
+                     + ((address2 == null) ? 0 : address2.hashCode());
             result = prime * result
-                    + ((billCycleDay == null) ? 0 : billCycleDay.hashCode());
+                     + ((billCycleDay == null) ? 0 : billCycleDay.hashCode());
             result = prime * result + ((city == null) ? 0 : city.hashCode());
             result = prime * result
-                    + ((companyName == null) ? 0 : companyName.hashCode());
+                     + ((companyName == null) ? 0 : companyName.hashCode());
             result = prime * result
-                    + ((country == null) ? 0 : country.hashCode());
+                     + ((country == null) ? 0 : country.hashCode());
             result = prime * result
-                    + ((currency == null) ? 0 : currency.hashCode());
+                     + ((currency == null) ? 0 : currency.hashCode());
             result = prime * result + ((email == null) ? 0 : email.hashCode());
             result = prime * result
-                    + ((externalKey == null) ? 0 : externalKey.hashCode());
+                     + ((externalKey == null) ? 0 : externalKey.hashCode());
             result = prime
-                    * result
-                    + ((firstNameLength == null) ? 0 : firstNameLength
+                     * result
+                     + ((firstNameLength == null) ? 0 : firstNameLength
                     .hashCode());
             result = prime * result
-                    + ((locale == null) ? 0 : locale.hashCode());
+                     + ((locale == null) ? 0 : locale.hashCode());
             result = prime * result + ((name == null) ? 0 : name.hashCode());
             result = prime
-                    * result
-                    + ((paymentMethodId == null) ? 0 : paymentMethodId
+                     * result
+                     + ((paymentMethodId == null) ? 0 : paymentMethodId
                     .hashCode());
             result = prime * result + ((phone == null) ? 0 : phone.hashCode());
             result = prime * result
-                    + ((postalCode == null) ? 0 : postalCode.hashCode());
+                     + ((postalCode == null) ? 0 : postalCode.hashCode());
             result = prime
-                    * result
-                    + ((stateOrProvince == null) ? 0 : stateOrProvince
+                     * result
+                     + ((stateOrProvince == null) ? 0 : stateOrProvince
                     .hashCode());
             result = prime * result
-                    + ((timeZone == null) ? 0 : timeZone.hashCode());
+                     + ((timeZone == null) ? 0 : timeZone.hashCode());
             return result;
         }
 
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 9869b97..5986385 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
@@ -36,7 +36,6 @@ import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallContextFactory;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.TenantContext;
-import com.ning.billing.util.entity.EntityPersistenceException;
 
 import com.google.inject.Inject;
 
@@ -58,14 +57,14 @@ public class DefaultAccountUserApi implements AccountUserApi {
 
     @Override
     public Account createAccount(final AccountData data, final CallContext context) throws AccountApiException {
-        final Account account = new DefaultAccount(data);
-
-        try {
-            accountDao.create(account, internalCallContextFactory.createInternalCallContext(account.getId(), context));
-        } catch (AccountApiException e) {
-            throw new AccountApiException(e, ErrorCode.ACCOUNT_CREATION_FAILED);
+        // Not transactional, but there is a db constraint on that column
+        if (getIdFromKey(data.getExternalKey(), context) != null) {
+            throw new AccountApiException(ErrorCode.ACCOUNT_ALREADY_EXISTS, data.getExternalKey());
         }
 
+        final Account account = new DefaultAccount(data);
+        accountDao.create(account, internalCallContextFactory.createInternalCallContext(account.getId(), context));
+
         return account;
     }
 
@@ -75,6 +74,7 @@ public class DefaultAccountUserApi implements AccountUserApi {
         if (account == null) {
             throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_KEY, key);
         }
+
         return account;
     }
 
@@ -84,6 +84,7 @@ public class DefaultAccountUserApi implements AccountUserApi {
         if (account == null) {
             throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID, id);
         }
+
         return account;
     }
 
@@ -99,25 +100,13 @@ public class DefaultAccountUserApi implements AccountUserApi {
 
     @Override
     public void updateAccount(final Account account, final CallContext context) throws AccountApiException {
-        try {
-            accountDao.update(account, internalCallContextFactory.createInternalCallContext(account.getId(), context));
-        } catch (EntityPersistenceException e) {
-            throw new AccountApiException(e, ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID, account.getId());
-        }
-
+        accountDao.update(account, internalCallContextFactory.createInternalCallContext(account.getId(), context));
     }
 
     @Override
-    public void updateAccount(final UUID accountId, final AccountData accountData, final CallContext context)
-            throws AccountApiException {
-        try {
-            final Account account = new DefaultAccount(accountId, accountData);
-            accountDao.update(account, internalCallContextFactory.createInternalCallContext(account.getId(), context));
-        } catch (EntityPersistenceException e) {
-            throw new AccountApiException(e, ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID);
-        } catch (RuntimeException e /* EntityPersistenceException */) {
-            throw new AccountApiException(e, ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID, accountId);
-        }
+    public void updateAccount(final UUID accountId, final AccountData accountData, final CallContext context) throws AccountApiException {
+        final Account account = new DefaultAccount(accountId, accountData);
+        updateAccount(account, context);
     }
 
     @Override
@@ -130,20 +119,19 @@ public class DefaultAccountUserApi implements AccountUserApi {
     }
 
     @Override
-    public Account migrateAccount(final MigrationAccountData data, final CallContext context)
-            throws AccountApiException {
+    public Account migrateAccount(final MigrationAccountData data, final CallContext context) throws AccountApiException {
+        // Create a special (migration) context
         final DateTime createdDate = data.getCreatedDate() == null ? context.getCreatedDate() : data.getCreatedDate();
         final DateTime updatedDate = data.getUpdatedDate() == null ? context.getUpdatedDate() : data.getUpdatedDate();
         final CallContext migrationContext = callContextFactory.toMigrationCallContext(context, createdDate, updatedDate);
+
+        // Create the account
         final Account account = new DefaultAccount(data);
+        createAccount(account, migrationContext);
 
-        try {
-            accountDao.create(account, internalCallContextFactory.createInternalCallContext(account.getId(), migrationContext));
-            for (final String cur : data.getAdditionalContactEmails()) {
-                addEmail(account.getId(), new DefaultAccountEmail(account.getId(), cur), migrationContext);
-            }
-        } catch (AccountApiException e) {
-            throw new AccountApiException(e, ErrorCode.ACCOUNT_CREATION_FAILED);
+        // Add associated contact emails
+        for (final String cur : data.getAdditionalContactEmails()) {
+            addEmail(account.getId(), new DefaultAccountEmail(account.getId(), cur), migrationContext);
         }
 
         return account;
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountBinder.java b/account/src/main/java/com/ning/billing/account/dao/AccountBinder.java
index 3a413f0..9f0e744 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountBinder.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountBinder.java
@@ -35,7 +35,9 @@ import com.ning.billing.catalog.api.Currency;
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.PARAMETER})
 public @interface AccountBinder {
+
     public static class AccountBinderFactory implements BinderFactory {
+
         @Override
         public Binder<AccountBinder, Account> build(final Annotation annotation) {
             return new Binder<AccountBinder, Account>() {
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 4c2002e..3935b19 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
@@ -22,9 +22,7 @@ import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-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, AccountApiException> {
 
@@ -39,7 +37,7 @@ public interface AccountDao extends EntityDao<Account, AccountApiException> {
      * @param accountId       the id of the account
      * @param paymentMethodId the is of the current default paymentMethod
      */
-    public void updatePaymentMethod(UUID accountId, UUID paymentMethodId, InternalCallContext context) throws EntityPersistenceException;
+    public void updatePaymentMethod(UUID accountId, UUID paymentMethodId, InternalCallContext context) throws AccountApiException;
 
-    public void update(Account account, InternalCallContext context) throws EntityPersistenceException;
+    public void update(Account account, InternalCallContext context) throws AccountApiException;
 }
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountEmailBinder.java b/account/src/main/java/com/ning/billing/account/dao/AccountEmailBinder.java
index bf2cb77..7d60b3b 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountEmailBinder.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountEmailBinder.java
@@ -33,7 +33,9 @@ import com.ning.billing.account.api.AccountEmail;
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.PARAMETER})
 public @interface AccountEmailBinder {
+
     public static class AccountEmailBinderFactory implements BinderFactory {
+
         @Override
         public Binder<AccountEmailBinder, AccountEmail> build(final Annotation annotation) {
             return new Binder<AccountEmailBinder, AccountEmail>() {
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountEmailHistoryBinder.java b/account/src/main/java/com/ning/billing/account/dao/AccountEmailHistoryBinder.java
index 866063c..ef79ae4 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountEmailHistoryBinder.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountEmailHistoryBinder.java
@@ -34,7 +34,9 @@ import com.ning.billing.util.dao.EntityHistory;
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.PARAMETER})
 public @interface AccountEmailHistoryBinder {
+
     public static class AccountEmailHistoryBinderFactory implements BinderFactory {
+
         @Override
         public Binder<AccountEmailHistoryBinder, EntityHistory<AccountEmail>> build(final Annotation annotation) {
             return new Binder<AccountEmailHistoryBinder, EntityHistory<AccountEmail>>() {
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountEmailMapper.java b/account/src/main/java/com/ning/billing/account/dao/AccountEmailMapper.java
index 9bfcbee..e668bba 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountEmailMapper.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountEmailMapper.java
@@ -28,6 +28,7 @@ import com.ning.billing.account.api.DefaultAccountEmail;
 import com.ning.billing.util.dao.MapperBase;
 
 public class AccountEmailMapper extends MapperBase implements ResultSetMapper<AccountEmail> {
+
     @Override
     public AccountEmail map(final int index, final ResultSet result, final StatementContext context) throws SQLException {
         final UUID id = UUID.fromString(result.getString("id"));
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountHistoryBinder.java b/account/src/main/java/com/ning/billing/account/dao/AccountHistoryBinder.java
index 7c70303..ba8eec0 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountHistoryBinder.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountHistoryBinder.java
@@ -36,7 +36,9 @@ import com.ning.billing.util.dao.EntityHistory;
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.PARAMETER})
 public @interface AccountHistoryBinder {
+
     public static class AccountHistoryBinderFactory implements BinderFactory {
+
         @Override
         public Binder<AccountHistoryBinder, EntityHistory<Account>> build(final Annotation annotation) {
             return new Binder<AccountHistoryBinder, EntityHistory<Account>>() {
diff --git a/account/src/main/java/com/ning/billing/account/glue/DefaultAccountModule.java b/account/src/main/java/com/ning/billing/account/glue/DefaultAccountModule.java
index b419725..bbbce7c 100644
--- a/account/src/main/java/com/ning/billing/account/glue/DefaultAccountModule.java
+++ b/account/src/main/java/com/ning/billing/account/glue/DefaultAccountModule.java
@@ -23,8 +23,8 @@ import com.ning.billing.account.api.svcs.DefaultAccountInternalApi;
 import com.ning.billing.account.api.user.DefaultAccountUserApi;
 import com.ning.billing.account.dao.AccountDao;
 import com.ning.billing.account.dao.AccountEmailDao;
-import com.ning.billing.account.dao.AuditedAccountDao;
-import com.ning.billing.account.dao.AuditedAccountEmailDao;
+import com.ning.billing.account.dao.DefaultAccountDao;
+import com.ning.billing.account.dao.DefaultAccountEmailDao;
 import com.ning.billing.glue.AccountModule;
 import com.ning.billing.util.glue.RealImplementation;
 import com.ning.billing.util.svcapi.account.AccountInternalApi;
@@ -32,12 +32,13 @@ import com.ning.billing.util.svcapi.account.AccountInternalApi;
 import com.google.inject.AbstractModule;
 
 public class DefaultAccountModule extends AbstractModule implements AccountModule {
+
     private void installConfig() {
     }
 
     protected void installAccountDao() {
-        bind(AccountEmailDao.class).to(AuditedAccountEmailDao.class).asEagerSingleton();
-        bind(AccountDao.class).to(AuditedAccountDao.class).asEagerSingleton();
+        bind(AccountEmailDao.class).to(DefaultAccountEmailDao.class).asEagerSingleton();
+        bind(AccountDao.class).to(DefaultAccountDao.class).asEagerSingleton();
     }
 
     @Override
@@ -49,6 +50,7 @@ public class DefaultAccountModule extends AbstractModule implements AccountModul
     public void installInternalApi() {
         bind(AccountInternalApi.class).to(DefaultAccountInternalApi.class).asEagerSingleton();
     }
+
     private void installAccountService() {
         bind(AccountService.class).to(DefaultAccountService.class).asEagerSingleton();
     }
diff --git a/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg b/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
index a3d6075..e50cc57 100644
--- a/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
+++ b/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
@@ -1,5 +1,7 @@
 group AccountDaoSql: EntitySqlDao;
 
+tableName() ::= "accounts";
+historyTableName() ::= "account_history";
 
 tableFields(prefix) ::= <<
   <prefix>external_key
@@ -54,9 +56,9 @@ tableValues() ::= <<
 , :updatedBy
  >>
 
-historyTableName() ::= "account_history"
-
-accountRecordIdField(prefix) ::= ""
+/** The accounts table doesn't have an account_record_id column (it's the record_id) **/
+accountRecordIdFieldWithComma(prefix) ::= ""
+accountRecordIdValueWithComma(prefix) ::= ""
 
 update() ::= <<
     UPDATE accounts
diff --git a/account/src/test/java/com/ning/billing/account/api/user/TestDefaultAccountUserApi.java b/account/src/test/java/com/ning/billing/account/api/user/TestDefaultAccountUserApi.java
index 51111aa..9dd5f33 100644
--- a/account/src/test/java/com/ning/billing/account/api/user/TestDefaultAccountUserApi.java
+++ b/account/src/test/java/com/ning/billing/account/api/user/TestDefaultAccountUserApi.java
@@ -27,6 +27,7 @@ import org.testng.annotations.Test;
 import com.ning.billing.account.AccountTestSuite;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountData;
+import com.ning.billing.account.api.AccountEmail;
 import com.ning.billing.account.api.BillCycleDay;
 import com.ning.billing.account.api.DefaultAccount;
 import com.ning.billing.account.api.DefaultAccountEmail;
@@ -36,11 +37,11 @@ import com.ning.billing.account.dao.AccountEmailDao;
 import com.ning.billing.account.dao.MockAccountDao;
 import com.ning.billing.account.dao.MockAccountEmailDao;
 import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.util.svcsapi.bus.InternalBus;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallContextFactory;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.svcsapi.bus.InternalBus;
 
 public class TestDefaultAccountUserApi extends AccountTestSuite {
 
@@ -118,17 +119,19 @@ public class TestDefaultAccountUserApi extends AccountTestSuite {
         Assert.assertEquals(accountEmailDao.getByAccountId(accountId, tenantContext).size(), 0);
 
         // Add the first email
-        final String email1 = UUID.randomUUID().toString();
-        accountUserApi.addEmail(accountId, new DefaultAccountEmail(accountId, email1), callContext);
+        final String emailAddress1 = UUID.randomUUID().toString();
+        final AccountEmail email1 = new DefaultAccountEmail(accountId, emailAddress1);
+        accountUserApi.addEmail(accountId, email1, callContext);
         Assert.assertEquals(accountEmailDao.getByAccountId(accountId, tenantContext).size(), 1);
 
         // Add a second one
-        final String email2 = UUID.randomUUID().toString();
-        accountUserApi.addEmail(accountId, new DefaultAccountEmail(accountId, email2), callContext);
+        final String emailAddress2 = UUID.randomUUID().toString();
+        final AccountEmail email2 = new DefaultAccountEmail(accountId, emailAddress2);
+        accountUserApi.addEmail(accountId, email2, callContext);
         Assert.assertEquals(accountEmailDao.getByAccountId(accountId, tenantContext).size(), 2);
 
         // Remove the first second one
-        accountUserApi.removeEmail(accountId, new DefaultAccountEmail(accountId, email1), callContext);
+        accountUserApi.removeEmail(accountId, email1, callContext);
         Assert.assertEquals(accountEmailDao.getByAccountId(accountId, tenantContext).size(), 1);
     }
 }
diff --git a/account/src/test/java/com/ning/billing/account/api/user/TestEventJson.java b/account/src/test/java/com/ning/billing/account/api/user/TestEventJson.java
index 8dd87bb..f19d940 100644
--- a/account/src/test/java/com/ning/billing/account/api/user/TestEventJson.java
+++ b/account/src/test/java/com/ning/billing/account/api/user/TestEventJson.java
@@ -24,14 +24,15 @@ import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import com.ning.billing.account.AccountTestSuite;
-import com.ning.billing.util.events.ChangedField;
 import com.ning.billing.account.api.DefaultBillCycleDay;
 import com.ning.billing.account.api.DefaultChangedField;
 import com.ning.billing.account.api.user.DefaultAccountCreationEvent.DefaultAccountData;
 import com.ning.billing.util.events.AccountChangeInternalEvent;
+import com.ning.billing.util.events.ChangedField;
 import com.ning.billing.util.jackson.ObjectMapper;
 
 public class TestEventJson extends AccountTestSuite {
+
     private final ObjectMapper mapper = new ObjectMapper();
 
     @Test(groups = "fast")
@@ -52,7 +53,7 @@ public class TestEventJson extends AccountTestSuite {
     public void testAccountCreationEvent() throws Exception {
         final DefaultAccountData data = new DefaultAccountData("dsfdsf", "bobo", 3, "bobo@yahoo.com", new DefaultBillCycleDay(12), "USD", UUID.randomUUID(),
                                                                "UTC", "US", "21 avenue", "", "Gling", "San Franciso", "CA", "94110", "USA", "4126789887", false, false);
-        final DefaultAccountCreationEvent e = new DefaultAccountCreationEvent(data, UUID.randomUUID(), UUID.randomUUID(),  1L, 45L);
+        final DefaultAccountCreationEvent e = new DefaultAccountCreationEvent(data, UUID.randomUUID(), UUID.randomUUID(), 1L, 45L);
         final String json = mapper.writeValueAsString(e);
 
         final DefaultAccountCreationEvent obj = mapper.readValue(json, DefaultAccountCreationEvent.class);
diff --git a/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java b/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
index f28c777..1f8a9cb 100644
--- a/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
+++ b/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
@@ -22,13 +22,12 @@ import org.skife.jdbi.v2.IDBI;
 import org.testng.annotations.BeforeClass;
 
 import com.ning.billing.account.AccountTestSuiteWithEmbeddedDB;
+import com.ning.billing.util.bus.DefaultBusService;
+import com.ning.billing.util.bus.InMemoryInternalBus;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.clock.ClockMock;
-import com.ning.billing.util.clock.DefaultClock;
-import com.ning.billing.util.svcsapi.bus.InternalBus;
 import com.ning.billing.util.svcsapi.bus.BusService;
-import com.ning.billing.util.bus.DefaultBusService;
-import com.ning.billing.util.bus.InMemoryInternalBus;
+import com.ning.billing.util.svcsapi.bus.InternalBus;
 import com.ning.billing.util.tag.api.user.TagEventBuilder;
 
 import static org.testng.Assert.fail;
@@ -51,11 +50,11 @@ public abstract class AccountDaoTestBase extends AccountTestSuiteWithEmbeddedDB 
             final BusService busService = new DefaultBusService(bus);
             ((DefaultBusService) busService).startBus();
 
-            accountDao = new AuditedAccountDao(dbi, bus, new InternalCallContextFactory(dbi, new ClockMock()));
+            accountDao = new DefaultAccountDao(dbi, bus, new InternalCallContextFactory(dbi, new ClockMock()));
             // Health check test to make sure MySQL is setup properly
             accountDao.test(internalCallContext);
 
-            accountEmailDao = new AuditedAccountEmailDao(dbi);
+            accountEmailDao = new DefaultAccountEmailDao(dbi);
             // Health check test to make sure MySQL is setup properly
             accountEmailDao.test(internalCallContext);
         } catch (Throwable t) {
diff --git a/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
index fddfbc1..adab356 100644
--- a/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
@@ -16,28 +16,28 @@
 
 package com.ning.billing.account.dao;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Map;
 import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
 
+import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.account.api.DefaultAccount;
+import com.ning.billing.account.api.DefaultMutableAccountData;
 import com.ning.billing.account.api.user.DefaultAccountChangeEvent;
 import com.ning.billing.account.api.user.DefaultAccountCreationEvent;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
-import com.ning.billing.util.entity.EntityPersistenceException;
+import com.ning.billing.util.entity.dao.MockEntityDaoBase;
 import com.ning.billing.util.events.AccountChangeInternalEvent;
 import com.ning.billing.util.svcsapi.bus.InternalBus;
 import com.ning.billing.util.svcsapi.bus.InternalBus.EventBusException;
 
 import com.google.inject.Inject;
 
-public class MockAccountDao implements AccountDao {
+public class MockAccountDao extends MockEntityDaoBase<Account, AccountApiException> implements AccountDao {
 
     private final InternalBus eventBus;
-    private final Map<UUID, Account> accounts = new ConcurrentHashMap<UUID, Account>();
 
     @Inject
     public MockAccountDao(final InternalBus eventBus) {
@@ -45,42 +45,41 @@ public class MockAccountDao implements AccountDao {
     }
 
     @Override
-    public Long getRecordId(final UUID id, final InternalTenantContext context) {
-        return 1L;
-    }
-
-    @Override
-    public void create(final Account account, final InternalCallContext context) {
-        accounts.put(account.getId(), account);
+    public void create(final Account account, final InternalCallContext context) throws AccountApiException {
+        super.create(account, context);
 
         try {
-            eventBus.post(new DefaultAccountCreationEvent(account, null, 1L, 1L), context);
+            eventBus.post(new DefaultAccountCreationEvent(account, null, getRecordId(account.getId(), context), context.getTenantRecordId()), context);
         } catch (final EventBusException ex) {
             throw new RuntimeException(ex);
         }
     }
 
     @Override
-    public Account getById(final UUID id, final InternalTenantContext context) {
-        return accounts.get(id);
-    }
-
-    @Override
-    public List<Account> get(final InternalTenantContext context) {
-        return new ArrayList<Account>(accounts.values());
-    }
+    public void update(final Account account, final InternalCallContext context) {
+        final Account currentAccount = getById(account.getId(), context);
+        super.update(account, context);
 
-    @Override
-    public void test(final InternalTenantContext context) {
+        final AccountChangeInternalEvent changeEvent = new DefaultAccountChangeEvent(account.getId(), null, currentAccount, account,
+                                                                                     getRecordId(account.getId(), context), context.getTenantRecordId());
+        if (changeEvent.hasChanges()) {
+            try {
+                eventBus.post(changeEvent, context);
+            } catch (final EventBusException ex) {
+                throw new RuntimeException(ex);
+            }
+        }
     }
 
     @Override
     public Account getAccountByKey(final String externalKey, final InternalTenantContext context) {
-        for (final Account account : accounts.values()) {
-            if (externalKey.equals(account.getExternalKey())) {
+        for (final Map<Long, Account> accountRow : entities.values()) {
+            final Account account = accountRow.values().iterator().next();
+            if (account.getExternalKey().equals(externalKey)) {
                 return account;
             }
         }
+
         return null;
     }
 
@@ -91,25 +90,15 @@ public class MockAccountDao implements AccountDao {
     }
 
     @Override
-    public void update(final Account account, final InternalCallContext context) {
-        final Account currentAccount = accounts.put(account.getId(), account);
-
-        final AccountChangeInternalEvent changeEvent = new DefaultAccountChangeEvent(account.getId(), null, currentAccount, account, 1L, 1L);
-        if (changeEvent.hasChanges()) {
-            try {
-                eventBus.post(changeEvent, context);
-            } catch (final EventBusException ex) {
-                throw new RuntimeException(ex);
-            }
+    public void updatePaymentMethod(final UUID accountId, final UUID paymentMethodId, final InternalCallContext context) throws AccountApiException {
+        final Account currentAccount = getById(accountId, context);
+        if (currentAccount == null) {
+            throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID, accountId);
         }
-    }
 
-    @Override
-    public void updatePaymentMethod(final UUID accountId, final UUID paymentMethodId, final InternalCallContext context) throws EntityPersistenceException {
-    }
+        final DefaultMutableAccountData updatedAccount = new DefaultMutableAccountData(currentAccount);
+        updatedAccount.setPaymentMethodId(paymentMethodId);
 
-    @Override
-    public Account getByRecordId(final Long recordId, final InternalTenantContext context) {
-        return null;
+        update(new DefaultAccount(updatedAccount), 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 aa4c401..f408592 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
@@ -16,69 +16,28 @@
 
 package com.ning.billing.account.dao;
 
-import java.util.HashSet;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-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;
-import com.ning.billing.util.entity.EntityPersistenceException;
+import com.ning.billing.util.entity.dao.MockEntityDaoBase;
 
-import com.google.common.collect.ImmutableList;
-
-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 AccountApiException {
-        Set<AccountEmail> theSet = emails.get(entity.getAccountId());
-        theSet.add(entity);
-    }
-
-    @Override
-    public Long getRecordId(final UUID id, final InternalTenantContext context) {
-        return 1L;
-    }
-
-    @Override
-    public AccountEmail getById(final UUID id, final InternalTenantContext context) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public List<AccountEmail> get(final InternalTenantContext context) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void delete(final AccountEmail email, final InternalCallContext context) {
-        Set<AccountEmail> theSet = emails.get(email.getAccountId());
-        theSet.remove(email);
-    }
+public class MockAccountEmailDao extends MockEntityDaoBase<AccountEmail, AccountApiException> implements AccountEmailDao {
 
     @Override
     public List<AccountEmail> getByAccountId(final UUID accountId, final InternalTenantContext context) {
-        final Set<AccountEmail> accountEmails = emails.get(accountId);
-        if (accountEmails == null) {
-            return ImmutableList.<AccountEmail>of();
-        } else {
-            return ImmutableList.<AccountEmail>copyOf(accountEmails.iterator());
+        final List<AccountEmail> accountEmails = new ArrayList<AccountEmail>();
+        for (final Map<Long, AccountEmail> accountEmail : entities.values()) {
+            final AccountEmail email = accountEmail.values().iterator().next();
+            if (email.getAccountId().equals(accountId)) {
+                accountEmails.add(email);
+            }
         }
-    }
 
-    @Override
-    public void test(final InternalTenantContext context) {
-    }
-
-    @Override
-    public AccountEmail getByRecordId(final Long recordId,
-            final InternalTenantContext context) {
-        return null;
+        return accountEmails;
     }
 }
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 05168f9..e69f69a 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
@@ -144,7 +144,7 @@ public class TestAccountDao extends AccountDaoTestBase {
     }
 
     @Test(groups = "slow")
-    public void testCustomFields() throws  CustomFieldApiException {
+    public void testCustomFields() throws CustomFieldApiException {
         final String fieldName = "testField1";
         final String fieldValue = "testField1_value";
 
@@ -170,7 +170,6 @@ public class TestAccountDao extends AccountDaoTestBase {
 
         final TagDao tagDao = new AuditedTagDao(dbi, tagEventBuilder, bus, new DefaultClock());
 
-
         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());
diff --git a/account/src/test/java/com/ning/billing/account/glue/AccountModuleWithMocks.java b/account/src/test/java/com/ning/billing/account/glue/AccountModuleWithMocks.java
index 5db6b80..226f505 100644
--- a/account/src/test/java/com/ning/billing/account/glue/AccountModuleWithMocks.java
+++ b/account/src/test/java/com/ning/billing/account/glue/AccountModuleWithMocks.java
@@ -25,6 +25,7 @@ import com.ning.billing.mock.glue.MockClockModule;
 import com.ning.billing.util.glue.CallContextModule;
 
 public class AccountModuleWithMocks extends DefaultAccountModule {
+
     @Override
     protected void installAccountDao() {
         bind(MockAccountDao.class).asEagerSingleton();
diff --git a/analytics/src/test/java/com/ning/billing/analytics/TestBusinessTagRecorder.java b/analytics/src/test/java/com/ning/billing/analytics/TestBusinessTagRecorder.java
index ccd29e0..4d9a939 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/TestBusinessTagRecorder.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/TestBusinessTagRecorder.java
@@ -32,8 +32,8 @@ import com.ning.billing.account.api.svcs.DefaultAccountInternalApi;
 import com.ning.billing.account.api.user.DefaultAccountUserApi;
 import com.ning.billing.account.dao.AccountDao;
 import com.ning.billing.account.dao.AccountEmailDao;
-import com.ning.billing.account.dao.AuditedAccountDao;
-import com.ning.billing.account.dao.AuditedAccountEmailDao;
+import com.ning.billing.account.dao.DefaultAccountDao;
+import com.ning.billing.account.dao.DefaultAccountEmailDao;
 import com.ning.billing.analytics.dao.BusinessAccountTagSqlDao;
 import com.ning.billing.analytics.dao.BusinessInvoicePaymentTagSqlDao;
 import com.ning.billing.analytics.dao.BusinessInvoiceTagSqlDao;
@@ -59,7 +59,6 @@ import com.ning.billing.util.bus.InMemoryInternalBus;
 import com.ning.billing.util.callcontext.DefaultCallContextFactory;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.clock.ClockMock;
-import com.ning.billing.util.clock.DefaultClock;
 import com.ning.billing.util.notificationq.DefaultNotificationQueueService;
 import com.ning.billing.util.svcapi.account.AccountInternalApi;
 import com.ning.billing.util.svcapi.entitlement.EntitlementInternalApi;
@@ -85,8 +84,8 @@ public class TestBusinessTagRecorder extends AnalyticsTestSuiteWithEmbeddedDB {
         final BusinessInvoicePaymentTagSqlDao invoicePaymentTagSqlDao = dbi.onDemand(BusinessInvoicePaymentTagSqlDao.class);
         subscriptionTransitionTagSqlDao = dbi.onDemand(BusinessSubscriptionTransitionTagSqlDao.class);
         eventBus = new InMemoryInternalBus();
-        final AccountDao accountDao = new AuditedAccountDao(dbi, eventBus, new InternalCallContextFactory(dbi, new ClockMock()));
-        final AccountEmailDao accountEmailDao = new AuditedAccountEmailDao(dbi);
+        final AccountDao accountDao = new DefaultAccountDao(dbi, eventBus, new InternalCallContextFactory(dbi, new ClockMock()));
+        final AccountEmailDao accountEmailDao = new DefaultAccountEmailDao(dbi);
         callContextFactory = new DefaultCallContextFactory(clock);
         final InternalCallContextFactory internalCallContextFactory = new InternalCallContextFactory(dbi, clock);
         accountApi = new DefaultAccountInternalApi(accountDao, accountEmailDao);
diff --git a/api/src/main/java/com/ning/billing/util/api/AuditUserApi.java b/api/src/main/java/com/ning/billing/util/api/AuditUserApi.java
index d6a7a4f..ee9cc2a 100644
--- a/api/src/main/java/com/ning/billing/util/api/AuditUserApi.java
+++ b/api/src/main/java/com/ning/billing/util/api/AuditUserApi.java
@@ -21,6 +21,7 @@ import java.util.UUID;
 
 import com.ning.billing.ObjectType;
 import com.ning.billing.entitlement.api.timeline.BundleTimeline;
+import com.ning.billing.entitlement.api.timeline.EntitlementRepairException;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.payment.api.Payment;
@@ -36,6 +37,16 @@ import com.ning.billing.util.callcontext.TenantContext;
 public interface AuditUserApi {
 
     /**
+     * Fetch all audit logs for a bundle.
+     *
+     * @param bundleId   the bundle id to lookup
+     * @param auditLevel audit level (verbosity)
+     * @param context    the tenant context
+     * @return all audit logs for these refunds
+     */
+    public AuditLogsForBundles getAuditLogsForBundle(UUID bundleId, AuditLevel auditLevel, TenantContext context) throws EntitlementRepairException;
+
+    /**
      * Fetch all audit logs for bundles.
      *
      * @param bundles    the bundles to lookup
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/util/AuditChecker.java b/beatrix/src/test/java/com/ning/billing/beatrix/util/AuditChecker.java
new file mode 100644
index 0000000..11a6a2d
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/util/AuditChecker.java
@@ -0,0 +1,130 @@
+/*
+ * 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.beatrix.util;
+
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+
+import com.ning.billing.entitlement.api.timeline.EntitlementRepairException;
+import com.ning.billing.util.api.AuditLevel;
+import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.audit.AuditLogsForBundles;
+import com.ning.billing.util.audit.ChangeType;
+import com.ning.billing.util.callcontext.CallContext;
+
+import com.google.inject.Inject;
+
+public class AuditChecker {
+
+    private static final Logger log = LoggerFactory.getLogger(AuditChecker.class);
+
+    private final AuditUserApi auditUserApi;
+
+    @Inject
+    public AuditChecker(final AuditUserApi auditUserApi) {
+        this.auditUserApi = auditUserApi;
+    }
+
+    // Pass the call context used to create the bundle
+    public void checkBundleCreated(final UUID bundleId, final CallContext context) {
+        final AuditLogsForBundles auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
+
+        Assert.assertEquals(auditLogsForBundles.getBundlesAuditLogs().keySet().size(), 1);
+        Assert.assertEquals(auditLogsForBundles.getBundlesAuditLogs().get(bundleId).size(), 1);
+        checkAuditLog(ChangeType.INSERT, context, auditLogsForBundles.getBundlesAuditLogs().get(bundleId).get(0));
+    }
+
+    // Pass the call context used to update the bundle
+    public void checkBundleUpdated(final UUID bundleId, final CallContext context) {
+        final AuditLogsForBundles auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
+
+        Assert.assertEquals(auditLogsForBundles.getBundlesAuditLogs().keySet().size(), 1);
+        Assert.assertEquals(auditLogsForBundles.getBundlesAuditLogs().get(bundleId).size(), 2);
+        checkAuditLog(ChangeType.INSERT, auditLogsForBundles.getBundlesAuditLogs().get(bundleId).get(0));
+        checkAuditLog(ChangeType.UPDATE, context, auditLogsForBundles.getBundlesAuditLogs().get(bundleId).get(1));
+    }
+
+    // Pass the call context used to create the subscription
+    public void checkSubscriptionCreated(final UUID bundleId, final UUID subscriptionId, final CallContext context) {
+        final AuditLogsForBundles auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
+
+        Assert.assertEquals(auditLogsForBundles.getSubscriptionsAuditLogs().keySet().size(), 1);
+        Assert.assertEquals(auditLogsForBundles.getSubscriptionsAuditLogs().get(subscriptionId).size(), 1);
+        checkAuditLog(ChangeType.INSERT, context, auditLogsForBundles.getSubscriptionsAuditLogs().get(subscriptionId).get(0));
+    }
+
+    // Pass the call context used to update the subscription
+    public void checkSubscriptionUpdated(final UUID bundleId, final UUID subscriptionId, final CallContext context) {
+        final AuditLogsForBundles auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
+
+        Assert.assertEquals(auditLogsForBundles.getSubscriptionsAuditLogs().keySet().size(), 1);
+        Assert.assertEquals(auditLogsForBundles.getSubscriptionsAuditLogs().get(subscriptionId).size(), 2);
+        checkAuditLog(ChangeType.INSERT, auditLogsForBundles.getSubscriptionsAuditLogs().get(subscriptionId).get(0));
+        checkAuditLog(ChangeType.UPDATE, context, auditLogsForBundles.getSubscriptionsAuditLogs().get(subscriptionId).get(1));
+    }
+
+    // Pass the call context used to create the subscription event
+    public void checkSubscriptionEventCreated(final UUID bundleId, final UUID subscriptionEventId, final CallContext context) {
+        final AuditLogsForBundles auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
+
+        Assert.assertEquals(auditLogsForBundles.getSubscriptionEventsAuditLogs().keySet().size(), 1);
+        Assert.assertEquals(auditLogsForBundles.getSubscriptionEventsAuditLogs().get(subscriptionEventId).size(), 1);
+        checkAuditLog(ChangeType.INSERT, context, auditLogsForBundles.getSubscriptionEventsAuditLogs().get(subscriptionEventId).get(0));
+    }
+
+    // Pass the call context used to update the subscription event
+    public void checkSubscriptionEventUpdated(final UUID bundleId, final UUID subscriptionEventId, final CallContext context) {
+        final AuditLogsForBundles auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
+
+        Assert.assertEquals(auditLogsForBundles.getSubscriptionEventsAuditLogs().keySet().size(), 1);
+        Assert.assertEquals(auditLogsForBundles.getSubscriptionEventsAuditLogs().get(subscriptionEventId).size(), 2);
+        checkAuditLog(ChangeType.INSERT, auditLogsForBundles.getSubscriptionEventsAuditLogs().get(subscriptionEventId).get(0));
+        checkAuditLog(ChangeType.UPDATE, context, auditLogsForBundles.getSubscriptionEventsAuditLogs().get(subscriptionEventId).get(1));
+    }
+
+    private AuditLogsForBundles getAuditLogsForBundle(final UUID bundleId, final CallContext context) {
+        try {
+            return auditUserApi.getAuditLogsForBundle(bundleId, AuditLevel.FULL, context);
+        } catch (EntitlementRepairException e) {
+            Assert.fail(e.toString());
+            return null;
+        }
+    }
+
+    private void checkAuditLog(final ChangeType insert, final AuditLog auditLog) {
+        checkAuditLog(insert, null, auditLog);
+    }
+
+    private void checkAuditLog(final ChangeType changeType, @Nullable final CallContext context, final AuditLog auditLog) {
+        Assert.assertEquals(auditLog.getChangeType(), changeType);
+
+        if (context != null) {
+            Assert.assertEquals(auditLog.getUserName(), context.getUserName());
+            Assert.assertEquals(auditLog.getReasonCode(), context.getReasonCode());
+            // TODO check 'Next Billing Date' and 'Transition' - add comment, maybe internal reason code and token
+            Assert.assertEquals(auditLog.getComment(), context.getComments());
+            Assert.assertEquals(auditLog.getUserToken(), context.getUserToken().toString());
+            Assert.assertEquals(auditLog.getCreatedDate(), context.getCreatedDate());
+        }
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/audit/api/DefaultAuditUserApi.java b/util/src/main/java/com/ning/billing/util/audit/api/DefaultAuditUserApi.java
index 0429e67..e95e1b2 100644
--- a/util/src/main/java/com/ning/billing/util/audit/api/DefaultAuditUserApi.java
+++ b/util/src/main/java/com/ning/billing/util/audit/api/DefaultAuditUserApi.java
@@ -25,6 +25,8 @@ import javax.inject.Inject;
 
 import com.ning.billing.ObjectType;
 import com.ning.billing.entitlement.api.timeline.BundleTimeline;
+import com.ning.billing.entitlement.api.timeline.EntitlementRepairException;
+import com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi;
 import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline;
 import com.ning.billing.entitlement.api.timeline.SubscriptionTimeline.ExistingEvent;
 import com.ning.billing.invoice.api.Invoice;
@@ -55,15 +57,22 @@ import com.google.common.collect.ImmutableList;
 public class DefaultAuditUserApi implements AuditUserApi {
 
     private final AuditDao auditDao;
+    private final EntitlementTimelineApi timelineApi;
     private final InternalCallContextFactory internalCallContextFactory;
 
     @Inject
-    public DefaultAuditUserApi(final AuditDao auditDao, final InternalCallContextFactory internalCallContextFactory) {
+    public DefaultAuditUserApi(final AuditDao auditDao, final EntitlementTimelineApi timelineApi, final InternalCallContextFactory internalCallContextFactory) {
         this.auditDao = auditDao;
+        this.timelineApi = timelineApi;
         this.internalCallContextFactory = internalCallContextFactory;
     }
 
     @Override
+    public AuditLogsForBundles getAuditLogsForBundle(final UUID bundleId, final AuditLevel auditLevel, final TenantContext context) throws EntitlementRepairException {
+        return getAuditLogsForBundles(ImmutableList.<BundleTimeline>of(timelineApi.getBundleTimeline(bundleId, context)), auditLevel, context);
+    }
+
+    @Override
     public AuditLogsForBundles getAuditLogsForBundles(final List<BundleTimeline> bundles, final AuditLevel auditLevel, final TenantContext context) {
         final Map<UUID, List<AuditLog>> bundlesAuditLogs = new HashMap<UUID, List<AuditLog>>();
         final Map<UUID, List<AuditLog>> subscriptionsAuditLogs = new HashMap<UUID, List<AuditLog>>();
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 03fdf7f..bd4ea2c 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
@@ -56,7 +56,7 @@ public class EntitySqlDaoWrapperFactory<InitialSqlDao extends EntitySqlDao> {
     private <NewSqlDao extends EntitySqlDao<NewEntity>, NewEntity extends Entity> NewSqlDao create(final Class<NewSqlDao> newSqlDaoClass, final NewSqlDao newSqlDao) {
         final ClassLoader classLoader = newSqlDao.getClass().getClassLoader();
         final Class[] interfacesToImplement = {newSqlDaoClass};
-        final EntitySqlDaoWrapperInvocationHandler<NewSqlDao, NewEntity> wrapperInvocationHandler = new EntitySqlDaoWrapperInvocationHandler<NewSqlDao, NewEntity>(newSqlDao);
+        final EntitySqlDaoWrapperInvocationHandler<NewSqlDao, NewEntity> wrapperInvocationHandler = new EntitySqlDaoWrapperInvocationHandler<NewSqlDao, NewEntity>(newSqlDaoClass, newSqlDao);
 
         final Object newSqlDaoObject = Proxy.newProxyInstance(classLoader, interfacesToImplement, wrapperInvocationHandler);
         return newSqlDaoClass.cast(newSqlDaoObject);
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
index d3cc502..fbf6a1c 100644
--- a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
@@ -19,15 +19,19 @@ package com.ning.billing.util.entity.dao;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.UUID;
 
+import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.exceptions.DBIException;
+import org.skife.jdbi.v2.exceptions.StatementException;
 import org.skife.jdbi.v2.sqlobject.Bind;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.InternalCallContext;
@@ -47,9 +51,13 @@ import com.google.common.collect.ImmutableList.Builder;
  */
 public class EntitySqlDaoWrapperInvocationHandler<T extends EntitySqlDao<U>, U extends Entity> implements InvocationHandler {
 
+    private final Logger logger = LoggerFactory.getLogger(EntitySqlDaoWrapperInvocationHandler.class);
+
+    private final Class<T> sqlDaoClass;
     private final T sqlDao;
 
-    public EntitySqlDaoWrapperInvocationHandler(final T sqlDao) {
+    public EntitySqlDaoWrapperInvocationHandler(final Class<T> sqlDaoClass, final T sqlDao) {
+        this.sqlDaoClass = sqlDaoClass;
         this.sqlDao = sqlDao;
     }
 
@@ -60,12 +68,23 @@ public class EntitySqlDaoWrapperInvocationHandler<T extends EntitySqlDao<U>, U e
         } catch (Throwable t) {
             if (t.getCause() != null && t.getCause().getCause() != null && DBIException.class.isAssignableFrom(t.getCause().getClass())) {
                 // Likely the JDBC exception or a Billing exception we have thrown in the transaction
-                errorDuringTransaction(t.getCause().getCause());
+                // If it's a JDBC error, try to extract the SQL statement
+                if (t.getCause() instanceof StatementException) {
+                    final StatementContext statementContext = ((StatementException) t.getCause()).getStatementContext();
+                    if (statementContext != null) {
+                        final PreparedStatement statement = statementContext.getStatement();
+                        // Note: we rely on the JDBC driver to have a sane toString() method...
+                        errorDuringTransaction(t.getCause().getCause(), method, statement.toString());
+                        // Never reached
+                        return null;
+                    }
+                }
+                errorDuringTransaction(t.getCause().getCause(), method);
             } else if (t.getCause() != null) {
                 // t is likely not interesting (java.lang.reflect.InvocationTargetException)
-                errorDuringTransaction(t.getCause());
+                errorDuringTransaction(t.getCause(), method);
             } else {
-                errorDuringTransaction(t);
+                errorDuringTransaction(t, method);
             }
         }
 
@@ -74,7 +93,22 @@ public class EntitySqlDaoWrapperInvocationHandler<T extends EntitySqlDao<U>, U e
     }
 
     // Nice method name to ease debugging while looking at log files
-    private void errorDuringTransaction(final Throwable t) throws Throwable {
+    private void errorDuringTransaction(final Throwable t, final Method method, final String extraErrorMessage) throws Throwable {
+        final StringBuilder errorMessageBuilder = new StringBuilder("Error during transaction for sql entity {} and method {}");
+        if (t instanceof SQLException) {
+            final SQLException sqlException = (SQLException) t;
+            errorMessageBuilder.append(" [SQL State: ")
+                               .append(sqlException.getSQLState())
+                               .append(", Vendor Error Code: ")
+                               .append(sqlException.getErrorCode())
+                               .append("]");
+        }
+        if (extraErrorMessage != null) {
+            // This is usually the SQL statement
+            errorMessageBuilder.append("\n").append(extraErrorMessage);
+        }
+        logger.warn(errorMessageBuilder.toString(), sqlDaoClass, method.getName());
+
         // This is to avoid throwing an exception wrapped in an UndeclaredThrowableException
         if (!(t instanceof RuntimeException)) {
             throw new RuntimeException(t);
@@ -83,6 +117,10 @@ public class EntitySqlDaoWrapperInvocationHandler<T extends EntitySqlDao<U>, U e
         }
     }
 
+    private void errorDuringTransaction(final Throwable t, final Method method) throws Throwable {
+        errorDuringTransaction(t, method, null);
+    }
+
     private Object invokeSafely(final Object proxy, final Method method, final Object[] args) throws Throwable {
         final Audited annotation = method.getAnnotation(Audited.class);
 
@@ -225,8 +263,6 @@ public class EntitySqlDaoWrapperInvocationHandler<T extends EntitySqlDao<U>, U e
         return sqlDao.getHistoryRecordId(entityRecordId, context);
     }
 
-
-
     private void insertAudits(final TableName tableName, final Long historyRecordId, final ChangeType changeType, final InternalCallContext context) {
         // STEPH can we trust context or should we use Clock?
         final TableName destinationTableName = Objects.firstNonNull(tableName.getHistoryTableName(), tableName);
diff --git a/util/src/test/java/com/ning/billing/util/audit/api/TestDefaultAuditUserApi.java b/util/src/test/java/com/ning/billing/util/audit/api/TestDefaultAuditUserApi.java
index d1fef2c..32912cf 100644
--- a/util/src/test/java/com/ning/billing/util/audit/api/TestDefaultAuditUserApi.java
+++ b/util/src/test/java/com/ning/billing/util/audit/api/TestDefaultAuditUserApi.java
@@ -67,7 +67,7 @@ public class TestDefaultAuditUserApi extends AuditLogsTestBase {
             }
         }
 
-        auditUserApi = new DefaultAuditUserApi(auditDao, Mockito.mock(InternalCallContextFactory.class));
+        auditUserApi = new DefaultAuditUserApi(auditDao, null, Mockito.mock(InternalCallContextFactory.class));
     }
 
     @Test(groups = "fast")
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 a40dbb4..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
@@ -30,16 +30,17 @@ import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
 import com.ning.billing.ObjectType;
+import com.ning.billing.mock.glue.MockEntitlementModule;
 import com.ning.billing.util.UtilTestSuiteWithEmbeddedDB;
 import com.ning.billing.util.api.AuditLevel;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.audit.AuditLog;
 import com.ning.billing.util.audit.ChangeType;
-import com.ning.billing.util.svcsapi.bus.InternalBus;
 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;
@@ -51,7 +52,7 @@ import com.ning.billing.util.tag.dao.TagDefinitionDao;
 
 import com.google.inject.Inject;
 
-@Guice(modules = {MockTagStoreModuleSql.class, AuditModule.class})
+@Guice(modules = {MockTagStoreModuleSql.class, AuditModule.class, MockEntitlementModule.class})
 public class TestDefaultAuditDao extends UtilTestSuiteWithEmbeddedDB {
 
     @Inject
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
index 53b8d91..d59dc79 100644
--- 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
@@ -26,19 +26,16 @@ 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 static final 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));
@@ -51,7 +48,7 @@ public class MockEntityDaoBase<T extends Entity, U extends BillingExceptionBase>
 
     @Override
     public T getByRecordId(final Long recordId, final InternalTenantContext context) {
-        for (Map<Long, T> cur : entities.values()) {
+        for (final Map<Long, T> cur : entities.values()) {
             if (cur.keySet().iterator().next().equals(recordId)) {
                 cur.values().iterator().next();
             }
@@ -66,13 +63,22 @@ public class MockEntityDaoBase<T extends Entity, U extends BillingExceptionBase>
 
     @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());
+        final List<T> result = new ArrayList<T>();
+        for (final Map<Long, T> cur : entities.values()) {
+            result.add(cur.values().iterator().next());
         }
         return result;
     }
 
+    public void update(final T entity, final InternalCallContext context) {
+        final Long entityRecordId = getRecordId(entity.getId(), context);
+        entities.get(entity.getId()).put(entityRecordId, entity);
+    }
+
+    public void delete(final T entity, final InternalCallContext context) {
+        entities.remove(entity.getId());
+    }
+
     @Override
     public void test(final InternalTenantContext context) {
     }