killbill-memoizeit

move MapperBase and BinderBase; introduce ChangeTypeBinder;

4/5/2012 8:54:33 PM

Changes

Details

diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
index fdc35d3..8c09af4 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
@@ -22,7 +22,7 @@ import java.util.UUID;
 
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallContextBinder;
-import com.ning.billing.util.entity.MapperBase;
+import com.ning.billing.util.dao.MapperBase;
 import com.ning.billing.util.entity.UpdatableEntityDao;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
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 97e4aab..2d06666 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
@@ -130,7 +130,7 @@ public class AuditedAccountDao implements AccountDao {
 
                     AuditSqlDao auditDao = accountSqlDao.become(AuditSqlDao.class);
                     auditDao.insertAuditFromTransaction("account_history", historyId.toString(),
-                                                         ChangeType.INSERT.toString(), context);
+                                                         ChangeType.INSERT, context);
 
                     saveTagsFromWithinTransaction(account, transactionalDao, context);
                     saveCustomFieldsFromWithinTransaction(account, transactionalDao, context);
@@ -175,7 +175,7 @@ public class AuditedAccountDao implements AccountDao {
 
                     AuditSqlDao auditDao = accountSqlDao.become(AuditSqlDao.class);
                     auditDao.insertAuditFromTransaction("account_history" ,historyId.toString(),
-                                                        ChangeType.INSERT.toString(), context);
+                                                        ChangeType.INSERT, context);
 
                     saveTagsFromWithinTransaction(account, accountSqlDao, context);
                     saveCustomFieldsFromWithinTransaction(account, accountSqlDao, context);
diff --git a/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java
index 633b6b5..7feb492 100644
--- a/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java
@@ -25,6 +25,7 @@ import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.util.entity.EntityPersistenceException;
+import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.testng.annotations.Test;
 
@@ -32,39 +33,37 @@ import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountData;
 import com.ning.billing.account.api.DefaultAccount;
-import com.ning.billing.account.api.user.AccountBuilder;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.util.tag.DefaultTagDefinition;
 import com.ning.billing.util.tag.Tag;
 import com.ning.billing.util.tag.TagDefinition;
 import com.ning.billing.util.tag.dao.TagDefinitionSqlDao;
 
