killbill-aplcache

audit: initial check-in for Audit API optimizations This

12/20/2013 5:21:55 PM

Changes

util/src/main/java/com/ning/billing/util/audit/api/DefaultAuditLogsForAccount.java 36(+0 -36)

util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForBundles.java 94(+0 -94)

util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForInvoicePayments.java 67(+0 -67)

util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForPayments.java 67(+0 -67)

util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForRefunds.java 67(+0 -67)

util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForBundles.java 49(+0 -49)

util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForInvoicePayments.java 34(+0 -34)

util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForInvoices.java 42(+0 -42)

util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForPayments.java 34(+0 -34)

util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForRefunds.java 34(+0 -34)

Details

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
index 1099bf6..53cab7f 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/util/AuditChecker.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/util/AuditChecker.java
@@ -16,7 +16,6 @@
 
 package com.ning.billing.beatrix.util;
 
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -46,14 +45,12 @@ import com.ning.billing.subscription.engine.dao.SubscriptionEventSqlDao;
 import com.ning.billing.subscription.engine.dao.SubscriptionSqlDao;
 import com.ning.billing.util.api.AuditLevel;
 import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.audit.AccountAuditLogs;
 import com.ning.billing.util.audit.AuditLog;
-import com.ning.billing.util.audit.AuditLogsForAccount;
-import com.ning.billing.util.audit.AuditLogsForBundles;
-import com.ning.billing.util.audit.AuditLogsForInvoices;
-import com.ning.billing.util.audit.AuditLogsForPayments;
 import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
+import com.ning.billing.util.callcontext.TenantContext;
 import com.ning.billing.util.dao.NonEntityDao;
 import com.ning.billing.util.entity.Entity;
 import com.ning.billing.util.entity.dao.EntityModelDao;
@@ -85,15 +82,14 @@ public class AuditChecker {
      */
 
     public void checkAccountCreated(final Account account, final CallContext context) {
-        AuditLogsForAccount result = auditUserApi.getAuditLogsForAccount(account.getId(), AuditLevel.FULL, context);
-        checkAuditLog(ChangeType.INSERT, context, result.getAccountAuditLogs().get(0), account.getId(), AccountSqlDao.class, true, true);
-        checkAuditLog(ChangeType.UPDATE, context, result.getAccountAuditLogs().get(1), account.getId(), AccountSqlDao.class, true, true);
+        final AccountAuditLogs result = auditUserApi.getAccountAuditLogs(account.getId(), AuditLevel.FULL, context);
+        checkAuditLog(ChangeType.INSERT, context, result.getAuditLogsForAccount().get(0), account.getId(), AccountSqlDao.class, true, true);
+        checkAuditLog(ChangeType.UPDATE, context, result.getAuditLogsForAccount().get(1), account.getId(), AccountSqlDao.class, true, true);
     }
 
     public void checkPaymentCreated(final Payment payment, final CallContext context) {
-        AuditLogsForPayments result = getAuditLogsForPayment(payment, context);
-        Assert.assertEquals(result.getPaymentsAuditLogs().size(), 1);
-        final List<AuditLog> paymentLogs = result.getPaymentsAuditLogs().get(payment.getId());
+        final AccountAuditLogs result = auditUserApi.getAccountAuditLogs(payment.getAccountId(), AuditLevel.FULL, context);
+        final List<AuditLog> paymentLogs = result.getAuditLogsForPayment(payment.getId());
         Assert.assertEquals(paymentLogs.size(), 2);
         checkAuditLog(ChangeType.INSERT, context, paymentLogs.get(0), payment.getId(), PaymentSqlDao.class, true, false);
         checkAuditLog(ChangeType.UPDATE, context, paymentLogs.get(1), payment.getId(), PaymentSqlDao.class, true, false);
@@ -105,16 +101,16 @@ public class AuditChecker {
 
     // Pass the call callcontext 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);
-        checkAuditLog(ChangeType.INSERT, context, auditLogsForBundles.getBundlesAuditLogs().get(bundleId).get(0), bundleId, BundleSqlDao.class, false, false);
+        final List<AuditLog> auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
+        Assert.assertEquals(auditLogsForBundles.size(), 1);
+        checkAuditLog(ChangeType.INSERT, context, auditLogsForBundles.get(0), bundleId, BundleSqlDao.class, false, false);
     }
 
     // Pass the call callcontext 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);
-        checkAuditLog(ChangeType.UPDATE, context, auditLogsForBundles.getBundlesAuditLogs().get(bundleId).get(auditLogsForBundles.getBundlesAuditLogs().get(bundleId).size() - 1), bundleId, BundleSqlDao.class, false, false);
+        final List<AuditLog> auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
+        Assert.assertTrue(auditLogsForBundles.size() > 1);
+        checkAuditLog(ChangeType.UPDATE, context, auditLogsForBundles.get(auditLogsForBundles.size() - 1), bundleId, BundleSqlDao.class, false, false);
     }
 
     /**
@@ -123,19 +119,17 @@ public class AuditChecker {
 
     // Pass the call callcontext 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);
-        checkAuditLog(ChangeType.INSERT, context, auditLogsForBundles.getSubscriptionsAuditLogs().get(subscriptionId).get(0), subscriptionId, SubscriptionSqlDao.class, false, true);
+        final List<AuditLog> auditLogsForSubscription = getAuditLogsForSubscription(bundleId, subscriptionId, context);
+        Assert.assertEquals(auditLogsForSubscription.size(), 1);
+        checkAuditLog(ChangeType.INSERT, context, auditLogsForSubscription.get(0), subscriptionId, SubscriptionSqlDao.class, false, true);
     }
 
     // Pass the call callcontext 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), subscriptionId, SubscriptionSqlDao.class, false, false);
+        final List<AuditLog> auditLogsForSubscription = getAuditLogsForSubscription(bundleId, subscriptionId, context);
+        Assert.assertEquals(auditLogsForSubscription.size(), 2);
+        checkAuditLog(ChangeType.INSERT, auditLogsForSubscription.get(0));
+        checkAuditLog(ChangeType.UPDATE, context, auditLogsForSubscription.get(1), subscriptionId, SubscriptionSqlDao.class, false, false);
     }
 
     /**
@@ -144,24 +138,17 @@ public class AuditChecker {
 
     // Pass the call callcontext used to create the subscription event
     public void checkSubscriptionEventCreated(final UUID bundleId, final UUID subscriptionEventId, final CallContext context) {
-        final AuditLogsForBundles auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
-        checkAuditLog(ChangeType.INSERT, context, auditLogsForBundles.getSubscriptionEventsAuditLogs().get(subscriptionEventId).get(0), subscriptionEventId, SubscriptionEventSqlDao.class, false, true);
+        final List<AuditLog> auditLogsForSubscriptionEvent = getAuditLogsForSubscriptionEvent(bundleId, subscriptionEventId, context);
+        Assert.assertEquals(auditLogsForSubscriptionEvent.size(), 1);
+        checkAuditLog(ChangeType.INSERT, context, auditLogsForSubscriptionEvent.get(0), subscriptionEventId, SubscriptionEventSqlDao.class, false, true);
     }
 
     // Pass the call callcontext used to update the subscription event
     public void checkSubscriptionEventUpdated(final UUID bundleId, final UUID subscriptionEventId, final CallContext context) {
-        final AuditLogsForBundles auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
-        checkAuditLog(ChangeType.INSERT, auditLogsForBundles.getSubscriptionEventsAuditLogs().get(subscriptionEventId).get(0));
-        checkAuditLog(ChangeType.UPDATE, context, auditLogsForBundles.getSubscriptionEventsAuditLogs().get(subscriptionEventId).get(1), subscriptionEventId, SubscriptionEventSqlDao.class, false, true);
-    }
-
-    /**
-     * ********************************************  PAYMENT *******************************************************
-     */
-
-    private AuditLogsForPayments getAuditLogsForPayment(final Payment payment, final CallContext context) {
-        AuditLogsForPayments results = auditUserApi.getAuditLogsForPayments(Collections.singletonList(payment), AuditLevel.FULL, context);
-        return results;
+        final List<AuditLog> auditLogsForSubscriptionEvent = getAuditLogsForSubscriptionEvent(bundleId, subscriptionEventId, context);
+        Assert.assertEquals(auditLogsForSubscriptionEvent.size(), 2);
+        checkAuditLog(ChangeType.INSERT, auditLogsForSubscriptionEvent.get(0));
+        checkAuditLog(ChangeType.UPDATE, context, auditLogsForSubscriptionEvent.get(1), subscriptionEventId, SubscriptionEventSqlDao.class, false, true);
     }
 
     /**
@@ -169,32 +156,53 @@ public class AuditChecker {
      */
 
     public void checkInvoiceCreated(final Invoice invoice, final CallContext context) {
-        AuditLogsForInvoices result = getAuditLogForInvoice(invoice, context);
-        Assert.assertEquals(result.getInvoiceAuditLogs().keySet().size(), 1);
-        final List<AuditLog> invoiceLogs = result.getInvoiceAuditLogs().get(invoice.getId());
+        final List<AuditLog> invoiceLogs = getAuditLogForInvoice(invoice, context);
         Assert.assertEquals(invoiceLogs.size(), 1);
         checkAuditLog(ChangeType.INSERT, context, invoiceLogs.get(0), invoice.getId(), InvoiceSqlDao.class, false, false);
 
-        Assert.assertEquals(result.getInvoiceItemsAuditLogs().keySet().size(), invoice.getInvoiceItems().size());
         for (InvoiceItem cur : invoice.getInvoiceItems()) {
-            final List<AuditLog> auditLogs = result.getInvoiceItemsAuditLogs().get(cur.getId());
+            final List<AuditLog> auditLogs = getAuditLogForInvoiceItem(cur, context);
             Assert.assertEquals(auditLogs.size(), 1);
             checkAuditLog(ChangeType.INSERT, context, auditLogs.get(0), cur.getId(), InvoiceItemSqlDao.class, false, false);
         }
     }
 
-    private AuditLogsForBundles getAuditLogsForBundle(final UUID bundleId, final CallContext context) {
+    private List<AuditLog> getAuditLogsForBundle(final UUID bundleId, final TenantContext context) {
+        try {
+            final SubscriptionBundle bundle = subscriptionApi.getSubscriptionBundle(bundleId, context);
+            return auditUserApi.getAccountAuditLogs(bundle.getAccountId(), AuditLevel.FULL, context).getAuditLogsForBundle(bundle.getId());
+        } catch (final SubscriptionApiException e) {
+            Assert.fail(e.toString());
+            return null;
+        }
+    }
+
+    private List<AuditLog> getAuditLogsForSubscription(final UUID bundleId, final UUID subscriptionId, final TenantContext context) {
         try {
             final SubscriptionBundle bundle = subscriptionApi.getSubscriptionBundle(bundleId, context);
-            return auditUserApi.getAuditLogsForBundles(Collections.singletonList(bundle), AuditLevel.FULL, context);
-        } catch (SubscriptionApiException e) {
+            return auditUserApi.getAccountAuditLogs(bundle.getAccountId(), AuditLevel.FULL, context).getAuditLogsForSubscription(subscriptionId);
+        } catch (final SubscriptionApiException e) {
             Assert.fail(e.toString());
             return null;
         }
     }
 
-    private AuditLogsForInvoices getAuditLogForInvoice(final Invoice invoice, final CallContext context) {
-        return auditUserApi.getAuditLogsForInvoices(Collections.singletonList(invoice), AuditLevel.FULL, context);
+    private List<AuditLog> getAuditLogsForSubscriptionEvent(final UUID bundleId, final UUID subscriptionEventId, final TenantContext context) {
+        try {
+            final SubscriptionBundle bundle = subscriptionApi.getSubscriptionBundle(bundleId, context);
+            return auditUserApi.getAccountAuditLogs(bundle.getAccountId(), AuditLevel.FULL, context).getAuditLogsForSubscriptionEvent(subscriptionEventId);
+        } catch (final SubscriptionApiException e) {
+            Assert.fail(e.toString());
+            return null;
+        }
+    }
+
+    private List<AuditLog> getAuditLogForInvoice(final Invoice invoice, final TenantContext context) {
+        return auditUserApi.getAccountAuditLogs(invoice.getAccountId(), AuditLevel.FULL, context).getAuditLogsForInvoice(invoice.getId());
+    }
+
+    private List<AuditLog> getAuditLogForInvoiceItem(final InvoiceItem invoiceItem, final TenantContext context) {
+        return auditUserApi.getAccountAuditLogs(invoiceItem.getAccountId(), AuditLevel.FULL, context).getAuditLogsForInvoiceItem(invoiceItem.getId());
     }
 
     private void checkAuditLog(final ChangeType insert, final AuditLog auditLog) {
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountTimelineJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountTimelineJson.java
index e408af4..21507b8 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountTimelineJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountTimelineJson.java
@@ -20,12 +20,9 @@ import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
 
-import javax.annotation.Nullable;
-
 import com.ning.billing.account.api.Account;
 import com.ning.billing.entitlement.api.SubscriptionBundle;
 import com.ning.billing.invoice.api.Invoice;
@@ -34,16 +31,11 @@ import com.ning.billing.invoice.api.InvoiceItemType;
 import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.payment.api.Payment;
 import com.ning.billing.payment.api.Refund;
+import com.ning.billing.util.audit.AccountAuditLogs;
 import com.ning.billing.util.audit.AuditLog;
-import com.ning.billing.util.audit.AuditLogsForBundles;
-import com.ning.billing.util.audit.AuditLogsForInvoicePayments;
-import com.ning.billing.util.audit.AuditLogsForInvoices;
-import com.ning.billing.util.audit.AuditLogsForPayments;
-import com.ning.billing.util.audit.AuditLogsForRefunds;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Multimap;
 
 public class AccountTimelineJson {
@@ -95,35 +87,14 @@ public class AccountTimelineJson {
         return tmp.toString();
     }
 
-
     public AccountTimelineJson(final Account account, final List<Invoice> invoices, final List<Payment> payments,
-                               final List<SubscriptionBundle> bundlesTimeline, final Multimap<UUID, Refund> refundsByPayment,
-                               final Multimap<UUID, InvoicePayment> chargebacksByPayment, @Nullable final AuditLogsForInvoices invoicesAuditLogs,
-                               @Nullable final AuditLogsForPayments paymentsAuditLogs, @Nullable final AuditLogsForRefunds refundsAuditLogs,
-                               @Nullable final AuditLogsForInvoicePayments chargebacksAuditLogs, @Nullable final AuditLogsForBundles bundlesAuditLogs) {
-        this(account, invoices, payments, bundlesTimeline, refundsByPayment, chargebacksByPayment,
-             invoicesAuditLogs == null ? ImmutableMap.<UUID, List<AuditLog>>of() : invoicesAuditLogs.getInvoiceAuditLogs(),
-             invoicesAuditLogs == null ? ImmutableMap.<UUID, List<AuditLog>>of() : invoicesAuditLogs.getInvoiceItemsAuditLogs(),
-             paymentsAuditLogs == null ? ImmutableMap.<UUID, List<AuditLog>>of() : paymentsAuditLogs.getPaymentsAuditLogs(),
-             refundsAuditLogs == null ? ImmutableMap.<UUID, List<AuditLog>>of() : refundsAuditLogs.getRefundsAuditLogs(),
-             chargebacksAuditLogs == null ? ImmutableMap.<UUID, List<AuditLog>>of() : chargebacksAuditLogs.getInvoicePaymentsAuditLogs(),
-             bundlesAuditLogs == null ? ImmutableMap.<UUID, List<AuditLog>>of() : bundlesAuditLogs.getBundlesAuditLogs(),
-             bundlesAuditLogs == null ? ImmutableMap.<UUID, List<AuditLog>>of() : bundlesAuditLogs.getSubscriptionsAuditLogs(),
-             bundlesAuditLogs == null ? ImmutableMap.<UUID, List<AuditLog>>of() : bundlesAuditLogs.getSubscriptionEventsAuditLogs());
-    }
-
-    public AccountTimelineJson(final Account account, final List<Invoice> invoices, final List<Payment> payments, final List<SubscriptionBundle> bundles,
-                               final Multimap<UUID, Refund> refundsByPayment, final Multimap<UUID, InvoicePayment> chargebacksByPayment,
-                               final Map<UUID, List<AuditLog>> invoiceAuditLogs, final Map<UUID, List<AuditLog>> invoiceItemsAuditLogs,
-                               final Map<UUID, List<AuditLog>> paymentsAuditLogs, final Map<UUID, List<AuditLog>> refundsAuditLogs,
-                               final Map<UUID, List<AuditLog>> chargebacksAuditLogs, final Map<UUID, List<AuditLog>> bundlesAuditLogs,
-                               final Map<UUID, List<AuditLog>> subscriptionsAuditLogs, final Map<UUID, List<AuditLog>> subscriptionEventsAuditLogs) {
+                               final List<SubscriptionBundle> bundles, final Multimap<UUID, Refund> refundsByPayment,
+                               final Multimap<UUID, InvoicePayment> chargebacksByPayment, final AccountAuditLogs accountAuditLogs) {
         this.account = new AccountJson(account, null, null);
         this.bundles = new LinkedList<BundleJson>();
         for (final SubscriptionBundle bundle : bundles) {
-            final List<AuditLog> bundleAuditLogs = bundlesAuditLogs.get(bundle.getId());
-            final BundleJson jsonWithSubscriptions = new BundleJson(bundle, bundleAuditLogs,
-                                                                                                      subscriptionsAuditLogs, subscriptionEventsAuditLogs);
+            final List<AuditLog> bundleAuditLogs = accountAuditLogs.getAuditLogsForBundle(bundle.getId());
+            final BundleJson jsonWithSubscriptions = new BundleJson(bundle, accountAuditLogs);
             this.bundles.add(jsonWithSubscriptions);
         }
 
@@ -133,43 +104,41 @@ public class AccountTimelineJson {
         for (final Invoice invoice : invoices) {
             for (final InvoiceItem invoiceItem : invoice.getInvoiceItems()) {
                 if (InvoiceItemType.CREDIT_ADJ.equals(invoiceItem.getInvoiceItemType())) {
-                    final List<AuditLog> auditLogs = invoiceItemsAuditLogs.get(invoiceItem.getId());
+                    final List<AuditLog> auditLogs = accountAuditLogs.getAuditLogsForInvoiceItem(invoiceItem.getId());
                     credits.add(new CreditJson(invoice, invoiceItem, auditLogs));
                 }
             }
         }
         // Create now the invoice json objects
         for (final Invoice invoice : invoices) {
-            final List<AuditLog> auditLogs = invoiceAuditLogs.get(invoice.getId());
+            final List<AuditLog> auditLogs = accountAuditLogs.getAuditLogsForInvoice(invoice.getId());
             this.invoices.add(new InvoiceJson(invoice,
-                                                            getBundleExternalKey(invoice, bundles),
-                                                            credits,
-                                                            auditLogs));
+                                              getBundleExternalKey(invoice, bundles),
+                                              credits,
+                                              auditLogs));
         }
 
         this.payments = new LinkedList<PaymentJson>();
         for (final Payment payment : payments) {
             final List<RefundJson> refunds = new ArrayList<RefundJson>();
             for (final Refund refund : refundsByPayment.get(payment.getId())) {
-                final List<AuditLog> auditLogs = refundsAuditLogs.get(refund.getId());
+                final List<AuditLog> auditLogs = accountAuditLogs.getAuditLogsForRefund(refund.getId());
                 // TODO add adjusted invoice items?
                 refunds.add(new RefundJson(refund, null, auditLogs));
             }
 
             final List<ChargebackJson> chargebacks = new ArrayList<ChargebackJson>();
             for (final InvoicePayment chargeback : chargebacksByPayment.get(payment.getId())) {
-                final List<AuditLog> auditLogs = chargebacksAuditLogs.get(chargeback.getId());
+                final List<AuditLog> auditLogs = accountAuditLogs.getAuditLogsForChargeback(chargeback.getId());
                 chargebacks.add(new ChargebackJson(payment.getAccountId(), chargeback, auditLogs));
             }
 
-            final int nbOfPaymentAttempts = payment.getAttempts().size();
-            final String status = payment.getPaymentStatus().toString();
-            final List<AuditLog> auditLogs = paymentsAuditLogs.get(payment.getId());
+            final List<AuditLog> auditLogs = accountAuditLogs.getAuditLogsForPayment(payment.getId());
             this.payments.add(new PaymentJson(payment,
                                               getBundleExternalKey(payment.getInvoiceId(), invoices, bundles),
                                               refunds,
-                                                            chargebacks,
-                                                            auditLogs));
+                                              chargebacks,
+                                              auditLogs));
         }
     }
 
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/BundleJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/BundleJson.java
index da1a3e8..c3cae9c 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/BundleJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/BundleJson.java
@@ -18,15 +18,13 @@ package com.ning.billing.jaxrs.json;
 
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
-import java.util.UUID;
 
 import javax.annotation.Nullable;
 
 import com.ning.billing.entitlement.api.Subscription;
 import com.ning.billing.entitlement.api.SubscriptionBundle;
 import com.ning.billing.entitlement.api.SubscriptionEvent;
-import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.audit.AccountAuditLogs;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -71,26 +69,21 @@ public class BundleJson extends JsonBase {
         return externalKey;
     }
 
-    public BundleJson(final SubscriptionBundle bundle, final List<AuditLog> auditLogs,
-                      final Map<UUID, List<AuditLog>> subscriptionsAuditLogs, final Map<UUID, List<AuditLog>> subscriptionEventsAuditLogs) {
-        super(toAuditLogJson(auditLogs));
+    public BundleJson(final SubscriptionBundle bundle, @Nullable final AccountAuditLogs accountAuditLogs) {
+        super(toAuditLogJson(accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForBundle(bundle.getId())));
         this.accountId = bundle.getAccountId().toString();
         this.bundleId = bundle.getId().toString();
         this.externalKey = bundle.getExternalKey();
 
         this.subscriptions = new LinkedList<SubscriptionJson>();
         for (final Subscription cur : bundle.getSubscriptions()) {
-
             final ImmutableList<SubscriptionEvent> events = ImmutableList.<SubscriptionEvent>copyOf(Collections2.filter(bundle.getTimeline().getSubscriptionEvents(), new Predicate<SubscriptionEvent>() {
                 @Override
                 public boolean apply(@Nullable final SubscriptionEvent input) {
                     return input.getEntitlementId().equals(cur.getId());
                 }
             }));
-            this.subscriptions.add(new SubscriptionJson(cur,
-                                                        events,
-                                                        (subscriptionsAuditLogs != null ? subscriptionsAuditLogs.get(cur.getId()) : null),
-                                                        subscriptionEventsAuditLogs));
+            this.subscriptions.add(new SubscriptionJson(cur, events, accountAuditLogs));
         }
     }
 
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJson.java
index 3958225..f142361 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJson.java
@@ -19,8 +19,6 @@ package com.ning.billing.jaxrs.json;
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
-import java.util.UUID;
 
 import javax.annotation.Nullable;
 
@@ -28,6 +26,7 @@ import org.joda.time.LocalDate;
 
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.util.audit.AccountAuditLogs;
 import com.ning.billing.util.audit.AuditLog;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
@@ -80,6 +79,9 @@ public class InvoiceJson extends JsonBase {
         this.items = items;
     }
 
+    public InvoiceJson(final Invoice input) {
+        this(input, null, null, null);
+    }
 
     public InvoiceJson(final Invoice input, @Nullable final List<AuditLog> auditLogs) {
         this(input, null, null, auditLogs);
@@ -91,11 +93,11 @@ public class InvoiceJson extends JsonBase {
              input.getBalance(), input.getAccountId().toString(), bundleKeys, credits, null, toAuditLogJson(auditLogs));
     }
 
-    public InvoiceJson(final Invoice input, @Nullable final List<AuditLog> invoiceAuditLogs, @Nullable final Map<UUID, List<AuditLog>> invoiceItemsAuditLogs) {
-        super(toAuditLogJson(invoiceAuditLogs));
+    public InvoiceJson(final Invoice input, final AccountAuditLogs accountAuditLogs) {
+        super(toAuditLogJson(accountAuditLogs.getAuditLogsForInvoice(input.getId())));
         this.items = new ArrayList<InvoiceItemJson>(input.getInvoiceItems().size());
         for (final InvoiceItem item : input.getInvoiceItems()) {
-            this.items.add(new InvoiceItemJson(item, invoiceItemsAuditLogs == null ? null : invoiceItemsAuditLogs.get(item.getId())));
+            this.items.add(new InvoiceItemJson(item, accountAuditLogs.getAuditLogsForInvoiceItem(item.getId())));
         }
         this.amount = input.getChargedAmount();
         this.currency = input.getCurrency().toString();
@@ -199,7 +201,7 @@ public class InvoiceJson extends JsonBase {
         if (amount != null ? amount.compareTo(that.amount) != 0 : that.amount != null) {
             return false;
         }
-        if (balance != null ? balance.compareTo(that.balance) != 0: that.balance != null) {
+        if (balance != null ? balance.compareTo(that.balance) != 0 : that.balance != null) {
             return false;
         }
         if (bundleKeys != null ? !bundleKeys.equals(that.bundleKeys) : that.bundleKeys != null) {
@@ -226,7 +228,7 @@ public class InvoiceJson extends JsonBase {
         if (items != null ? !items.equals(that.items) : that.items != null) {
             return false;
         }
-        if (refundAdj != null ? refundAdj.compareTo(that.refundAdj) != 0: that.refundAdj != null) {
+        if (refundAdj != null ? refundAdj.compareTo(that.refundAdj) != 0 : that.refundAdj != null) {
             return false;
         }
         if (targetDate != null ? targetDate.compareTo(that.targetDate) != 0 : that.targetDate != null) {
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/SubscriptionJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/SubscriptionJson.java
index ad23683..dfaca55 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/SubscriptionJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/SubscriptionJson.java
@@ -18,8 +18,6 @@ package com.ning.billing.jaxrs.json;
 
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
-import java.util.UUID;
 
 import javax.annotation.Nullable;
 
@@ -31,7 +29,7 @@ import com.ning.billing.catalog.api.PriceList;
 import com.ning.billing.catalog.api.Product;
 import com.ning.billing.entitlement.api.Subscription;
 import com.ning.billing.entitlement.api.SubscriptionEvent;
-import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.audit.AccountAuditLogs;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -313,12 +311,10 @@ public class SubscriptionJson extends JsonBase {
         this.newEvents = newEvents;
     }
 
-
     public SubscriptionJson(final Subscription subscription,
                             final List<SubscriptionEvent> subscriptionEvents,
-                            final List<AuditLog> subscriptionAuditLogs,
-                            final Map<UUID, List<AuditLog>> subscriptionEventsAuditLogs) {
-        super(toAuditLogJson(subscriptionAuditLogs));
+                            @Nullable final AccountAuditLogs accountAuditLogs) {
+        super(toAuditLogJson(accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForSubscription(subscription.getId())));
         this.startDate = subscription.getEffectiveStartDate();
         this.productName = subscription.getLastActiveProduct().getName();
         this.productCategory = subscription.getLastActiveProductCategory().name();
@@ -334,20 +330,20 @@ public class SubscriptionJson extends JsonBase {
         this.externalKey = subscription.getExternalKey();
         this.events = subscriptionEvents != null ? new LinkedList<EventSubscriptionJson>() : null;
         if (events != null) {
-            for (SubscriptionEvent cur : subscriptionEvents) {
+            for (final SubscriptionEvent cur : subscriptionEvents) {
                 final BillingPeriod billingPeriod = cur.getNextBillingPeriod() != null ? cur.getNextBillingPeriod() : cur.getPrevBillingPeriod();
                 final Product product = cur.getNextProduct() != null ? cur.getNextProduct() : cur.getPrevProduct();
                 final PriceList priceList = cur.getNextPriceList() != null ? cur.getNextPriceList() : cur.getPrevPriceList();
                 final PlanPhase phase = cur.getNextPhase() != null ? cur.getNextPhase() : cur.getPrevPhase();
                 this.events.add(new EventSubscriptionJson(cur.getId().toString(),
-                                                              billingPeriod != null ? billingPeriod.toString() : null,
-                                                              cur.getRequestedDate(),
-                                                              cur.getEffectiveDate(),
-                                                              product != null ? product.getName() : null,
-                                                              priceList != null ? priceList.getName() : null,
-                                                              cur.getSubscriptionEventType().toString(),
-                                                              phase != null ? phase.getName() : null,
-                                                              (subscriptionEventsAuditLogs != null ? toAuditLogJson(subscriptionEventsAuditLogs.get(cur.getId())) : null)));
+                                                          billingPeriod != null ? billingPeriod.toString() : null,
+                                                          cur.getRequestedDate(),
+                                                          cur.getEffectiveDate(),
+                                                          product != null ? product.getName() : null,
+                                                          priceList != null ? priceList.getName() : null,
+                                                          cur.getSubscriptionEventType().toString(),
+                                                          phase != null ? phase.getName() : null,
+                                                          toAuditLogJson(accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForSubscriptionEvent(cur.getId()))));
             }
         }
         this.newEvents = null;
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
index a48b849..0cca7e0 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
@@ -90,11 +90,7 @@ import com.ning.billing.util.api.CustomFieldUserApi;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.api.TagUserApi;
-import com.ning.billing.util.audit.AuditLogsForBundles;
-import com.ning.billing.util.audit.AuditLogsForInvoicePayments;
-import com.ning.billing.util.audit.AuditLogsForInvoices;
-import com.ning.billing.util.audit.AuditLogsForPayments;
-import com.ning.billing.util.audit.AuditLogsForRefunds;
+import com.ning.billing.util.audit.AccountAuditLogs;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.TenantContext;
 import com.ning.billing.util.entity.Pagination;
@@ -231,7 +227,7 @@ public class AccountResource extends JaxRsResourceBase {
         final Collection<BundleJson> result = Collections2.transform(bundles, new Function<SubscriptionBundle, BundleJson>() {
             @Override
             public BundleJson apply(final SubscriptionBundle input) {
-                return new BundleJson(input, null, null, null);
+                return new BundleJson(input, null);
             }
         });
         return Response.status(Status.OK).entity(result).build();
@@ -322,15 +318,12 @@ public class AccountResource extends JaxRsResourceBase {
 
         // Get the invoices
         final List<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId(), tenantContext);
-        final AuditLogsForInvoices invoicesAuditLogs = auditUserApi.getAuditLogsForInvoices(invoices, auditMode.getLevel(), tenantContext);
 
         // Get the payments
         final List<Payment> payments = paymentApi.getAccountPayments(accountId, tenantContext);
-        final AuditLogsForPayments paymentsAuditLogs = auditUserApi.getAuditLogsForPayments(payments, auditMode.getLevel(), tenantContext);
 
         // Get the refunds
         final List<Refund> refunds = paymentApi.getAccountRefunds(account, tenantContext);
-        final AuditLogsForRefunds refundsAuditLogs = auditUserApi.getAuditLogsForRefunds(refunds, auditMode.getLevel(), tenantContext);
         final Multimap<UUID, Refund> refundsByPayment = ArrayListMultimap.<UUID, Refund>create();
         for (final Refund refund : refunds) {
             refundsByPayment.put(refund.getPaymentId(), refund);
@@ -338,7 +331,6 @@ public class AccountResource extends JaxRsResourceBase {
 
         // Get the chargebacks
         final List<InvoicePayment> chargebacks = invoicePaymentApi.getChargebacksByAccountId(accountId, tenantContext);
-        final AuditLogsForInvoicePayments chargebacksAuditLogs = auditUserApi.getAuditLogsForInvoicePayments(chargebacks, auditMode.getLevel(), tenantContext);
         final Multimap<UUID, InvoicePayment> chargebacksByPayment = ArrayListMultimap.<UUID, InvoicePayment>create();
         for (final InvoicePayment chargeback : chargebacks) {
             chargebacksByPayment.put(chargeback.getPaymentId(), chargeback);
@@ -346,12 +338,13 @@ public class AccountResource extends JaxRsResourceBase {
 
         // Get the bundles
         final List<SubscriptionBundle> bundles = subscriptionApi.getSubscriptionBundlesForAccountId(account.getId(), tenantContext);
-        final AuditLogsForBundles bundlesAuditLogs = auditUserApi.getAuditLogsForBundles(bundles, auditMode.getLevel(), tenantContext);
+
+        // Get all audit logs
+        final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(accountId, auditMode.getLevel(), tenantContext);
 
         final AccountTimelineJson json = new AccountTimelineJson(account, invoices, payments, bundles,
                                                                  refundsByPayment, chargebacksByPayment,
-                                                                 invoicesAuditLogs, paymentsAuditLogs, refundsAuditLogs,
-                                                                 chargebacksAuditLogs, bundlesAuditLogs);
+                                                                 accountAuditLogs);
         return Response.status(Status.OK).entity(json).build();
     }
 
@@ -420,7 +413,7 @@ public class AccountResource extends JaxRsResourceBase {
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + INVOICES)
     @Produces(APPLICATION_JSON)
-    public Response getInvoices(@PathParam("accountId") final String accountId,
+    public Response getInvoices(@PathParam("accountId") final String accountIdString,
                                 @QueryParam(QUERY_INVOICE_WITH_ITEMS) @DefaultValue("false") final boolean withItems,
                                 @QueryParam(QUERY_UNPAID_INVOICES_ONLY) @DefaultValue("false") final boolean unpaidInvoicesOnly,
                                 @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
@@ -428,28 +421,26 @@ public class AccountResource extends JaxRsResourceBase {
         final TenantContext tenantContext = context.createContext(request);
 
         // Verify the account exists
-        accountUserApi.getAccountById(UUID.fromString(accountId), tenantContext);
+        final UUID accountId = UUID.fromString(accountIdString);
+        accountUserApi.getAccountById(accountId, tenantContext);
 
         final List<Invoice> invoices = unpaidInvoicesOnly ?
-                                       new ArrayList<Invoice>(invoiceApi.getUnpaidInvoicesByAccountId(UUID.fromString(accountId), null, tenantContext)) :
-                                       invoiceApi.getInvoicesByAccount(UUID.fromString(accountId), tenantContext);
+                                       new ArrayList<Invoice>(invoiceApi.getUnpaidInvoicesByAccountId(accountId, null, tenantContext)) :
+                                       invoiceApi.getInvoicesByAccount(accountId, tenantContext);
 
-        final AuditLogsForInvoices invoicesAuditLogs = auditUserApi.getAuditLogsForInvoices(invoices, auditMode.getLevel(), tenantContext);
+        final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(accountId, auditMode.getLevel(), tenantContext);
 
         if (withItems) {
             final List<InvoiceJson> result = new LinkedList<InvoiceJson>();
             for (final Invoice invoice : invoices) {
-                result.add(new InvoiceJson(invoice,
-                                           invoicesAuditLogs.getInvoiceAuditLogs().get(invoice.getId()),
-                                           invoicesAuditLogs.getInvoiceItemsAuditLogs()));
+                result.add(new InvoiceJson(invoice, accountAuditLogs));
             }
 
             return Response.status(Status.OK).entity(result).build();
         } else {
             final List<InvoiceJson> result = new LinkedList<InvoiceJson>();
             for (final Invoice invoice : invoices) {
-                result.add(new InvoiceJson(invoice,
-                                           invoicesAuditLogs.getInvoiceAuditLogs().get(invoice.getId())));
+                result.add(new InvoiceJson(invoice, accountAuditLogs.getAuditLogsForInvoice(invoice.getId())));
             }
 
             return Response.status(Status.OK).entity(result).build();
@@ -681,11 +672,12 @@ public class AccountResource extends JaxRsResourceBase {
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + TAGS)
     @Produces(APPLICATION_JSON)
-    public Response getTags(@PathParam(ID_PARAM_NAME) final String id,
+    public Response getTags(@PathParam(ID_PARAM_NAME) final String accountIdString,
                             @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                             @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
                             @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException {
-        return super.getTags(UUID.fromString(id), auditMode, includedDeleted, context.createContext(request));
+        final UUID accountId = UUID.fromString(accountIdString);
+        return super.getTags(accountId, accountId, auditMode, includedDeleted, context.createContext(request));
     }
 
     @POST
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
index c0583f2..e7aa16d 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
@@ -57,6 +57,7 @@ import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.api.TagUserApi;
 import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.TenantContext;
 
 import com.google.inject.Inject;
 
@@ -90,10 +91,9 @@ public class BundleResource extends JaxRsResourceBase {
     @Produces(APPLICATION_JSON)
     public Response getBundle(@PathParam("bundleId") final String bundleId,
                               @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException {
-
         final UUID id = UUID.fromString(bundleId);
         final SubscriptionBundle bundle = subscriptionApi.getSubscriptionBundle(id, context.createContext(request));
-        final BundleJson json = new BundleJson(bundle, null, null, null);
+        final BundleJson json = new BundleJson(bundle, null);
         return Response.status(Status.OK).entity(json).build();
     }
 
@@ -102,7 +102,7 @@ public class BundleResource extends JaxRsResourceBase {
     public Response getBundleByKey(@QueryParam(QUERY_EXTERNAL_KEY) final String externalKey,
                                    @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException {
         final SubscriptionBundle bundle = subscriptionApi.getActiveSubscriptionBundleForExternalKey(externalKey, context.createContext(request));
-        final BundleJson json = new BundleJson(bundle, null, null, null);
+        final BundleJson json = new BundleJson(bundle, null);
         return Response.status(Status.OK).entity(json).build();
     }
 
@@ -184,11 +184,14 @@ public class BundleResource extends JaxRsResourceBase {
     @GET
     @Path("/{bundleId:" + UUID_PATTERN + "}/" + TAGS)
     @Produces(APPLICATION_JSON)
-    public Response getTags(@PathParam(ID_PARAM_NAME) final String id,
+    public Response getTags(@PathParam(ID_PARAM_NAME) final String bundleIdString,
                             @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                             @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
-                            @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException {
-        return super.getTags(UUID.fromString(id), auditMode, includedDeleted, context.createContext(request));
+                            @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException, SubscriptionApiException {
+        final UUID bundleId = UUID.fromString(bundleIdString);
+        final TenantContext tenantContext = context.createContext(request);
+        final SubscriptionBundle bundle = subscriptionApi.getSubscriptionBundle(bundleId, context.createContext(request));
+        return super.getTags(bundle.getAccountId(), bundleId, auditMode, includedDeleted, tenantContext);
     }
 
     @PUT
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
index 2068d70..0af2810 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
@@ -67,13 +67,12 @@ import com.ning.billing.util.api.CustomFieldUserApi;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.api.TagUserApi;
-import com.ning.billing.util.audit.AuditLogsForInvoices;
-import com.ning.billing.util.audit.AuditLogsForPayments;
+import com.ning.billing.util.audit.AccountAuditLogs;
+import com.ning.billing.util.audit.AccountAuditLogsForObjectType;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.TenantContext;
 
 import com.google.common.base.Objects;
-import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
@@ -115,19 +114,12 @@ public class InvoiceResource extends JaxRsResourceBase {
                                @javax.ws.rs.core.Context final HttpServletRequest request) throws InvoiceApiException {
         final TenantContext tenantContext = context.createContext(request);
         final Invoice invoice = invoiceApi.getInvoice(UUID.fromString(invoiceId), tenantContext);
-        final AuditLogsForInvoices invoicesAuditLogs = auditUserApi.getAuditLogsForInvoices(ImmutableList.<Invoice>of(invoice),
-                                                                                            auditMode.getLevel(),
-                                                                                            tenantContext);
+        final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(invoice.getAccountId(), auditMode.getLevel(), tenantContext);
 
         if (invoice == null) {
             throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceId);
         } else {
-            final InvoiceJson json = withItems ?
-                                     new InvoiceJson(invoice,
-                                                     invoicesAuditLogs.getInvoiceAuditLogs().get(invoice.getId()),
-                                                     invoicesAuditLogs.getInvoiceItemsAuditLogs()) :
-                                     new InvoiceJson(invoice,
-                                                     invoicesAuditLogs.getInvoiceAuditLogs().get(invoice.getId()));
+            final InvoiceJson json = new InvoiceJson(invoice, accountAuditLogs);
             return Response.status(Status.OK).entity(json).build();
         }
     }
@@ -141,19 +133,12 @@ public class InvoiceResource extends JaxRsResourceBase {
                                        @javax.ws.rs.core.Context final HttpServletRequest request) throws InvoiceApiException {
         final TenantContext tenantContext = context.createContext(request);
         final Invoice invoice = invoiceApi.getInvoiceByNumber(invoiceNumber, tenantContext);
-        final AuditLogsForInvoices invoicesAuditLogs = auditUserApi.getAuditLogsForInvoices(ImmutableList.<Invoice>of(invoice),
-                                                                                            auditMode.getLevel(),
-                                                                                            tenantContext);
+        final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(invoice.getAccountId(), auditMode.getLevel(), tenantContext);
 
         if (invoice == null) {
             throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceNumber);
         } else {
-            final InvoiceJson json = withItems ?
-                                     new InvoiceJson(invoice,
-                                                     invoicesAuditLogs.getInvoiceAuditLogs().get(invoice.getId()),
-                                                     invoicesAuditLogs.getInvoiceItemsAuditLogs()) :
-                                     new InvoiceJson(invoice,
-                                                     invoicesAuditLogs.getInvoiceAuditLogs().get(invoice.getId()));
+            final InvoiceJson json = new InvoiceJson(invoice, accountAuditLogs);
             return Response.status(Status.OK).entity(json).build();
         }
     }
@@ -182,7 +167,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         final Invoice generatedInvoice = invoiceApi.triggerInvoiceGeneration(UUID.fromString(accountId), inputDate, dryRun,
                                                                              callContext);
         if (dryRun) {
-            return Response.status(Status.OK).entity(new InvoiceJson(generatedInvoice, null)).build();
+            return Response.status(Status.OK).entity(new InvoiceJson(generatedInvoice)).build();
         } else {
             return uriBuilder.buildResponse(InvoiceResource.class, "getInvoice", generatedInvoice.getId());
         }
@@ -330,11 +315,18 @@ public class InvoiceResource extends JaxRsResourceBase {
                                 @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException {
         final TenantContext tenantContext = context.createContext(request);
         final List<Payment> payments = paymentApi.getInvoicePayments(UUID.fromString(invoiceId), tenantContext);
-        final AuditLogsForPayments auditLogsForPayments = auditUserApi.getAuditLogsForPayments(payments, auditMode.getLevel(), tenantContext);
-
         final List<PaymentJson> result = new ArrayList<PaymentJson>(payments.size());
+        if (payments.size() == 0) {
+            return Response.status(Status.OK).entity(result).build();
+        }
+
+        final AccountAuditLogsForObjectType auditLogsForPayments = auditUserApi.getAccountAuditLogs(payments.get(0).getAccountId(),
+                                                                                                    ObjectType.PAYMENT,
+                                                                                                    auditMode.getLevel(),
+                                                                                                    tenantContext);
+
         for (final Payment cur : payments) {
-            result.add(new PaymentJson(cur, auditLogsForPayments.getPaymentsAuditLogs().get(cur.getId())));
+            result.add(new PaymentJson(cur, auditLogsForPayments.getAuditLogs(cur.getId())));
         }
 
         return Response.status(Status.OK).entity(result).build();
@@ -428,11 +420,14 @@ public class InvoiceResource extends JaxRsResourceBase {
     @GET
     @Path("/{invoiceId:" + UUID_PATTERN + "}/" + TAGS)
     @Produces(APPLICATION_JSON)
-    public Response getTags(@PathParam(ID_PARAM_NAME) final String id,
+    public Response getTags(@PathParam(ID_PARAM_NAME) final String invoiceIdString,
                             @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                             @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
-                            @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException {
-        return super.getTags(UUID.fromString(id), auditMode, includedDeleted, context.createContext(request));
+                            @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException, InvoiceApiException {
+        final UUID invoiceId = UUID.fromString(invoiceIdString);
+        final TenantContext tenantContext = context.createContext(request);
+        final Invoice invoice = invoiceApi.getInvoice(invoiceId, tenantContext);
+        return super.getTags(invoice.getAccountId(), invoiceId, auditMode, includedDeleted, tenantContext);
     }
 
     @POST
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java
index 0e1d63c..e583cd4 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java
@@ -50,6 +50,7 @@ import com.ning.billing.util.api.CustomFieldUserApi;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.api.TagUserApi;
+import com.ning.billing.util.audit.AccountAuditLogsForObjectType;
 import com.ning.billing.util.audit.AuditLog;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.TenantContext;
@@ -100,8 +101,9 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
         return null;
     }
 
-    protected Response getTags(final UUID taggedObjectId, final AuditMode auditMode, final boolean includeDeleted, final TenantContext context) throws TagDefinitionApiException {
+    protected Response getTags(final UUID accountId, final UUID taggedObjectId, final AuditMode auditMode, final boolean includeDeleted, final TenantContext context) throws TagDefinitionApiException {
         final List<Tag> tags = tagUserApi.getTagsForObject(taggedObjectId, getObjectType(), includeDeleted, context);
+        final AccountAuditLogsForObjectType tagsAuditLogs = auditUserApi.getAccountAuditLogs(accountId, ObjectType.TAG, auditMode.getLevel(), context);
 
         final Map<UUID, TagDefinition> tagDefinitionsCache = new HashMap<UUID, TagDefinition>();
         final Collection<TagJson> result = new LinkedList<TagJson>();
@@ -111,8 +113,7 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
             }
             final TagDefinition tagDefinition = tagDefinitionsCache.get(tag.getTagDefinitionId());
 
-            // TODO PIERRE - Bulk API
-            final List<AuditLog> auditLogs = auditUserApi.getAuditLogs(tag.getId(), ObjectType.TAG, auditMode.getLevel(), context);
+            final List<AuditLog> auditLogs = tagsAuditLogs.getAuditLogs(tag.getId());
             result.add(new TagJson(tagDefinition, auditLogs));
         }
 
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
index 21a4ef1..8f99a77 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
@@ -265,11 +265,14 @@ public class PaymentResource extends JaxRsResourceBase {
     @GET
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + TAGS)
     @Produces(APPLICATION_JSON)
-    public Response getTags(@PathParam(ID_PARAM_NAME) final String id,
+    public Response getTags(@PathParam(ID_PARAM_NAME) final String paymentIdString,
                             @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                             @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
-                            @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException {
-        return super.getTags(UUID.fromString(id), auditMode, includedDeleted, context.createContext(request));
+                            @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException, PaymentApiException {
+        final UUID paymentId = UUID.fromString(paymentIdString);
+        final TenantContext tenantContext = context.createContext(request);
+        final Payment payment = paymentApi.getPayment(paymentId, false, tenantContext);
+        return super.getTags(payment.getAccountId(), paymentId, auditMode, includedDeleted, tenantContext);
     }
 
     @POST
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
index 4e4a22f..eeb33e6 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
@@ -74,6 +74,7 @@ import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.api.TagUserApi;
 import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.TenantContext;
 import com.ning.billing.util.userrequest.CompletionUserRequestBase;
 
 import com.google.inject.Inject;
@@ -114,7 +115,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
                                    @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException {
         final UUID uuid = UUID.fromString(subscriptionId);
         final Subscription subscription = subscriptionApi.getSubscriptionForEntitlementId(uuid, context.createContext(request));
-        final SubscriptionJson json = new SubscriptionJson(subscription, null, null, null);
+        final SubscriptionJson json = new SubscriptionJson(subscription, null, null);
         return Response.status(Status.OK).entity(json).build();
     }
 
@@ -429,11 +430,14 @@ public class SubscriptionResource extends JaxRsResourceBase {
     @GET
     @Path("/{subscriptionId:" + UUID_PATTERN + "}/" + TAGS)
     @Produces(APPLICATION_JSON)
-    public Response getTags(@PathParam(ID_PARAM_NAME) final String id,
+    public Response getTags(@PathParam(ID_PARAM_NAME) final String subscriptionIdString,
                             @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                             @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
-                            @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException {
-        return super.getTags(UUID.fromString(id), auditMode, includedDeleted, context.createContext(request));
+                            @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException, SubscriptionApiException {
+        final UUID subscriptionId = UUID.fromString(subscriptionIdString);
+        final TenantContext tenantContext = context.createContext(request);
+        final Subscription subscription = subscriptionApi.getSubscriptionForEntitlementId(subscriptionId, tenantContext);
+        return super.getTags(subscription.getAccountId(), subscriptionId, auditMode, includedDeleted, tenantContext);
     }
 
     @POST
diff --git a/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAuditLogJson.java b/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAuditLogJson.java
index 30a7eea..d123f56 100644
--- a/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAuditLogJson.java
+++ b/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAuditLogJson.java
@@ -22,10 +22,12 @@ import org.joda.time.DateTime;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.ning.billing.ObjectType;
 import com.ning.billing.jaxrs.JaxrsTestSuiteNoDB;
 import com.ning.billing.util.audit.AuditLog;
 import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.audit.DefaultAuditLog;
+import com.ning.billing.util.audit.dao.AuditLogModelDao;
 import com.ning.billing.util.dao.EntityAudit;
 import com.ning.billing.util.dao.TableName;
 
@@ -67,7 +69,7 @@ public class TestAuditLogJson extends JaxrsTestSuiteNoDB {
         final ChangeType changeType = ChangeType.DELETE;
         final EntityAudit entityAudit = new EntityAudit(tableName, recordId, changeType, null);
 
-        final AuditLog auditLog = new DefaultAuditLog(entityAudit, callContext);
+        final AuditLog auditLog = new DefaultAuditLog(new AuditLogModelDao(entityAudit, callContext), ObjectType.ACCOUNT_EMAIL, UUID.randomUUID());
 
         final AuditLogJson auditLogJson = new AuditLogJson(auditLog);
         Assert.assertEquals(auditLogJson.getChangeType(), changeType.toString());
diff --git a/server/src/main/java/com/ning/billing/server/modules/DBIProvider.java b/server/src/main/java/com/ning/billing/server/modules/DBIProvider.java
index 81d747f..7891ebb 100644
--- a/server/src/main/java/com/ning/billing/server/modules/DBIProvider.java
+++ b/server/src/main/java/com/ning/billing/server/modules/DBIProvider.java
@@ -27,6 +27,7 @@ import org.skife.jdbi.v2.tweak.TransactionHandler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.ning.billing.util.dao.AuditLogModelDaoMapper;
 import com.ning.billing.util.dao.DateTimeArgumentFactory;
 import com.ning.billing.util.dao.DateTimeZoneArgumentFactory;
 import com.ning.billing.util.dao.EnumArgumentFactory;
@@ -71,6 +72,7 @@ public class DBIProvider implements Provider<DBI> {
         dbi.registerArgumentFactory(new LocalDateArgumentFactory());
         dbi.registerArgumentFactory(new EnumArgumentFactory());
         dbi.registerMapper(new UuidMapper());
+        dbi.registerMapper(new AuditLogModelDaoMapper());
 
         if (sqlLog != null) {
             dbi.setSQLLog(sqlLog);
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 6f629b8..fd57844 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
@@ -16,44 +16,24 @@
 
 package com.ning.billing.util.audit.api;
 
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
-import javax.annotation.Nullable;
 import javax.inject.Inject;
 
 import com.ning.billing.ObjectType;
-import com.ning.billing.entitlement.api.Subscription;
-import com.ning.billing.entitlement.api.SubscriptionBundle;
-import com.ning.billing.entitlement.api.SubscriptionEvent;
-import com.ning.billing.invoice.api.Invoice;
-import com.ning.billing.invoice.api.InvoiceItem;
-import com.ning.billing.invoice.api.InvoicePayment;
-import com.ning.billing.payment.api.Payment;
-import com.ning.billing.payment.api.Refund;
 import com.ning.billing.util.api.AuditLevel;
 import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.audit.AccountAuditLogs;
+import com.ning.billing.util.audit.AccountAuditLogsForObjectType;
 import com.ning.billing.util.audit.AuditLog;
-import com.ning.billing.util.audit.AuditLogsForAccount;
-import com.ning.billing.util.audit.AuditLogsForBundles;
-import com.ning.billing.util.audit.AuditLogsForInvoicePayments;
-import com.ning.billing.util.audit.AuditLogsForInvoices;
-import com.ning.billing.util.audit.AuditLogsForPayments;
-import com.ning.billing.util.audit.AuditLogsForRefunds;
-import com.ning.billing.util.audit.DefaultAuditLogsForBundles;
-import com.ning.billing.util.audit.DefaultAuditLogsForInvoicePayments;
-import com.ning.billing.util.audit.DefaultAuditLogsForInvoices;
-import com.ning.billing.util.audit.DefaultAuditLogsForPayments;
-import com.ning.billing.util.audit.DefaultAuditLogsForRefunds;
+import com.ning.billing.util.audit.DefaultAccountAuditLogs;
+import com.ning.billing.util.audit.DefaultAccountAuditLogsForObjectType;
 import com.ning.billing.util.audit.dao.AuditDao;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.TenantContext;
 import com.ning.billing.util.dao.TableName;
 
-import com.google.common.base.Predicate;
-import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
 
 public class DefaultAuditUserApi implements AuditUserApi {
@@ -68,77 +48,28 @@ public class DefaultAuditUserApi implements AuditUserApi {
     }
 
     @Override
-    public AuditLogsForAccount getAuditLogsForAccount(final UUID accountId, final AuditLevel auditLevel, final TenantContext context) {
-        return new DefaultAuditLogsForAccount(getAuditLogs(accountId, ObjectType.ACCOUNT, auditLevel, context));
-    }
-
-    @Override
-    public AuditLogsForBundles getAuditLogsForBundles(final List<SubscriptionBundle> 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>>();
-        final Map<UUID, List<AuditLog>> subscriptionEventsAuditLogs = new HashMap<UUID, List<AuditLog>>();
-        for (final SubscriptionBundle bundle : bundles) {
-            bundlesAuditLogs.put(bundle.getId(), getAuditLogs(bundle.getId(), ObjectType.BUNDLE, auditLevel, context));
-            for (final Subscription cur : bundle.getSubscriptions()) {
-
-                final ImmutableList<SubscriptionEvent> events = ImmutableList.<SubscriptionEvent>copyOf(Collections2.filter(bundle.getTimeline().getSubscriptionEvents(), new Predicate<SubscriptionEvent>() {
-                    @Override
-                    public boolean apply(@Nullable final SubscriptionEvent input) {
-                        return input.getEntitlementId().equals(cur.getId());
-                    }
-                }));
-                subscriptionsAuditLogs.put(cur.getId(), getAuditLogs(cur.getId(), ObjectType.SUBSCRIPTION, auditLevel, context));
-                for (final SubscriptionEvent event : events) {
-                    subscriptionEventsAuditLogs.put(event.getId(), getAuditLogs(event.getId(), event.getSubscriptionEventType().getObjectType(), auditLevel, context));
-                }
-            }
-        }
-
-        return new DefaultAuditLogsForBundles(bundlesAuditLogs, subscriptionsAuditLogs, subscriptionEventsAuditLogs);
-    }
-
-    @Override
-    public AuditLogsForInvoicePayments getAuditLogsForInvoicePayments(final List<InvoicePayment> invoicePayments, final AuditLevel auditLevel, final TenantContext context) {
-        final Map<UUID, List<AuditLog>> invoicePaymentsAuditLogs = new HashMap<UUID, List<AuditLog>>();
-        for (final InvoicePayment invoicePayment : invoicePayments) {
-            invoicePaymentsAuditLogs.put(invoicePayment.getId(), getAuditLogs(invoicePayment.getId(), ObjectType.INVOICE_PAYMENT, auditLevel, context));
-        }
-
-        return new DefaultAuditLogsForInvoicePayments(invoicePaymentsAuditLogs);
-    }
-
-    @Override
-    public AuditLogsForRefunds getAuditLogsForRefunds(final List<Refund> refunds, final AuditLevel auditLevel, final TenantContext context) {
-        final Map<UUID, List<AuditLog>> refundsAuditLogs = new HashMap<UUID, List<AuditLog>>();
-        for (final Refund refund : refunds) {
-            refundsAuditLogs.put(refund.getId(), getAuditLogs(refund.getId(), ObjectType.REFUND, auditLevel, context));
+    public AccountAuditLogs getAccountAuditLogs(final UUID accountId, final AuditLevel auditLevel, final TenantContext tenantContext) {
+        // Optimization - bail early
+        if (AuditLevel.NONE.equals(auditLevel)) {
+            return new DefaultAccountAuditLogs(accountId);
         }
 
-        return new DefaultAuditLogsForRefunds(refundsAuditLogs);
+        return auditDao.getAuditLogsForAccountRecordId(auditLevel, internalCallContextFactory.createInternalTenantContext(accountId, tenantContext));
     }
 
     @Override
-    public AuditLogsForPayments getAuditLogsForPayments(final List<Payment> payments, final AuditLevel auditLevel, final TenantContext context) {
-        final Map<UUID, List<AuditLog>> paymentsAuditLogs = new HashMap<UUID, List<AuditLog>>();
-        for (final Payment payment : payments) {
-            paymentsAuditLogs.put(payment.getId(), getAuditLogs(payment.getId(), ObjectType.PAYMENT, auditLevel, context));
+    public AccountAuditLogsForObjectType getAccountAuditLogs(final UUID accountId, final ObjectType objectType, final AuditLevel auditLevel, final TenantContext tenantContext) {
+        // Optimization - bail early
+        if (AuditLevel.NONE.equals(auditLevel)) {
+            return new DefaultAccountAuditLogsForObjectType(auditLevel);
         }
 
-        return new DefaultAuditLogsForPayments(paymentsAuditLogs);
-    }
-
-    @Override
-    public AuditLogsForInvoices getAuditLogsForInvoices(final List<Invoice> invoices, final AuditLevel auditLevel, final TenantContext context) {
-        final Map<UUID, List<AuditLog>> invoiceAuditLogs = new HashMap<UUID, List<AuditLog>>();
-        final Map<UUID, List<AuditLog>> invoiceItemsAuditLogs = new HashMap<UUID, List<AuditLog>>();
-        for (final Invoice invoice : invoices) {
-            invoiceAuditLogs.put(invoice.getId(), getAuditLogs(invoice.getId(), ObjectType.INVOICE, auditLevel, context));
-            for (final InvoiceItem invoiceItem : invoice.getInvoiceItems()) {
-                invoiceItemsAuditLogs.put(invoiceItem.getId(), getAuditLogs(invoiceItem.getId(), ObjectType.INVOICE_ITEM, auditLevel, context));
-            }
+        final TableName tableName = getTableNameFromObjectType(objectType);
+        if (tableName == null) {
+            return new DefaultAccountAuditLogsForObjectType(auditLevel);
         }
 
-        return new DefaultAuditLogsForInvoices(invoiceAuditLogs, invoiceItemsAuditLogs);
+        return auditDao.getAuditLogsForAccountRecordId(tableName, auditLevel, internalCallContextFactory.createInternalTenantContext(accountId, tenantContext));
     }
 
     @Override
diff --git a/util/src/main/java/com/ning/billing/util/audit/dao/AuditDao.java b/util/src/main/java/com/ning/billing/util/audit/dao/AuditDao.java
index ab88cbd..294eda3 100644
--- a/util/src/main/java/com/ning/billing/util/audit/dao/AuditDao.java
+++ b/util/src/main/java/com/ning/billing/util/audit/dao/AuditDao.java
@@ -19,12 +19,20 @@ package com.ning.billing.util.audit.dao;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.util.api.AuditLevel;
 import com.ning.billing.util.audit.AuditLog;
-import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.util.audit.DefaultAccountAuditLogs;
+import com.ning.billing.util.audit.DefaultAccountAuditLogsForObjectType;
 import com.ning.billing.util.dao.TableName;
 
 public interface AuditDao {
 
+    // Make sure to consume all or call close() when done for release the connection
+    public DefaultAccountAuditLogs getAuditLogsForAccountRecordId(AuditLevel auditLevel, InternalTenantContext context);
+
+    // Make sure to consume all or call close() when done for release the connection
+    public DefaultAccountAuditLogsForObjectType getAuditLogsForAccountRecordId(TableName tableName, AuditLevel auditLevel, InternalTenantContext context);
+
     public List<AuditLog> getAuditLogsForId(TableName tableName, UUID objectId, AuditLevel auditLevel, InternalTenantContext context);
 }
diff --git a/util/src/main/java/com/ning/billing/util/audit/dao/DefaultAuditDao.java b/util/src/main/java/com/ning/billing/util/audit/dao/DefaultAuditDao.java
index 1aa55e6..b49a8b7 100644
--- a/util/src/main/java/com/ning/billing/util/audit/dao/DefaultAuditDao.java
+++ b/util/src/main/java/com/ning/billing/util/audit/dao/DefaultAuditDao.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.util.audit.dao;
 
+import java.util.Iterator;
 import java.util.List;
 import java.util.UUID;
 
@@ -23,12 +24,16 @@ import javax.inject.Inject;
 
 import org.skife.jdbi.v2.IDBI;
 
+import com.ning.billing.ObjectType;
+import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.clock.Clock;
 import com.ning.billing.util.api.AuditLevel;
 import com.ning.billing.util.audit.AuditLog;
 import com.ning.billing.util.audit.ChangeType;
+import com.ning.billing.util.audit.DefaultAccountAuditLogs;
+import com.ning.billing.util.audit.DefaultAccountAuditLogsForObjectType;
+import com.ning.billing.util.audit.DefaultAuditLog;
 import com.ning.billing.util.cache.CacheControllerDispatcher;
-import com.ning.billing.callcontext.InternalTenantContext;
-import com.ning.billing.clock.Clock;
 import com.ning.billing.util.dao.NonEntityDao;
 import com.ning.billing.util.dao.NonEntitySqlDao;
 import com.ning.billing.util.dao.TableName;
@@ -37,7 +42,10 @@ import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
 import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
 import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 
+import com.google.common.base.Function;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Lists;
 
 public class DefaultAuditDao implements AuditDao {
 
@@ -51,6 +59,76 @@ public class DefaultAuditDao implements AuditDao {
     }
 
     @Override
+    public DefaultAccountAuditLogs getAuditLogsForAccountRecordId(final AuditLevel auditLevel, final InternalTenantContext context) {
+        final UUID accountId = nonEntitySqlDao.getIdFromObject(context.getAccountRecordId(), TableName.ACCOUNT.getTableName());
+
+        // Lazy evaluate records to minimize the memory footprint (these can yield a lot of results)
+        // We usually always want to wrap our queries in an EntitySqlDaoTransactionWrapper... except here.
+        // Since we want to stream the results out, we don't want to auto-commit when this method returns.
+        final EntitySqlDao auditSqlDao = transactionalSqlDao.onDemand(EntitySqlDao.class);
+        final Iterator<AuditLogModelDao> auditLogsForAccountRecordId = auditSqlDao.getAuditLogsForAccountRecordId(context);
+        final Iterator<AuditLog> allAuditLogs = buildAuditLogsFromModelDao(auditLogsForAccountRecordId);
+
+        return new DefaultAccountAuditLogs(accountId, auditLevel, allAuditLogs);
+    }
+
+    @Override
+    public DefaultAccountAuditLogsForObjectType getAuditLogsForAccountRecordId(final TableName tableName, final AuditLevel auditLevel, final InternalTenantContext context) {
+        final String actualTableName;
+        if (tableName.hasHistoryTable()) {
+            actualTableName = tableName.getHistoryTableName().name(); // upper cased
+        } else {
+            actualTableName = tableName.getTableName();
+        }
+
+        // Lazy evaluate records to minimize the memory footprint (these can yield a lot of results)
+        // We usually always want to wrap our queries in an EntitySqlDaoTransactionWrapper... except here.
+        // Since we want to stream the results out, we don't want to auto-commit when this method returns.
+        final EntitySqlDao auditSqlDao = transactionalSqlDao.onDemand(EntitySqlDao.class);
+        final Iterator<AuditLogModelDao> auditLogsForTableNameAndAccountRecordId = auditSqlDao.getAuditLogsForTableNameAndAccountRecordId(actualTableName, context);
+        final Iterator<AuditLog> allAuditLogs = buildAuditLogsFromModelDao(auditLogsForTableNameAndAccountRecordId);
+
+        return new DefaultAccountAuditLogsForObjectType(auditLevel, allAuditLogs);
+    }
+
+    private Iterator<AuditLog> buildAuditLogsFromModelDao(final Iterator<AuditLogModelDao> auditLogsForAccountRecordId) {
+        return Iterators.<AuditLogModelDao, AuditLog>transform(auditLogsForAccountRecordId,
+                                                               new Function<AuditLogModelDao, AuditLog>() {
+                                                                   @Override
+                                                                   public AuditLog apply(final AuditLogModelDao input) {
+                                                                       // If input is for e.g. TAG_DEFINITION_HISTORY, retrieve TAG_DEFINITIONS
+                                                                       // For tables without history, e.g. TENANT, originalTableNameForHistoryTableName will be null
+                                                                       final TableName originalTableNameForHistoryTableName = findTableNameForHistoryTableName(input.getTableName());
+
+                                                                       final ObjectType objectType;
+                                                                       final UUID auditedEntityId;
+                                                                       if (originalTableNameForHistoryTableName != null) {
+                                                                           // input point to a history entry
+                                                                           objectType = originalTableNameForHistoryTableName.getObjectType();
+                                                                           auditedEntityId = nonEntitySqlDao.getIdFromHistoryRecordId(input.getTargetRecordId(),
+                                                                                                                                      originalTableNameForHistoryTableName.getTableName(),
+                                                                                                                                      input.getTableName().getTableName());
+                                                                       } else {
+                                                                           objectType = input.getTableName().getObjectType();
+                                                                           auditedEntityId = nonEntitySqlDao.getIdFromObject(input.getTargetRecordId(), input.getTableName().getTableName());
+                                                                       }
+
+                                                                       return new DefaultAuditLog(input, objectType, auditedEntityId);
+                                                                   }
+
+                                                                   private TableName findTableNameForHistoryTableName(final TableName historyTableName) {
+                                                                       for (final TableName tableName : TableName.values()) {
+                                                                           if (historyTableName.equals(tableName.getHistoryTableName())) {
+                                                                               return tableName;
+                                                                           }
+                                                                       }
+
+                                                                       return null;
+                                                                   }
+                                                               });
+    }
+
+    @Override
     public List<AuditLog> getAuditLogsForId(final TableName tableName, final UUID objectId, final AuditLevel auditLevel, final InternalTenantContext context) {
         if (tableName.hasHistoryTable()) {
             return doGetAuditLogsViaHistoryForId(tableName, objectId, auditLevel, context);
@@ -64,7 +142,7 @@ public class DefaultAuditDao implements AuditDao {
         if (recordId == null) {
             return ImmutableList.<AuditLog>of();
         } else {
-            return getAuditLogsForRecordId(tableName, recordId, auditLevel, context);
+            return getAuditLogsForRecordId(tableName, objectId, recordId, auditLevel, context);
         }
     }
 
@@ -78,32 +156,44 @@ public class DefaultAuditDao implements AuditDao {
         final List<AuditLog> allAuditLogs = transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<AuditLog>>() {
             @Override
             public List<AuditLog> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                return entitySqlDaoWrapperFactory.become(EntitySqlDao.class).getAuditLogsViaHistoryForTargetRecordId(historyTableName.name(),
-                                                                                                                     historyTableName.getTableName().toLowerCase(),
-                                                                                                                     targetRecordId,
-                                                                                                                     context);
+                final List<AuditLogModelDao> auditLogsViaHistoryForTargetRecordId = entitySqlDaoWrapperFactory.become(EntitySqlDao.class).getAuditLogsViaHistoryForTargetRecordId(historyTableName.name(),
+                                                                                                                                                                                  historyTableName.getTableName().toLowerCase(),
+                                                                                                                                                                                  targetRecordId,
+                                                                                                                                                                                  context);
+                return buildAuditLogsFromModelDao(auditLogsViaHistoryForTargetRecordId, tableName.getObjectType(), objectId);
             }
         });
-        return buildAuditLogs(auditLevel, allAuditLogs);
+        return filterAuditLogs(auditLevel, allAuditLogs);
     }
 
-    private List<AuditLog> getAuditLogsForRecordId(final TableName tableName, final Long targetRecordId, final AuditLevel auditLevel, final InternalTenantContext context) {
+    private List<AuditLog> getAuditLogsForRecordId(final TableName tableName, final UUID auditedEntityId, final Long targetRecordId, final AuditLevel auditLevel, final InternalTenantContext context) {
         final List<AuditLog> allAuditLogs = transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<AuditLog>>() {
             @Override
             public List<AuditLog> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                return entitySqlDaoWrapperFactory.become(EntitySqlDao.class).getAuditLogsForTargetRecordId(tableName.name(),
-                                                                                                           targetRecordId,
-                                                                                                           context);
+                final List<AuditLogModelDao> auditLogsForTargetRecordId = entitySqlDaoWrapperFactory.become(EntitySqlDao.class).getAuditLogsForTargetRecordId(tableName.name(),
+                                                                                                                                                              targetRecordId,
+                                                                                                                                                              context);
+                return buildAuditLogsFromModelDao(auditLogsForTargetRecordId, tableName.getObjectType(), auditedEntityId);
             }
         });
-        return buildAuditLogs(auditLevel, allAuditLogs);
+        return filterAuditLogs(auditLevel, allAuditLogs);
+    }
+
+    private List<AuditLog> buildAuditLogsFromModelDao(final List<AuditLogModelDao> auditLogsForAccountRecordId, final ObjectType objectType, final UUID auditedEntityId) {
+        return Lists.<AuditLogModelDao, AuditLog>transform(auditLogsForAccountRecordId,
+                                                           new Function<AuditLogModelDao, AuditLog>() {
+                                                               @Override
+                                                               public AuditLog apply(final AuditLogModelDao input) {
+                                                                   return new DefaultAuditLog(input, objectType, auditedEntityId);
+                                                               }
+                                                           });
     }
 
-    private List<AuditLog> buildAuditLogs(final AuditLevel auditLevel, final List<AuditLog> auditLogs) {
+    private List<AuditLog> filterAuditLogs(final AuditLevel auditLevel, final List<AuditLog> auditLogs) {
         // TODO Do the filtering in the query
         if (AuditLevel.FULL.equals(auditLevel)) {
             return auditLogs;
-        } else if (AuditLevel.MINIMAL.equals(auditLevel) && auditLogs.size() > 0) {
+        } else if (AuditLevel.MINIMAL.equals(auditLevel) && !auditLogs.isEmpty()) {
             if (ChangeType.INSERT.equals(auditLogs.get(0).getChangeType())) {
                 return ImmutableList.<AuditLog>of(auditLogs.get(0));
             } else {
diff --git a/util/src/main/java/com/ning/billing/util/audit/DefaultAccountAuditLogs.java b/util/src/main/java/com/ning/billing/util/audit/DefaultAccountAuditLogs.java
new file mode 100644
index 0000000..ef1f691
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/audit/DefaultAccountAuditLogs.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2010-2013 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.audit;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import com.ning.billing.ObjectType;
+import com.ning.billing.util.api.AuditLevel;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.AbstractIterator;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
+
+public class DefaultAccountAuditLogs implements AccountAuditLogs {
+
+    private final UUID accountId;
+    private final AuditLevel auditLevel;
+    private final Collection<AuditLog> accountAuditLogs;
+
+    private final Map<ObjectType, DefaultAccountAuditLogsForObjectType> auditLogsCache = new HashMap<ObjectType, DefaultAccountAuditLogsForObjectType>();
+
+    public DefaultAccountAuditLogs(final UUID accountId) {
+        this(accountId, AuditLevel.NONE, Iterators.<AuditLog>emptyIterator());
+    }
+
+    public DefaultAccountAuditLogs(final UUID accountId, final AuditLevel auditLevel, final Iterator<AuditLog> accountAuditLogsOrderedByTableName) {
+        this.accountId = accountId;
+        this.auditLevel = auditLevel;
+        // TODO pierre - lame, we should be smarter to avoid loading all entries in memory. It's a bit tricky though...
+        this.accountAuditLogs = ImmutableList.<AuditLog>copyOf(accountAuditLogsOrderedByTableName);
+    }
+
+    public void close() {
+        // Make sure to go through the results to close the connection
+        // no-op for now, see TODO above
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForAccount() {
+        return getAuditLogs(ObjectType.BUNDLE).getAuditLogs(accountId);
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForBundle(final UUID bundleId) {
+        return getAuditLogs(ObjectType.BUNDLE).getAuditLogs(bundleId);
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForSubscription(final UUID subscriptionId) {
+        return getAuditLogs(ObjectType.SUBSCRIPTION).getAuditLogs(subscriptionId);
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForSubscriptionEvent(final UUID subscriptionEventId) {
+        return getAuditLogs(ObjectType.SUBSCRIPTION_EVENT).getAuditLogs(subscriptionEventId);
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForInvoice(final UUID invoiceId) {
+        return getAuditLogs(ObjectType.INVOICE).getAuditLogs(invoiceId);
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForInvoiceItem(final UUID invoiceItemId) {
+        return getAuditLogs(ObjectType.INVOICE_ITEM).getAuditLogs(invoiceItemId);
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForPayment(final UUID paymentId) {
+        return getAuditLogs(ObjectType.PAYMENT).getAuditLogs(paymentId);
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForRefund(final UUID refundId) {
+        return getAuditLogs(ObjectType.REFUND).getAuditLogs(refundId);
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForChargeback(final UUID chargebackId) {
+        return getAuditLogs(ObjectType.INVOICE_PAYMENT).getAuditLogs(chargebackId);
+    }
+
+    @Override
+    public AccountAuditLogsForObjectType getAuditLogs(final ObjectType objectType) {
+        if (auditLogsCache.get(objectType) == null) {
+            auditLogsCache.put(objectType, new DefaultAccountAuditLogsForObjectType(auditLevel, new ObjectTypeFilter(objectType, accountAuditLogs.iterator())));
+        }
+
+        // Should never be null
+        return auditLogsCache.get(objectType);
+    }
+
+    private final class ObjectTypeFilter extends AbstractIterator<AuditLog> {
+
+        private boolean hasSeenObjectType = false;
+
+        private final ObjectType objectType;
+        private final Iterator<AuditLog> accountAuditLogs;
+
+        private ObjectTypeFilter(final ObjectType objectType, final Iterator<AuditLog> accountAuditLogs) {
+            this.objectType = objectType;
+            this.accountAuditLogs = accountAuditLogs;
+        }
+
+        @Override
+        protected AuditLog computeNext() {
+            while (accountAuditLogs.hasNext()) {
+                final AuditLog element = accountAuditLogs.next();
+                if (predicate.apply(element)) {
+                    hasSeenObjectType = true;
+                    return element;
+                } else if (hasSeenObjectType) {
+                    // Optimization trick: audit log records are ordered first by table name
+                    // (hence object type) - when we are done and we switch to another ObjectType,
+                    // we are guaranteed there is nothing left to do
+                    return endOfData();
+                }
+            }
+
+            return endOfData();
+        }
+
+        private final Predicate<AuditLog> predicate = new Predicate<AuditLog>() {
+            @Override
+            public boolean apply(final AuditLog auditLog) {
+                return objectType.equals(auditLog.getAuditedObjectType());
+            }
+        };
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/audit/DefaultAccountAuditLogsForObjectType.java b/util/src/main/java/com/ning/billing/util/audit/DefaultAccountAuditLogsForObjectType.java
new file mode 100644
index 0000000..108b170
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/audit/DefaultAccountAuditLogsForObjectType.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2010-2013 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.audit;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import com.ning.billing.util.api.AuditLevel;
+import com.ning.billing.util.customfield.ShouldntHappenException;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
+
+public class DefaultAccountAuditLogsForObjectType implements AccountAuditLogsForObjectType {
+
+    private final Map<UUID, List<AuditLog>> auditLogsCache;
+
+    private final AuditLevel auditLevel;
+    private final Iterator<AuditLog> allAuditLogsForObjectType;
+
+    public DefaultAccountAuditLogsForObjectType(final AuditLevel auditLevel) {
+        this(auditLevel, Iterators.<AuditLog>emptyIterator());
+    }
+
+    public DefaultAccountAuditLogsForObjectType(final AuditLevel auditLevel, final Iterator<AuditLog> allAuditLogsForObjectType) {
+        this.auditLevel = auditLevel;
+        this.auditLogsCache = new HashMap<UUID, List<AuditLog>>();
+        this.allAuditLogsForObjectType = allAuditLogsForObjectType;
+    }
+
+    // Used by DefaultAccountAuditLogs
+    void initializeIfNeeded(final UUID objectId) {
+        if (auditLogsCache.get(objectId) == null) {
+            auditLogsCache.put(objectId, new LinkedList<AuditLog>());
+        }
+    }
+
+    public void close() {
+        // Make sure to go through the results to close the connection
+        while (allAuditLogsForObjectType.hasNext()) {
+            allAuditLogsForObjectType.next();
+        }
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogs(final UUID objectId) {
+        switch (auditLevel) {
+            case FULL:
+                // We need to go through the whole list
+                cacheAllAuditLogs();
+
+                // We went through the whole list, mark we don't have any entry for it if needed
+                initializeIfNeeded(objectId);
+
+                // Should never be null
+                return auditLogsCache.get(objectId);
+            case MINIMAL:
+                if (auditLogsCache.get(objectId) == null) {
+                    // We just want the first INSERT audit log
+                    final AuditLog candidate = Iterators.<AuditLog>tryFind(allAuditLogsForObjectType,
+                                                                           new Predicate<AuditLog>() {
+                                                                               @Override
+                                                                               public boolean apply(final AuditLog auditLog) {
+                                                                                   // As we consume the data source, cache the entries
+                                                                                   cacheAuditLog(auditLog);
+
+                                                                                   return objectId.equals(auditLog.getAuditedEntityId()) &&
+                                                                                          // Given our ordering, this should always be true for the first entry
+                                                                                          ChangeType.INSERT.equals(auditLog.getChangeType());
+                                                                               }
+                                                                           }).orNull();
+
+                    if (candidate == null) {
+                        // We went through the whole list, mark we don't have any entry for it
+                        initializeIfNeeded(objectId);
+                    }
+                }
+
+                // Should never be null
+                return auditLogsCache.get(objectId);
+            case NONE:
+                // Close the connection ASAP since we won't need it
+                close();
+                return ImmutableList.<AuditLog>of();
+            default:
+                throw new ShouldntHappenException("AuditLevel " + auditLevel + " unsupported");
+        }
+    }
+
+    private void cacheAllAuditLogs() {
+        while (allAuditLogsForObjectType.hasNext()) {
+            final AuditLog auditLog = allAuditLogsForObjectType.next();
+            cacheAuditLog(auditLog);
+        }
+    }
+
+    private void cacheAuditLog(final AuditLog auditLog) {
+        initializeIfNeeded(auditLog.getAuditedEntityId());
+        auditLogsCache.get(auditLog.getAuditedEntityId()).add(auditLog);
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("DefaultAccountAuditLogsForObjectType{");
+        sb.append("auditLogsCache=").append(auditLogsCache);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final DefaultAccountAuditLogsForObjectType that = (DefaultAccountAuditLogsForObjectType) o;
+
+        if (!auditLogsCache.equals(that.auditLogsCache)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return auditLogsCache.hashCode();
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLog.java b/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLog.java
index 0c3e07e..b2deef3 100644
--- a/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLog.java
+++ b/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2010-2012 Ning, Inc.
+ * Copyright 2010-2013 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
@@ -16,56 +16,76 @@
 
 package com.ning.billing.util.audit;
 
+import java.util.UUID;
+
 import org.joda.time.DateTime;
 
-import com.ning.billing.util.callcontext.CallContext;
-import com.ning.billing.util.dao.EntityAudit;
+import com.ning.billing.ObjectType;
+import com.ning.billing.entity.EntityBase;
+import com.ning.billing.util.audit.dao.AuditLogModelDao;
+
+public class DefaultAuditLog extends EntityBase implements AuditLog {
 
-public class DefaultAuditLog extends EntityAudit implements AuditLog {
+    private final AuditLogModelDao auditLogModelDao;
+    private final ObjectType objectType;
+    private final UUID auditedEntityId;
 
-    private final CallContext callContext;
+    public DefaultAuditLog(final AuditLogModelDao auditLogModelDao, final ObjectType objectType, final UUID auditedEntityId) {
+        super(auditLogModelDao);
+        this.auditLogModelDao = auditLogModelDao;
+        this.objectType = objectType;
+        this.auditedEntityId = auditedEntityId;
+    }
 
-    public DefaultAuditLog(final EntityAudit entityAudit, final CallContext callContext) {
-        super(entityAudit.getId(), entityAudit.getTableName(), entityAudit.getTargetRecordId(), entityAudit.getChangeType(), entityAudit.getCreatedDate());
-        this.callContext = callContext;
+    @Override
+    public UUID getAuditedEntityId() {
+        return auditedEntityId;
     }
 
+    @Override
+    public ObjectType getAuditedObjectType() {
+        return objectType;
+    }
+
+    @Override
+    public ChangeType getChangeType() {
+        return auditLogModelDao.getChangeType();
+    }
 
     @Override
     public String getUserName() {
-        return callContext.getUserName();
+        return auditLogModelDao.getCallContext().getUserName();
     }
 
     @Override
     public DateTime getCreatedDate() {
-        return callContext.getCreatedDate();
+        return auditLogModelDao.getCallContext().getCreatedDate();
     }
 
     @Override
     public String getReasonCode() {
-        return callContext.getReasonCode();
+        return auditLogModelDao.getCallContext().getReasonCode();
     }
 
     @Override
     public String getUserToken() {
-        if (callContext.getUserToken() == null) {
+        if (auditLogModelDao.getCallContext().getUserToken() == null) {
             return null;
         } else {
-            return callContext.getUserToken().toString();
+            return auditLogModelDao.getCallContext().getUserToken().toString();
         }
     }
 
     @Override
     public String getComment() {
-        return callContext.getComments();
+        return auditLogModelDao.getCallContext().getComments();
     }
 
     @Override
     public String toString() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append("DefaultAuditLog {");
-        sb.append(super.toString());
-        sb.append(", callcontext=").append(callContext);
+        final StringBuilder sb = new StringBuilder("DefaultAuditLog{");
+        sb.append("auditLogModelDao=").append(auditLogModelDao);
+        sb.append(", auditedEntityId=").append(auditedEntityId);
         sb.append('}');
         return sb.toString();
     }
@@ -84,7 +104,10 @@ public class DefaultAuditLog extends EntityAudit implements AuditLog {
 
         final DefaultAuditLog that = (DefaultAuditLog) o;
 
-        if (callContext != null ? !callContext.equals(that.callContext) : that.callContext != null) {
+        if (auditLogModelDao != null ? !auditLogModelDao.equals(that.auditLogModelDao) : that.auditLogModelDao != null) {
+            return false;
+        }
+        if (auditedEntityId != null ? !auditedEntityId.equals(that.auditedEntityId) : that.auditedEntityId != null) {
             return false;
         }
 
@@ -94,7 +117,8 @@ public class DefaultAuditLog extends EntityAudit implements AuditLog {
     @Override
     public int hashCode() {
         int result = super.hashCode();
-        result = 31 * result + (callContext != null ? callContext.hashCode() : 0);
+        result = 31 * result + (auditLogModelDao != null ? auditLogModelDao.hashCode() : 0);
+        result = 31 * result + (auditedEntityId != null ? auditedEntityId.hashCode() : 0);
         return result;
     }
 }
diff --git a/util/src/main/java/com/ning/billing/util/dao/AuditSqlDao.java b/util/src/main/java/com/ning/billing/util/dao/AuditSqlDao.java
index d9f9b83..7f91aac 100644
--- a/util/src/main/java/com/ning/billing/util/dao/AuditSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/dao/AuditSqlDao.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.util.dao;
 
+import java.util.Iterator;
 import java.util.List;
 
 import org.skife.jdbi.v2.sqlobject.Bind;
@@ -23,11 +24,11 @@ import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.customizers.Define;
-import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
+import org.skife.jdbi.v2.sqlobject.customizers.FetchSize;
 
 import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.callcontext.InternalTenantContext;
-import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.audit.dao.AuditLogModelDao;
 import com.ning.billing.util.cache.Cachable;
 import com.ning.billing.util.cache.Cachable.CacheType;
 import com.ning.billing.util.cache.CachableKey;
@@ -39,11 +40,11 @@ import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
  * which is good enough: the cache will always get at least the initial CREATION audit log entry, which is the one
  * we really care about (both for Analytics and for Kaui's endpoints). Besides, we do cache invalidation properly
  * on our own node (see EntitySqlDaoWrapperInvocationHandler).
- *
+ * <p/>
  * Note 2: in the queries below, tableName always refers to the TableName enum, not the actual table name (TableName.getTableName()).
  */
 @EntitySqlDaoStringTemplate("/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg")
-@RegisterMapper(AuditLogMapper.class)
+// Note: @RegisterMapper annotation won't work here as we build the SqlObject via EntitySqlDao (annotations won't be inherited for JDBI)
 public interface AuditSqlDao {
 
     @SqlUpdate
@@ -51,15 +52,28 @@ public interface AuditSqlDao {
                                            @BindBean final InternalCallContext context);
 
     @SqlQuery
+    // Magic value to force MySQL to stream from the database
+    // See http://dev.mysql.com/doc/refman/5.0/en/connector-j-reference-implementation-notes.html (ResultSet)
+    @FetchSize(Integer.MIN_VALUE)
+    public Iterator<AuditLogModelDao> getAuditLogsForAccountRecordId(@BindBean final InternalTenantContext context);
+
+    @SqlQuery
+    // Magic value to force MySQL to stream from the database
+    // See http://dev.mysql.com/doc/refman/5.0/en/connector-j-reference-implementation-notes.html (ResultSet)
+    @FetchSize(Integer.MIN_VALUE)
+    public Iterator<AuditLogModelDao> getAuditLogsForTableNameAndAccountRecordId(@Bind("tableName") final String tableName,
+                                                                                 @BindBean final InternalTenantContext context);
+
+    @SqlQuery
     @Cachable(CacheType.AUDIT_LOG)
-    public List<AuditLog> getAuditLogsForTargetRecordId(@CachableKey(1) @Bind("tableName") final String tableName,
-                                                        @CachableKey(2) @Bind("targetRecordId") final long targetRecordId,
-                                                        @BindBean final InternalTenantContext context);
+    public List<AuditLogModelDao> getAuditLogsForTargetRecordId(@CachableKey(1) @Bind("tableName") final String tableName,
+                                                                @CachableKey(2) @Bind("targetRecordId") final long targetRecordId,
+                                                                @BindBean final InternalTenantContext context);
 
     @SqlQuery
     @Cachable(CacheType.AUDIT_LOG_VIA_HISTORY)
-    public List<AuditLog> getAuditLogsViaHistoryForTargetRecordId(@CachableKey(1) @Bind("tableName") final String historyTableName, /* Uppercased - used to find entries in audit_log table */
-                                                                  @CachableKey(2) @Define("historyTableName") final String actualHistoryTableName, /* Actual table name, used in the inner join query */
-                                                                  @CachableKey(3) @Bind("targetRecordId") final long targetRecordId,
-                                                                  @BindBean final InternalTenantContext context);
+    public List<AuditLogModelDao> getAuditLogsViaHistoryForTargetRecordId(@CachableKey(1) @Bind("tableName") final String historyTableName, /* Uppercased - used to find entries in audit_log table */
+                                                                          @CachableKey(2) @Define("historyTableName") final String actualHistoryTableName, /* Actual table name, used in the inner join query */
+                                                                          @CachableKey(3) @Bind("targetRecordId") final long targetRecordId,
+                                                                          @BindBean final InternalTenantContext context);
 }
diff --git a/util/src/main/java/com/ning/billing/util/dao/NonEntitySqlDao.java b/util/src/main/java/com/ning/billing/util/dao/NonEntitySqlDao.java
index 36d7811..07e88c1 100644
--- a/util/src/main/java/com/ning/billing/util/dao/NonEntitySqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/dao/NonEntitySqlDao.java
@@ -54,4 +54,9 @@ public interface NonEntitySqlDao extends Transactional<NonEntitySqlDao>, CloseMe
 
     @SqlQuery
     public Long getHistoryTargetRecordId(@Bind("recordId") Long recordId, @Define("tableName") final String tableName);
+
+    @SqlQuery
+    public UUID getIdFromHistoryRecordId(@Bind("historyRecordId") Long historyRecordId,
+                                         @Define("tableName") String tableName,
+                                         @Define("historyTableName") String historyTableName);
 }
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoStringTemplate.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoStringTemplate.java
index 143f3bc..e70cfcd 100644
--- a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoStringTemplate.java
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoStringTemplate.java
@@ -77,9 +77,11 @@ public @interface EntitySqlDaoStringTemplate {
                                     final ParameterizedType type = (ParameterizedType) sqlObjectType.getGenericInterfaces()[i];
                                     for (int j = 0; j < type.getActualTypeArguments().length; j++) {
                                         final Type modelType = type.getActualTypeArguments()[j];
-                                        final Class modelClazz = (Class) modelType;
-                                        if (Entity.class.isAssignableFrom(modelClazz)) {
-                                            query.registerMapper(new LowerToCamelBeanMapperFactory(modelClazz));
+                                        if (modelType instanceof Class) {
+                                            final Class modelClazz = (Class) modelType;
+                                            if (Entity.class.isAssignableFrom(modelClazz)) {
+                                                query.registerMapper(new LowerToCamelBeanMapperFactory(modelClazz));
+                                            }
                                         }
                                     }
                                 }
diff --git a/util/src/main/resources/com/ning/billing/util/dao/NonEntitySqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/dao/NonEntitySqlDao.sql.stg
index 1969615..6b2b6af 100644
--- a/util/src/main/resources/com/ning/billing/util/dao/NonEntitySqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/dao/NonEntitySqlDao.sql.stg
@@ -73,3 +73,13 @@ where record_id = :recordId
 ;
 >>
 
+getIdFromHistoryRecordId(tableName, historyTableName) ::= <<
+select
+  t.id
+from <tableName> t
+join <historyTableName> ht on ht.target_record_id = t.record_id
+where ht.record_id = :historyRecordId
+limit 1
+;
+>>
+
diff --git a/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg
index b89eb6d..6214825 100644
--- a/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg
@@ -341,6 +341,29 @@ values (
 ;
 >>
 
+getAuditLogsForAccountRecordId() ::= <<
+select
+  <auditTableFields("t.")>
+from <auditTableName()> t
+where <accountRecordIdField("t.")> = :accountRecordId
+<andCheckSoftDeletionWithComma("t.")>
+<AND_CHECK_TENANT("t.")>
+order by t.table_name, <recordIdField("t.")> ASC
+;
+>>
+
+getAuditLogsForTableNameAndAccountRecordId() ::= <<
+select
+  <auditTableFields("t.")>
+from <auditTableName()> t
+where <accountRecordIdField("t.")> = :accountRecordId
+and t.table_name = :tableName
+<andCheckSoftDeletionWithComma("t.")>
+<AND_CHECK_TENANT("t.")>
+<defaultOrderBy("t.")>
+;
+>>
+
 getAuditLogsForTargetRecordId() ::= <<
 select
   <auditTableFields("t.")>
diff --git a/util/src/test/java/com/ning/billing/dbi/DBIProvider.java b/util/src/test/java/com/ning/billing/dbi/DBIProvider.java
index 824e50e..2c30cba 100644
--- a/util/src/test/java/com/ning/billing/dbi/DBIProvider.java
+++ b/util/src/test/java/com/ning/billing/dbi/DBIProvider.java
@@ -22,6 +22,7 @@ import org.skife.jdbi.v2.DBI;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.tweak.transactions.SerializableTransactionRunner;
 
+import com.ning.billing.util.dao.AuditLogModelDaoMapper;
 import com.ning.billing.util.dao.DateTimeArgumentFactory;
 import com.ning.billing.util.dao.DateTimeZoneArgumentFactory;
 import com.ning.billing.util.dao.EnumArgumentFactory;
@@ -53,6 +54,7 @@ public class DBIProvider implements Provider<IDBI> {
         dbi.registerArgumentFactory(new LocalDateArgumentFactory());
         dbi.registerArgumentFactory(new EnumArgumentFactory());
         dbi.registerMapper(new UuidMapper());
+        dbi.registerMapper(new AuditLogModelDaoMapper());
 
         // Restart transactions in case of deadlocks
         dbi.setTransactionHandler(new SerializableTransactionRunner());
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 0861ac9..a50d378 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
@@ -16,35 +16,19 @@
 
 package com.ning.billing.util.audit.api;
 
-import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
-import org.mockito.Mockito;
 import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 import com.ning.billing.ObjectType;
-import com.ning.billing.entitlement.api.SubscriptionBundle;
-import com.ning.billing.subscription.api.timeline.BundleBaseTimeline;
-import com.ning.billing.invoice.api.Invoice;
-import com.ning.billing.invoice.api.InvoiceItem;
-import com.ning.billing.invoice.api.InvoicePayment;
-import com.ning.billing.payment.api.Payment;
-import com.ning.billing.payment.api.Refund;
 import com.ning.billing.util.api.AuditLevel;
 import com.ning.billing.util.audit.AuditLog;
-import com.ning.billing.util.audit.AuditLogsForBundles;
-import com.ning.billing.util.audit.AuditLogsForInvoicePayments;
-import com.ning.billing.util.audit.AuditLogsForInvoices;
-import com.ning.billing.util.audit.AuditLogsForPayments;
-import com.ning.billing.util.audit.AuditLogsForRefunds;
 import com.ning.billing.util.audit.AuditLogsTestBase;
 import com.ning.billing.util.audit.dao.MockAuditDao;
 import com.ning.billing.util.dao.TableName;
-import com.ning.billing.util.entity.Entity;
 
 import com.google.common.collect.ImmutableList;
 
@@ -53,7 +37,6 @@ public class TestDefaultAuditUserApi extends AuditLogsTestBase {
     private List<AuditLog> auditLogs;
     private List<UUID> objectIds;
 
-
     @Override
     @BeforeClass(groups = "fast")
     public void beforeClass() throws Exception {
@@ -61,7 +44,6 @@ public class TestDefaultAuditUserApi extends AuditLogsTestBase {
         auditLogs = ImmutableList.<AuditLog>of(createAuditLog(), createAuditLog(), createAuditLog(), createAuditLog());
         objectIds = ImmutableList.<UUID>of(UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID());
 
-
         for (final TableName tableName : TableName.values()) {
             for (final UUID objectId : objectIds) {
                 for (final AuditLog auditLog : auditLogs) {
@@ -72,66 +54,6 @@ public class TestDefaultAuditUserApi extends AuditLogsTestBase {
     }
 
     @Test(groups = "fast")
-    public void testForBundles() throws Exception {
-        final List<SubscriptionBundle> bundles = new ArrayList<SubscriptionBundle>();
-        for (final UUID objectId : objectIds) {
-            final SubscriptionBundle entity = Mockito.mock(SubscriptionBundle.class);
-            Mockito.when(entity.getId()).thenReturn(objectId);
-            bundles.add(entity);
-        }
-
-        for (final AuditLevel level : AuditLevel.values()) {
-            final AuditLogsForBundles auditLogsForBundles = auditUserApi.getAuditLogsForBundles(bundles, level, callContext);
-            verifyAuditLogs(auditLogsForBundles.getBundlesAuditLogs(), level);
-        }
-    }
-
-    @Test(groups = "fast")
-    public void testForInvoicePayments() throws Exception {
-        final List<InvoicePayment> invoicePayments = createMocks(InvoicePayment.class);
-
-        for (final AuditLevel level : AuditLevel.values()) {
-            final AuditLogsForInvoicePayments auditLogsForInvoicePayments = auditUserApi.getAuditLogsForInvoicePayments(invoicePayments, level, callContext);
-            verifyAuditLogs(auditLogsForInvoicePayments.getInvoicePaymentsAuditLogs(), level);
-        }
-    }
-
-    @Test(groups = "fast")
-    public void testForRefunds() throws Exception {
-        final List<Refund> refunds = createMocks(Refund.class);
-
-        for (final AuditLevel level : AuditLevel.values()) {
-            final AuditLogsForRefunds auditLogsForRefunds = auditUserApi.getAuditLogsForRefunds(refunds, level, callContext);
-            verifyAuditLogs(auditLogsForRefunds.getRefundsAuditLogs(), level);
-        }
-    }
-
-    @Test(groups = "fast")
-    public void testForPayments() throws Exception {
-        final List<Payment> payments = createMocks(Payment.class);
-
-        for (final AuditLevel level : AuditLevel.values()) {
-            final AuditLogsForPayments auditLogsForPayments = auditUserApi.getAuditLogsForPayments(payments, level, callContext);
-            verifyAuditLogs(auditLogsForPayments.getPaymentsAuditLogs(), level);
-        }
-    }
-
-    @Test(groups = "fast")
-    public void testForInvoices() throws Exception {
-        final List<Invoice> invoices = createMocks(Invoice.class);
-        final List<InvoiceItem> invoiceItems = createMocks(InvoiceItem.class);
-        for (final Invoice invoice : invoices) {
-            Mockito.when(invoice.getInvoiceItems()).thenReturn(invoiceItems);
-        }
-
-        for (final AuditLevel level : AuditLevel.values()) {
-            final AuditLogsForInvoices auditLogsForInvoices = auditUserApi.getAuditLogsForInvoices(invoices, level, callContext);
-            verifyAuditLogs(auditLogsForInvoices.getInvoiceAuditLogs(), level);
-            verifyAuditLogs(auditLogsForInvoices.getInvoiceItemsAuditLogs(), level);
-        }
-    }
-
-    @Test(groups = "fast")
     public void testForObject() throws Exception {
         for (final ObjectType objectType : ObjectType.values()) {
             for (final UUID objectId : objectIds) {
@@ -147,34 +69,4 @@ public class TestDefaultAuditUserApi extends AuditLogsTestBase {
             }
         }
     }
-
-    private void verifyAuditLogs(final Map<UUID, List<AuditLog>> objectsAuditLogs, final AuditLevel level) {
-        Assert.assertEquals(objectsAuditLogs.size(), objectIds.size());
-
-        if (AuditLevel.NONE.equals(level)) {
-            for (final UUID objectId : objectIds) {
-                Assert.assertEquals(objectsAuditLogs.get(objectId).size(), 0);
-            }
-        } else if (AuditLevel.MINIMAL.equals(level)) {
-            for (final UUID objectId : objectIds) {
-                Assert.assertEquals(objectsAuditLogs.get(objectId).size(), 1);
-                Assert.assertEquals(objectsAuditLogs.get(objectId).get(0), auditLogs.get(0));
-            }
-        } else {
-            for (final UUID objectId : objectIds) {
-                Assert.assertEquals(objectsAuditLogs.get(objectId), auditLogs);
-            }
-        }
-    }
-
-    private <T extends Entity> List<T> createMocks(final Class<T> clazz) {
-        final List<T> entities = new ArrayList<T>();
-        for (final UUID objectId : objectIds) {
-            final T entity = Mockito.mock(clazz);
-            Mockito.when(entity.getId()).thenReturn(objectId);
-            entities.add(entity);
-        }
-
-        return entities;
-    }
 }
diff --git a/util/src/test/java/com/ning/billing/util/audit/dao/MockAuditDao.java b/util/src/test/java/com/ning/billing/util/audit/dao/MockAuditDao.java
index 56d2349..b6982ee 100644
--- a/util/src/test/java/com/ning/billing/util/audit/dao/MockAuditDao.java
+++ b/util/src/test/java/com/ning/billing/util/audit/dao/MockAuditDao.java
@@ -22,9 +22,11 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.util.api.AuditLevel;
 import com.ning.billing.util.audit.AuditLog;
-import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.util.audit.DefaultAccountAuditLogs;
+import com.ning.billing.util.audit.DefaultAccountAuditLogsForObjectType;
 import com.ning.billing.util.dao.TableName;
 
 import com.google.common.base.Objects;
@@ -51,6 +53,16 @@ public class MockAuditDao implements AuditDao {
     }
 
     @Override
+    public DefaultAccountAuditLogs getAuditLogsForAccountRecordId(final AuditLevel auditLevel, final InternalTenantContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public DefaultAccountAuditLogsForObjectType getAuditLogsForAccountRecordId(final TableName tableName, final AuditLevel auditLevel, final InternalTenantContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public List<AuditLog> getAuditLogsForId(final TableName tableName, final UUID objectId, final AuditLevel auditLevel, final InternalTenantContext context) {
         final Map<UUID, List<AuditLog>> auditLogsForTableName = auditLogsForTables.get(tableName);
         if (auditLogsForTableName == null) {
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 3374bfd..d8f837a 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
@@ -29,6 +29,8 @@ 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.AccountAuditLogs;
+import com.ning.billing.util.audit.AccountAuditLogsForObjectType;
 import com.ning.billing.util.audit.AuditLog;
 import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.dao.TableName;
@@ -63,6 +65,12 @@ public class TestDefaultAuditDao extends UtilTestSuiteWithEmbeddedDB {
         for (final AuditLevel level : AuditLevel.values()) {
             final List<AuditLog> auditLogs = auditDao.getAuditLogsForId(TableName.TAG, tag.getId(), level, internalCallContext);
             verifyAuditLogsForTag(auditLogs, level);
+
+            final AccountAuditLogs accountAuditLogs = auditDao.getAuditLogsForAccountRecordId(level, internalCallContext);
+            verifyAuditLogsForTag(accountAuditLogs.getAuditLogs(ObjectType.TAG).getAuditLogs(tag.getId()), level);
+
+            final AccountAuditLogsForObjectType accountAuditLogsForObjectType = auditDao.getAuditLogsForAccountRecordId(TableName.TAG, level, internalCallContext);
+            verifyAuditLogsForTag(accountAuditLogsForObjectType.getAuditLogs(tag.getId()), level);
         }
     }
 
diff --git a/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLog.java b/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLog.java
index b519df5..785b062 100644
--- a/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLog.java
+++ b/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLog.java
@@ -21,12 +21,14 @@ import java.util.UUID;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.ning.billing.ObjectType;
+import com.ning.billing.callcontext.DefaultCallContext;
+import com.ning.billing.clock.ClockMock;
 import com.ning.billing.util.UtilTestSuiteNoDB;
+import com.ning.billing.util.audit.dao.AuditLogModelDao;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallOrigin;
-import com.ning.billing.callcontext.DefaultCallContext;
 import com.ning.billing.util.callcontext.UserType;
-import com.ning.billing.clock.ClockMock;
 import com.ning.billing.util.dao.EntityAudit;
 import com.ning.billing.util.dao.TableName;
 
@@ -47,7 +49,7 @@ public class TestDefaultAuditLog extends UtilTestSuiteNoDB {
         final ClockMock clock = new ClockMock();
         final CallContext callContext = new DefaultCallContext(tenantId, userName, callOrigin, userType, userToken, clock);
 
-        final AuditLog auditLog = new DefaultAuditLog(entityAudit, callContext);
+        final AuditLog auditLog = new DefaultAuditLog(new AuditLogModelDao(entityAudit, callContext), ObjectType.ACCOUNT_EMAIL, UUID.randomUUID());
         Assert.assertEquals(auditLog.getChangeType(), changeType);
         Assert.assertNull(auditLog.getComment());
         Assert.assertNotNull(auditLog.getCreatedDate());
@@ -71,15 +73,15 @@ public class TestDefaultAuditLog extends UtilTestSuiteNoDB {
         final ClockMock clock = new ClockMock();
         final CallContext callContext = new DefaultCallContext(tenantId, userName, callOrigin, userType, userToken, clock);
 
-        final AuditLog auditLog = new DefaultAuditLog(entityAudit, callContext);
+        final AuditLogModelDao auditLog = new AuditLogModelDao(entityAudit, callContext);
         Assert.assertEquals(auditLog, auditLog);
 
-        final AuditLog sameAuditLog = new DefaultAuditLog(entityAudit, callContext);
+        final AuditLogModelDao sameAuditLog = new AuditLogModelDao(entityAudit, callContext);
         Assert.assertEquals(sameAuditLog, auditLog);
 
         clock.addMonths(1);
         final CallContext otherCallContext = new DefaultCallContext(tenantId, userName, callOrigin, userType, userToken, clock);
-        final AuditLog otherAuditLog = new DefaultAuditLog(entityAudit, otherCallContext);
+        final AuditLogModelDao otherAuditLog = new AuditLogModelDao(entityAudit, otherCallContext);
         Assert.assertNotEquals(otherAuditLog, auditLog);
     }
 }