-@Test(groups = {"account-dao"})
+@Test(groups = {"slow", "account-dao"})
 public class TestSimpleAccountDao extends AccountDaoTestBase {
-    private AccountBuilder createTestAccountBuilder() {
+    private Account createTestAccount(int billCycleDay) {
+        return createTestAccount(billCycleDay, "123-456-7890");
+    }
+
+    private Account createTestAccount(int billCycleDay, String phone) {
         String thisKey = "test" + UUID.randomUUID().toString();
         String lastName = UUID.randomUUID().toString();
         String thisEmail = "me@me.com" + " " + UUID.randomUUID();
         String firstName = "Bob";
         String name = firstName + " " + lastName;
-        String phone = "123-456-7890";
         String locale = "EN-US";
         DateTimeZone timeZone = DateTimeZone.forID("America/Los_Angeles");
-
         int firstNameLength = firstName.length();
-        return new AccountBuilder().externalKey(thisKey)
-                                   .name(name)
-                                   .phone(phone)
-                                   .firstNameLength(firstNameLength)
-                                   .email(thisEmail)
-                                   .currency(Currency.USD)
-                                   .locale(locale)
-                                   .timeZone(timeZone);
+
+        return new DefaultAccount(UUID.randomUUID(), thisKey, thisEmail, name, firstNameLength, Currency.USD,
+                billCycleDay, null, timeZone, locale,
+                null, null, null, null, null, null, null, // add null address fields
+                phone, "test", DateTime.now(), "test", DateTime.now());
     }
 
     @Test
     public void testBasic() throws EntityPersistenceException {
-        Account a = createTestAccountBuilder().build();
+        Account a = createTestAccount(5);
         accountDao.create(a, context);
         String key = a.getExternalKey();
 
@@ -84,7 +83,7 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
     // simple test to ensure long phone numbers can be stored
     @Test
     public void testLongPhoneNumber() throws EntityPersistenceException {
-        Account account = createTestAccountBuilder().phone("123456789012345678901234").build();
+        Account account = createTestAccount(1, "123456789012345678901234");
         accountDao.create(account, context);
 
         Account saved = accountDao.getAccountByKey(account.getExternalKey());
@@ -94,13 +93,13 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
     // simple test to ensure excessively long phone numbers cannot be stored
     @Test(expectedExceptions = {EntityPersistenceException.class})
     public void testOverlyLongPhoneNumber() throws EntityPersistenceException {
-        Account account = createTestAccountBuilder().phone("12345678901234567890123456").build();
+        Account account = createTestAccount(1, "12345678901234567890123456");
         accountDao.create(account, context);
     }
 
     @Test
     public void testGetById() throws EntityPersistenceException {
-        Account account = createTestAccountBuilder().build();
+        Account account = createTestAccount(1);
         UUID id = account.getId();
         String key = account.getExternalKey();
         String name = account.getName();
@@ -119,7 +118,7 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
 
     @Test
     public void testCustomFields() throws EntityPersistenceException {
-        Account account = createTestAccountBuilder().build();
+        Account account = createTestAccount(1);
         String fieldName = "testField1";
         String fieldValue = "testField1_value";
         account.setFieldValue(fieldName, fieldValue);
@@ -134,7 +133,7 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
 
     @Test
     public void testTags() throws EntityPersistenceException {
-        Account account = createTestAccountBuilder().build();
+        Account account = createTestAccount(1);
         TagDefinition definition = new DefaultTagDefinition("Test Tag", "For testing only");
         TagDefinitionSqlDao tagDescriptionDao = dbi.onDemand(TagDefinitionSqlDao.class);
         tagDescriptionDao.create(definition, context);
@@ -152,7 +151,7 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
 
     @Test
     public void testGetIdFromKey() throws EntityPersistenceException {
-        Account account = createTestAccountBuilder().build();
+        Account account = createTestAccount(1);
         accountDao.create(account, context);
 
         try {
@@ -171,7 +170,7 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
 
     @Test
     public void testUpdate() throws Exception {
-        final Account account = createTestAccountBuilder().build();
+        final Account account = createTestAccount(1);
         accountDao.create(account, context);
 
         AccountData accountData = new AccountData() {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
index 3480544..d9d9381 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
@@ -18,8 +18,8 @@ package com.ning.billing.entitlement.engine.dao;
 
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
-import com.ning.billing.util.entity.BinderBase;
-import com.ning.billing.util.entity.MapperBase;
+import com.ning.billing.util.dao.BinderBase;
+import com.ning.billing.util.dao.MapperBase;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.StatementContext;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
index 789b493..610a7f8 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
@@ -24,8 +24,8 @@ import com.ning.billing.entitlement.events.phase.PhaseEventBuilder;
 import com.ning.billing.entitlement.events.phase.PhaseEventData;
 import com.ning.billing.entitlement.events.user.*;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
-import com.ning.billing.util.entity.BinderBase;
-import com.ning.billing.util.entity.MapperBase;
+import com.ning.billing.util.dao.BinderBase;
+import com.ning.billing.util.dao.MapperBase;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.StatementContext;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
index 504610d..741ae83 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
@@ -20,10 +20,9 @@ import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
-import com.ning.billing.util.entity.BinderBase;
-import com.ning.billing.util.entity.MapperBase;
+import com.ning.billing.util.dao.BinderBase;
+import com.ning.billing.util.dao.MapperBase;
 import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
 import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
@@ -39,7 +38,6 @@ import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.sql.Timestamp;
 import java.util.Date;
 import java.util.List;
 import java.util.UUID;
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
index 13272c4..6aa3452 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
@@ -179,10 +179,10 @@ public class DefaultInvoiceDao implements InvoiceDao {
                     invoicePaymentSqlDao.batchCreateFromTransaction(invoicePayments, context);
 
                     AuditSqlDao auditSqlDao = invoiceDao.become(AuditSqlDao.class);
-                    auditSqlDao.insertAuditFromTransaction("invoices", invoice.getId().toString(), ChangeType.INSERT.toString(), context);
-                    auditSqlDao.insertAuditFromTransaction("recurring_invoice_items", getIdsFromInvoiceItems(recurringInvoiceItems), ChangeType.INSERT.toString(), context);
-                    auditSqlDao.insertAuditFromTransaction("fixed_invoice_items", getIdsFromInvoiceItems(fixedPriceInvoiceItems), ChangeType.INSERT.toString(), context);
-                    auditSqlDao.insertAuditFromTransaction("invoice_payments", getIdsFromInvoicePayments(invoicePayments), ChangeType.INSERT.toString(), context);
+                    auditSqlDao.insertAuditFromTransaction("invoices", invoice.getId().toString(), ChangeType.INSERT, context);
+                    auditSqlDao.insertAuditFromTransaction("recurring_invoice_items", getIdsFromInvoiceItems(recurringInvoiceItems), ChangeType.INSERT, context);
+                    auditSqlDao.insertAuditFromTransaction("fixed_invoice_items", getIdsFromInvoiceItems(fixedPriceInvoiceItems), ChangeType.INSERT, context);
+                    auditSqlDao.insertAuditFromTransaction("invoice_payments", getIdsFromInvoicePayments(invoicePayments), ChangeType.INSERT, context);
 
                 }
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
index eed9680..1a506c5 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
@@ -30,7 +30,7 @@ import java.util.UUID;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallContextBinder;
-import com.ning.billing.util.entity.MapperBase;
+import com.ning.billing.util.dao.MapperBase;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.StatementContext;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
index fc58dbc..60a4313 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
@@ -53,7 +53,6 @@ public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
     protected Clock clock;
     protected CallContext context;
     protected InvoiceGenerator generator;
-    private BusService busService;
 
     private final InvoiceConfig invoiceConfig = new InvoiceConfig() {
         @Override
@@ -72,12 +71,10 @@ public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
     protected void setup() throws IOException {
         try {
             module = new InvoiceModuleWithEmbeddedDb();
-            final String accountDdl = IOUtils.toString(DefaultInvoiceDao.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
             final String invoiceDdl = IOUtils.toString(DefaultInvoiceDao.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
             final String entitlementDdl = IOUtils.toString(DefaultInvoiceDao.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
 
             module.startDb();
-            module.initDb(accountDdl);
             module.initDb(invoiceDdl);
             module.initDb(entitlementDdl);
 
@@ -132,9 +129,6 @@ public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
 
     @AfterClass(alwaysRun = true)
     protected void tearDown() {
-    	if (busService != null) {
-            ((DefaultBusService) busService).stopBus();
-        }
         module.stopDb();
         assertTrue(true);
     }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java
index 38e95d6..bcdda9a 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java
@@ -62,7 +62,7 @@ public class AuditedPaymentDao implements PaymentDao {
                 transactional.insertPaymentAttemptHistory(historyRecordId.toString(), paymentAttempt, context);
                 AuditSqlDao auditSqlDao = transactional.become(AuditSqlDao.class);
                 auditSqlDao.insertAuditFromTransaction("payment_attempt", historyRecordId.toString(),
-                                                       ChangeType.INSERT.toString(), context);
+                                                       ChangeType.INSERT, context);
                 return savedPaymentAttempt;
             }
         });
@@ -79,7 +79,7 @@ public class AuditedPaymentDao implements PaymentDao {
                 transactional.insertPaymentAttemptHistory(historyRecordId.toString(), paymentAttempt, context);
                 AuditSqlDao auditSqlDao = transactional.become(AuditSqlDao.class);
                 auditSqlDao.insertAuditFromTransaction("payment_attempt", historyRecordId.toString(),
-                                                       ChangeType.INSERT.toString(), context);
+                                                       ChangeType.INSERT, context);
 
                 return paymentAttempt;
             }
@@ -96,7 +96,7 @@ public class AuditedPaymentDao implements PaymentDao {
                 transactional.insertPaymentInfoHistory(historyRecordId.toString(), info, context);
                 AuditSqlDao auditSqlDao = transactional.become(AuditSqlDao.class);
                 auditSqlDao.insertAuditFromTransaction("payment", historyRecordId.toString(),
-                                                       ChangeType.INSERT.toString(), context);
+                                                       ChangeType.INSERT, context);
 
                 return null;
             }
@@ -114,7 +114,7 @@ public class AuditedPaymentDao implements PaymentDao {
                 transactional.insertPaymentAttemptHistory(historyRecordId.toString(), paymentAttempt, context);
                 AuditSqlDao auditSqlDao = transactional.become(AuditSqlDao.class);
                 auditSqlDao.insertAuditFromTransaction("payment_attempt", historyRecordId.toString(),
-                                                       ChangeType.UPDATE.toString(), context);
+                                                       ChangeType.UPDATE, context);
 
                 return null;
             }
@@ -128,12 +128,12 @@ public class AuditedPaymentDao implements PaymentDao {
             @Override
             public Void inTransaction(PaymentSqlDao transactional, TransactionStatus status) throws Exception {
                 transactional.updatePaymentInfo(type, paymentId, cardType, cardCountry, context);
-                PaymentInfo paymentInfo = transactional.getPaymentInfo(paymentId.toString());
+                PaymentInfo paymentInfo = transactional.getPaymentInfo(paymentId);
                 UUID historyRecordId = UUID.randomUUID();
                 transactional.insertPaymentInfoHistory(historyRecordId.toString(), paymentInfo, context);
                 AuditSqlDao auditSqlDao = transactional.become(AuditSqlDao.class);
                 auditSqlDao.insertAuditFromTransaction("payments", historyRecordId.toString(),
-                                                       ChangeType.UPDATE.toString(), context);
+                                                       ChangeType.UPDATE, context);
 
                 return null;
             }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
index 245a971..96d7e0b 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
@@ -19,24 +19,20 @@ package com.ning.billing.payment.dao;
 import java.math.BigDecimal;
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.sql.Timestamp;
-import java.util.Date;
 import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallContextBinder;
-import com.ning.billing.util.entity.BinderBase;
-import com.ning.billing.util.entity.MapperBase;
+import com.ning.billing.util.dao.BinderBase;
+import com.ning.billing.util.dao.MapperBase;
 import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
 import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.Binder;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
-import org.skife.jdbi.v2.sqlobject.customizers.Mapper;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
 import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
 import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
diff --git a/util/src/main/java/com/ning/billing/util/audit/dao/AuditSqlDao.java b/util/src/main/java/com/ning/billing/util/audit/dao/AuditSqlDao.java
index f3f96f4..8c14942 100644
--- a/util/src/main/java/com/ning/billing/util/audit/dao/AuditSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/audit/dao/AuditSqlDao.java
@@ -16,8 +16,10 @@
 
 package com.ning.billing.util.audit.dao;
 
+import com.ning.billing.util.ChangeType;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallContextBinder;
+import com.ning.billing.util.dao.ChangeTypeBinder;
 import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.SqlBatch;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
@@ -30,12 +32,12 @@ public interface AuditSqlDao {
     @SqlUpdate
     public void insertAuditFromTransaction(@Bind("tableName") final String tableName,
                                            @Bind("recordId") final String recordId,
-                                           @Bind("changeType") String changeType,
+                                           @ChangeTypeBinder final ChangeType changeType,
                                            @CallContextBinder CallContext context);
 
     @SqlBatch(transactional = false)
     public void insertAuditFromTransaction(@Bind("tableName") final String tableName,
                                            @Bind("recordId") final List<String> recordIds,
-                                           @Bind("changeType") String changeType,
+                                           @ChangeTypeBinder final ChangeType changeType,
                                            @CallContextBinder CallContext context);
 }
diff --git a/util/src/main/java/com/ning/billing/util/customfield/CustomFieldMapper.java b/util/src/main/java/com/ning/billing/util/customfield/CustomFieldMapper.java
index 97d469d..0c2bc5b 100644
--- a/util/src/main/java/com/ning/billing/util/customfield/CustomFieldMapper.java
+++ b/util/src/main/java/com/ning/billing/util/customfield/CustomFieldMapper.java
@@ -16,7 +16,7 @@
 
 package com.ning.billing.util.customfield;
 
-import com.ning.billing.util.entity.MapperBase;
+import com.ning.billing.util.dao.MapperBase;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
diff --git a/util/src/main/java/com/ning/billing/util/dao/ChangeTypeBinder.java b/util/src/main/java/com/ning/billing/util/dao/ChangeTypeBinder.java
new file mode 100644
index 0000000..58ed27b
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/dao/ChangeTypeBinder.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2010-2011 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.dao;
+
+import com.ning.billing.util.ChangeType;
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BinderFactory;
+import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@BindingAnnotation(ChangeTypeBinder.ChangeTypeBinderFactory.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.PARAMETER})
+public @interface ChangeTypeBinder {
+    public static class ChangeTypeBinderFactory implements BinderFactory {
+        public Binder build(Annotation annotation) {
+            return new Binder<ChangeTypeBinder, ChangeType>() {
+                public void bind(SQLStatement q, ChangeTypeBinder bind, ChangeType changeType) {
+                    q.bind("changeType", changeType.toString());
+                }
+            };
+        }
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/dao/NotificationSqlDao.java b/util/src/main/java/com/ning/billing/util/notificationq/dao/NotificationSqlDao.java
index 9253e2d..3d0a8b1 100644
--- a/util/src/main/java/com/ning/billing/util/notificationq/dao/NotificationSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/notificationq/dao/NotificationSqlDao.java
@@ -18,15 +18,13 @@ package com.ning.billing.util.notificationq.dao;
 
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.sql.Timestamp;
 import java.util.Date;
 import java.util.List;
 import java.util.UUID;
 
-import com.ning.billing.util.entity.BinderBase;
-import com.ning.billing.util.entity.MapperBase;
+import com.ning.billing.util.dao.BinderBase;
+import com.ning.billing.util.dao.MapperBase;
 import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
 import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
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 6e361f9..374e0a3 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
@@ -19,6 +19,8 @@ package com.ning.billing.util.tag.dao;
 import com.google.inject.Inject;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.util.ChangeType;
+import com.ning.billing.util.audit.dao.AuditSqlDao;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.tag.Tag;
 import org.skife.jdbi.v2.IDBI;
@@ -26,48 +28,23 @@ import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 
+import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.UUID;
 
 public class AuditedTagDao implements TagDao {
     private final TagSqlDao tagSqlDao;
-    private final TagAuditSqlDao tagAuditSqlDao;
 
     @Inject
     public AuditedTagDao(final IDBI dbi) {
         this.tagSqlDao = dbi.onDemand(TagSqlDao.class);
-        this.tagAuditSqlDao = dbi.onDemand(TagAuditSqlDao.class);
     }
 
     @Override
     public void saveTags(final UUID objectId, final String objectType,
                          final List<Tag> tags, final CallContext context) {
-        // get list of existing tags
-        List<Tag> existingTags = tagSqlDao.load(objectId.toString(), objectType);
-
-        // sort into tags to update (tagsToUpdate), tags to add (tags), and tags to delete (existingTags)
-        Iterator<Tag> tagIterator = tags.iterator();
-        while (tagIterator.hasNext()) {
-            Tag tag = tagIterator.next();
-
-            Iterator<Tag> existingTagIterator = existingTags.iterator();
-            while (existingTagIterator.hasNext()) {
-                Tag existingTag = existingTagIterator.next();
-                if (tag.getTagDefinitionName().equals(existingTag.getTagDefinitionName())) {
-                    // if the tags match, remove from both lists
-                    // in the case of tag, this just means the tag remains associated
-                    tagIterator.remove();
-                    existingTagIterator.remove();
-                }
-            }
-        }
-
-        tagSqlDao.batchInsertFromTransaction(objectId.toString(), objectType, tags, context);
-        tagSqlDao.batchDeleteFromTransaction(objectId.toString(), objectType, existingTags, context);
-
-        tagAuditSqlDao.batchInsertFromTransaction(tags, context);
-        tagAuditSqlDao.batchDeleteFromTransaction(existingTags, context);
+        saveTagsFromTransaction(tagSqlDao, objectId, objectType, tags, context);
     }
 
     @Override
@@ -98,9 +75,22 @@ public class AuditedTagDao implements TagDao {
         tagSqlDao.batchInsertFromTransaction(objectId.toString(), objectType, tags, context);
         tagSqlDao.batchDeleteFromTransaction(objectId.toString(), objectType, existingTags, context);
 
-        TagAuditSqlDao auditDao = dao.become(TagAuditSqlDao.class);
-        auditDao.batchInsertFromTransaction(tags, context);
-        auditDao.batchDeleteFromTransaction(existingTags, context);
+        List<String> historyIdsForInsert = getIdList(tags.size());
+        tagSqlDao.batchInsertHistoryFromTransaction(objectId.toString(), objectType, historyIdsForInsert, tags, ChangeType.INSERT, context);
+        List<String> historyIdsForDelete = getIdList(existingTags.size());
+        tagSqlDao.batchInsertHistoryFromTransaction(objectId.toString(), objectType, historyIdsForDelete, existingTags, ChangeType.DELETE, context);
+
+        AuditSqlDao auditSqlDao = tagSqlDao.become(AuditSqlDao.class);
+        auditSqlDao.insertAuditFromTransaction("tag_history", historyIdsForInsert, ChangeType.INSERT, context);
+        auditSqlDao.insertAuditFromTransaction("tag_history", historyIdsForDelete, ChangeType.DELETE, context);
+    }
+
+    private List<String> getIdList(int size) {
+        List<String> results = new ArrayList<String>();
+        for (int i = 0; i < size; i++) {
+            results.add(UUID.randomUUID().toString());
+        }
+        return results;
     }
 
     @Override
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagAuditSqlDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagAuditSqlDao.java
index 02fb33d..7e6cbee 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagAuditSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagAuditSqlDao.java
@@ -31,14 +31,6 @@ import java.util.List;
 @ExternalizedSqlViaStringTemplate3
 @RegisterMapper(TagMapper.class)
 public interface TagAuditSqlDao extends Transactional<TagAuditSqlDao> {
-    @SqlBatch(transactional=false)
-    public void batchInsertFromTransaction(@TagBinder final List<Tag> tag,
-                                           @CallContextBinder final CallContext context);
-
-    @SqlBatch(transactional=false)
-    public void batchDeleteFromTransaction(@TagBinder final List<Tag> tag,
-                                           @CallContextBinder final CallContext context);
-
     @SqlUpdate
     public void addTagFromTransaction(@Bind("id") final String tagId,
                                       @CallContextBinder final CallContext context);
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java
index 74e6f04..211ae5a 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java
@@ -18,13 +18,9 @@ package com.ning.billing.util.tag.dao;
 
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.List;
 import java.util.UUID;
 
-import com.ning.billing.util.entity.MapperBase;
-import com.ning.billing.util.tag.ControlTag;
+import com.ning.billing.util.dao.MapperBase;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagSqlDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagSqlDao.java
index 79b68b0..a20e757 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagSqlDao.java
@@ -18,8 +18,10 @@ package com.ning.billing.util.tag.dao;
 
 import java.util.List;
 
+import com.ning.billing.util.ChangeType;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallContextBinder;
+import com.ning.billing.util.dao.ChangeTypeBinder;
 import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.SqlBatch;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
@@ -48,6 +50,14 @@ public interface TagSqlDao extends EntityCollectionDao<Tag>, Transactional<TagSq
                                            @TagBinder final List<Tag> entities,
                                            @CallContextBinder final CallContext context);
 
+    @SqlBatch(transactional = false)
+    public void batchInsertHistoryFromTransaction(@Bind("objectId") final String objectId,
+                                                  @Bind("objectType") final String objectType,
+                                                  @Bind("historyRecordId") final List<String> historyRecordIdList,
+                                                  @TagBinder final List<Tag> tags,
+                                                  @ChangeTypeBinder final ChangeType changeType,
+                                                  @CallContextBinder final CallContext context);
+
     @SqlUpdate
     public void addTagFromTransaction(@Bind("id") final String tagId,
                                       @Bind("tagDefinitionName") final String tagName,
diff --git a/util/src/main/resources/com/ning/billing/util/ddl.sql b/util/src/main/resources/com/ning/billing/util/ddl.sql
index 17442c1..77b66c9 100644
--- a/util/src/main/resources/com/ning/billing/util/ddl.sql
+++ b/util/src/main/resources/com/ning/billing/util/ddl.sql
@@ -68,7 +68,8 @@ CREATE UNIQUE INDEX tags_unique ON tags(tag_definition_name, object_id);
 
 DROP TABLE IF EXISTS tag_history;
 CREATE TABLE tag_history (
-  id char(36) NULL,
+  history_record_id char(36) NOT NULL,
+  id char(36) NOT NULL,
   tag_definition_name varchar(20) NOT NULL,
   object_id char(36) NOT NULL,
   object_type varchar(30) NOT NULL,
diff --git a/util/src/main/resources/com/ning/billing/util/tag/dao/TagAuditSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/tag/dao/TagAuditSqlDao.sql.stg
index c591fba..e3262a4 100644
--- a/util/src/main/resources/com/ning/billing/util/tag/dao/TagAuditSqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/tag/dao/TagAuditSqlDao.sql.stg
@@ -10,16 +10,6 @@ fields(prefix) ::= <<
     <prefix>comments
 >>
 
-batchInsertFromTransaction() ::= <<
-    INSERT INTO audit_log(<fields()>)
-    VALUES('tag', :id, 'INSERT', :createdDate, :userName, NULL, NULL);
->>
-
-batchDeleteFromTransaction() ::= <<
-    INSERT INTO audit_log(<fields()>)
-    VALUES('tag', :id, 'DELETE', :updatedDate, :userName, NULL, NULL);
->>
-
 addTagFromTransaction() ::= <<
     INSERT INTO audit_log(<fields()>)
     VALUES('tag', :id, 'INSERT', :createdDate, :userName, NULL, NULL);
diff --git a/util/src/main/resources/com/ning/billing/util/tag/dao/TagSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/tag/dao/TagSqlDao.sql.stg
index e950d75..8f98452 100644
--- a/util/src/main/resources/com/ning/billing/util/tag/dao/TagSqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/tag/dao/TagSqlDao.sql.stg
@@ -14,6 +14,11 @@ batchInsertFromTransaction() ::= <<
   VALUES (:id, :tagDefinitionName, :objectId, :objectType, :userName, :createdDate);
 >>
 
+batchInsertHistoryFromTransaction() ::= <<
+    INSERT INTO tag_history (history_record_id, id, tag_definition_name, object_id, object_type, change_type, updated_by, date)
+    VALUES (:historyRecordId, :id, :tagDefinitionName, :objectId, :objectType, :changeType, :userName, :updatedDate);
+>>
+
 batchDeleteFromTransaction() ::= <<
     DELETE FROM tags
     WHERE tag_definition_name = :tagDefinitionName
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 1e51e57..a8d353f 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
@@ -388,7 +388,7 @@ public class TestTagStore {
         assertEquals(savedTag.getId(), tag.getId());
 
         Handle handle = dbi.open();
-        String query = String.format("select * from audit_log where table_name = 'Tag' and record_id='%s'",
+        String query = String.format("select * from audit_log a inner join tag_history th on a.record_id = th.history_record_id where a.table_name = 'tag_history' and th.id='%s' and a.change_type='INSERT'",
                                      tag.getId().toString());
         List<Map<String, Object>> result = handle.select(query);
         assertNotNull(result);
@@ -417,7 +417,7 @@ public class TestTagStore {
         assertEquals(savedTags.size(), 0);
 
         Handle handle = dbi.open();
-        String query = String.format("select * from audit_log where table_name = 'tag' and record_id='%s' and change_type='DELETE'",
+        String query = String.format("select * from audit_log a inner join tag_history th on a.record_id = th.history_record_id where a.table_name = 'tag_history' and th.id='%s' and a.change_type='DELETE'",
                                      tag.getId().toString());
         List<Map<String, Object>> result = handle.select(query);
         assertNotNull(result);