killbill-memoizeit

Introduce new cache record_id -> object_id (requiring a key

8/26/2014 1:44:22 PM

Changes

Details

diff --git a/account/src/main/java/org/killbill/billing/account/dao/AccountEmailModelDao.java b/account/src/main/java/org/killbill/billing/account/dao/AccountEmailModelDao.java
index 5628e4a..41cd2e8 100644
--- a/account/src/main/java/org/killbill/billing/account/dao/AccountEmailModelDao.java
+++ b/account/src/main/java/org/killbill/billing/account/dao/AccountEmailModelDao.java
@@ -22,8 +22,9 @@ import org.killbill.billing.account.api.AccountEmail;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
+import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
-public class AccountEmailModelDao extends EntityBase implements EntityModelDao<AccountEmail> {
+public class AccountEmailModelDao extends EntityModelDaoBase implements EntityModelDao<AccountEmail> {
 
     private UUID accountId;
     private String email;
diff --git a/account/src/main/java/org/killbill/billing/account/dao/AccountModelDao.java b/account/src/main/java/org/killbill/billing/account/dao/AccountModelDao.java
index 4f9b770..4f8cd7d 100644
--- a/account/src/main/java/org/killbill/billing/account/dao/AccountModelDao.java
+++ b/account/src/main/java/org/killbill/billing/account/dao/AccountModelDao.java
@@ -29,8 +29,9 @@ import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
+import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
-public class AccountModelDao extends EntityBase implements EntityModelDao<Account> {
+public class AccountModelDao extends EntityModelDaoBase implements EntityModelDao<Account> {
 
     private String externalKey;
     private String email;
diff --git a/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java b/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java
index d5b1cff..01c8e9a 100644
--- a/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java
+++ b/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java
@@ -22,8 +22,6 @@ import javax.inject.Inject;
 import javax.inject.Named;
 
 import org.killbill.billing.ObjectType;
-import org.killbill.billing.account.api.Account;
-import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.entitlement.EntitlementTransitionType;
@@ -48,6 +46,8 @@ import org.killbill.billing.events.UserTagDeletionInternalEvent;
 import org.killbill.billing.lifecycle.glue.BusModule;
 import org.killbill.billing.notification.plugin.api.ExtBusEventType;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
+import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.CallOrigin;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.UserType;
@@ -71,6 +71,7 @@ public class BeatrixListener {
     private final InternalCallContextFactory internalCallContextFactory;
     private final AccountInternalApi accountApi;
     private final NonEntityDao nonEntityDao;
+    private final CacheControllerDispatcher cacheControllerDispatcher;
 
     protected final ObjectMapper objectMapper;
 
@@ -78,11 +79,13 @@ public class BeatrixListener {
     public BeatrixListener(@Named(BusModule.EXTERNAL_BUS_NAMED) final PersistentBus externalBus,
                            final InternalCallContextFactory internalCallContextFactory,
                            final AccountInternalApi accountApi,
+                           final  CacheControllerDispatcher cacheControllerDispatcher,
                            final NonEntityDao nonEntityDao) {
         this.externalBus = externalBus;
         this.internalCallContextFactory = internalCallContextFactory;
         this.accountApi = accountApi;
         this.nonEntityDao = nonEntityDao;
+        this.cacheControllerDispatcher = cacheControllerDispatcher;
         this.objectMapper = new ObjectMapper();
         objectMapper.registerModule(new JodaModule());
         objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
@@ -238,25 +241,19 @@ public class BeatrixListener {
 
             default:
         }
-        final UUID accountId = getAccountIdFromRecordId(event.getBusEventType(), objectId, context.getAccountRecordId(), context);
-        final UUID tenantId = nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT);
+        final UUID accountId = getAccountIdFromRecordId(event.getBusEventType(), objectId, context.getAccountRecordId());
+        final UUID tenantId = nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT, cacheControllerDispatcher.getCacheController(CacheType.OBJECT_ID));
 
         return eventBusType != null ?
                new DefaultBusExternalEvent(objectId, objectType, eventBusType, accountId, tenantId, context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()) :
                null;
     }
 
-    private UUID getAccountIdFromRecordId(final BusInternalEventType eventType, final UUID objectId, final Long recordId, final InternalCallContext context) {
+    private UUID getAccountIdFromRecordId(final BusInternalEventType eventType, final UUID objectId, final Long recordId) {
         // accountRecord_id is not set for ACCOUNT_CREATE event as we are in the transaction and value is known yet
         if (eventType == BusInternalEventType.ACCOUNT_CREATE) {
             return objectId;
         }
-        try {
-            final Account account = accountApi.getAccountByRecordId(recordId, context);
-            return account.getId();
-        } catch (final AccountApiException e) {
-            log.warn("Failed to retrieve acount from recordId {}", recordId);
-            return null;
-        }
+        return nonEntityDao.retrieveIdFromObject(recordId, ObjectType.ACCOUNT, cacheControllerDispatcher.getCacheController(CacheType.OBJECT_ID));
     }
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/util/SubscriptionChecker.java b/beatrix/src/test/java/org/killbill/billing/beatrix/util/SubscriptionChecker.java
index 2c0391b..1b0c6c9 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/util/SubscriptionChecker.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/util/SubscriptionChecker.java
@@ -21,6 +21,8 @@ import java.util.UUID;
 
 import javax.inject.Inject;
 
+import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
@@ -44,12 +46,14 @@ public class SubscriptionChecker {
     private final SubscriptionBaseInternalApi subscriptionApi;
     private final AuditChecker auditChecker;
     private final NonEntityDao nonEntityDao;
+    private final CacheControllerDispatcher cacheControllerDispatcher;
 
     @Inject
-    public SubscriptionChecker(final SubscriptionBaseInternalApi subscriptionApi, final AuditChecker auditChecker, final NonEntityDao nonEntityDao) {
+    public SubscriptionChecker(final SubscriptionBaseInternalApi subscriptionApi, final AuditChecker auditChecker, final NonEntityDao nonEntityDao, final CacheControllerDispatcher cacheControllerDispatcher) {
         this.subscriptionApi = subscriptionApi;
         this.auditChecker = auditChecker;
         this.nonEntityDao = nonEntityDao;
+        this.cacheControllerDispatcher = cacheControllerDispatcher;
     }
 
     public SubscriptionBaseBundle checkBundleNoAudits(final UUID bundleId, final UUID expectedAccountId, final String expectedKey, final InternalTenantContext context) throws SubscriptionBaseApiException {
@@ -61,7 +65,7 @@ public class SubscriptionChecker {
     }
 
     public SubscriptionBase checkSubscriptionCreated(final UUID subscriptionId, final InternalCallContext context) throws SubscriptionBaseApiException {
-        final UUID tenantId = nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT);
+        final UUID tenantId = nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT, cacheControllerDispatcher.getCacheController(CacheType.OBJECT_ID));
         final CallContext callContext = context.toCallContext(tenantId);
 
         final SubscriptionBase subscription = subscriptionApi.getSubscriptionFromId(subscriptionId, context);
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
index 3c96566..a96d33a 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
@@ -103,7 +103,7 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
     @Override
     public Subscription getSubscriptionForEntitlementId(final UUID entitlementId, final TenantContext context) throws SubscriptionApiException {
         final Long accountRecordId = nonEntityDao.retrieveAccountRecordIdFromObject(entitlementId, ObjectType.SUBSCRIPTION, cacheControllerDispatcher.getCacheController(CacheType.ACCOUNT_RECORD_ID));
-        final UUID accountId = nonEntityDao.retrieveIdFromObject(accountRecordId, ObjectType.ACCOUNT);
+        final UUID accountId = nonEntityDao.retrieveIdFromObject(accountRecordId, ObjectType.ACCOUNT, cacheControllerDispatcher.getCacheController(CacheType.OBJECT_ID));
 
         // Retrieve entitlements
         final AccountEntitlements accountEntitlements;
@@ -128,7 +128,7 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
     @Override
     public SubscriptionBundle getSubscriptionBundle(final UUID bundleId, final TenantContext context) throws SubscriptionApiException {
         final Long accountRecordId = nonEntityDao.retrieveAccountRecordIdFromObject(bundleId, ObjectType.BUNDLE, cacheControllerDispatcher.getCacheController(CacheType.ACCOUNT_RECORD_ID));
-        final UUID accountId = nonEntityDao.retrieveIdFromObject(accountRecordId, ObjectType.ACCOUNT);
+        final UUID accountId = nonEntityDao.retrieveIdFromObject(accountRecordId, ObjectType.ACCOUNT, cacheControllerDispatcher.getCacheController(CacheType.OBJECT_ID));
 
         final Optional<SubscriptionBundle> bundleOptional = Iterables.<SubscriptionBundle>tryFind(getSubscriptionBundlesForAccount(accountId, context),
                                                                                                   new Predicate<SubscriptionBundle>() {
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/BlockingStateModelDao.java b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/BlockingStateModelDao.java
index 6305dd1..f69435a 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/BlockingStateModelDao.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/BlockingStateModelDao.java
@@ -27,8 +27,9 @@ import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.junction.DefaultBlockingState;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
+import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
-public class BlockingStateModelDao extends EntityBase implements EntityModelDao<BlockingState>{
+public class BlockingStateModelDao extends EntityModelDaoBase implements EntityModelDao<BlockingState>{
 
     private UUID blockableId;
     private BlockingStateType type;
@@ -40,6 +41,8 @@ public class BlockingStateModelDao extends EntityBase implements EntityModelDao<
     private DateTime effectiveDate;
     private boolean isActive;
 
+    public BlockingStateModelDao() { /* For the DAO mapper */ }
+
     public BlockingStateModelDao(final UUID id, final UUID blockableId, final BlockingStateType blockingStateType, final String state, final String service, final Boolean blockChange, final Boolean blockEntitlement,
                                  final Boolean blockBilling, final DateTime effectiveDate, final boolean isActive, final DateTime createDate, final DateTime updateDate) {
         super(id, createDate, updateDate);
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/BlockingStateSqlDao.java b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/BlockingStateSqlDao.java
index bdc1179..c753ccd 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/BlockingStateSqlDao.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/BlockingStateSqlDao.java
@@ -16,34 +16,24 @@
 
 package org.killbill.billing.entitlement.dao;
 
-import java.sql.ResultSet;
-import java.sql.SQLException;
 import java.util.Date;
 import java.util.List;
 import java.util.UUID;
 
-import org.joda.time.DateTime;
-import org.skife.jdbi.v2.StatementContext;
-import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.BindBean;
-import org.skife.jdbi.v2.sqlobject.SqlQuery;
-import org.skife.jdbi.v2.sqlobject.SqlUpdate;
-import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
-import org.skife.jdbi.v2.tweak.ResultSetMapper;
-
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.entitlement.api.BlockingState;
-import org.killbill.billing.entitlement.api.BlockingStateType;
-import org.killbill.billing.junction.DefaultBlockingState;
 import org.killbill.billing.util.audit.ChangeType;
-import org.killbill.billing.util.dao.MapperBase;
 import org.killbill.billing.util.entity.dao.Audited;
 import org.killbill.billing.util.entity.dao.EntitySqlDao;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoStringTemplate;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.BindBean;
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
 
 @EntitySqlDaoStringTemplate
-@RegisterMapper(BlockingStateSqlDao.BlockingHistorySqlMapper.class)
 public interface BlockingStateSqlDao extends EntitySqlDao<BlockingStateModelDao, BlockingState> {
 
     @SqlQuery
@@ -66,37 +56,4 @@ public interface BlockingStateSqlDao extends EntitySqlDao<BlockingStateModelDao,
     @Audited(ChangeType.UPDATE)
     public void unactiveEvent(@Bind("id") String id,
                               @BindBean final InternalCallContext context);
-
-    public class BlockingHistorySqlMapper extends MapperBase implements ResultSetMapper<BlockingStateModelDao> {
-
-        @Override
-        public BlockingStateModelDao map(final int index, final ResultSet r, final StatementContext ctx)
-                throws SQLException {
-
-            final UUID id;
-            final UUID blockableId;
-            final String stateName;
-            final String service;
-            final boolean blockChange;
-            final boolean blockEntitlement;
-            final boolean blockBilling;
-            final boolean isActive;
-            final DateTime effectiveDate;
-            final DateTime createdDate;
-            final BlockingStateType type;
-
-            id = UUID.fromString(r.getString("id"));
-            blockableId = UUID.fromString(r.getString("blockable_id"));
-            stateName = r.getString("state") == null ? DefaultBlockingState.CLEAR_STATE_NAME : r.getString("state");
-            service = r.getString("service");
-            type = BlockingStateType.valueOf(r.getString("type"));
-            blockChange = r.getBoolean("block_change");
-            blockEntitlement = r.getBoolean("block_entitlement");
-            blockBilling = r.getBoolean("block_billing");
-            isActive = r.getBoolean("is_active");
-            effectiveDate = getDateTime(r, "effective_date");
-            createdDate = getDateTime(r, "created_date");
-            return new BlockingStateModelDao(id, blockableId, type, stateName, service, blockChange, blockEntitlement, blockBilling, effectiveDate, isActive, createdDate, createdDate);
-        }
-    }
 }
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java b/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java
index 0127690..ca86524 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java
@@ -34,6 +34,8 @@ import org.killbill.billing.entitlement.engine.core.EntitlementNotificationKey;
 import org.killbill.billing.entitlement.engine.core.EntitlementNotificationKeyAction;
 import org.killbill.billing.platform.api.LifecycleHandlerType;
 import org.killbill.billing.platform.api.LifecycleHandlerType.LifecycleLevel;
+import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.CallOrigin;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.UserType;
@@ -64,6 +66,7 @@ public class DefaultEntitlementService implements EntitlementService {
     private final PersistentBus eventBus;
     private final NotificationQueueService notificationQueueService;
     private final InternalCallContextFactory internalCallContextFactory;
+    private final CacheControllerDispatcher controllerDispatcher;
 
     private NotificationQueue entitlementEventQueue;
 
@@ -73,13 +76,15 @@ public class DefaultEntitlementService implements EntitlementService {
                                      final NonEntityDao nonEntityDao,
                                      final PersistentBus eventBus,
                                      final NotificationQueueService notificationQueueService,
-                                     final InternalCallContextFactory internalCallContextFactory) {
+                                     final InternalCallContextFactory internalCallContextFactory,
+                                     final CacheControllerDispatcher controllerDispatcher) {
         this.entitlementApi = entitlementApi;
         this.blockingStateDao = blockingStateDao;
         this.nonEntityDao = nonEntityDao;
         this.eventBus = eventBus;
         this.notificationQueueService = notificationQueueService;
         this.internalCallContextFactory = internalCallContextFactory;
+        this.controllerDispatcher = controllerDispatcher;
     }
 
     @Override
@@ -96,7 +101,7 @@ public class DefaultEntitlementService implements EntitlementService {
                     final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(tenantRecordId, accountRecordId, "EntitlementQueue", CallOrigin.INTERNAL, UserType.SYSTEM, fromNotificationQueueUserToken);
 
                     if (inputKey instanceof EntitlementNotificationKey) {
-                        final UUID tenantId = nonEntityDao.retrieveIdFromObject(tenantRecordId, ObjectType.TENANT);
+                        final UUID tenantId = nonEntityDao.retrieveIdFromObject(tenantRecordId, ObjectType.TENANT, controllerDispatcher.getCacheController(CacheType.OBJECT_ID));
                         processEntitlementNotification((EntitlementNotificationKey) inputKey, tenantId, internalCallContext);
                     } else if (inputKey instanceof BlockingTransitionNotificationKey) {
                         processBlockingNotification((BlockingTransitionNotificationKey) inputKey, internalCallContext);
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemModelDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemModelDao.java
index 2caf2ee..e7a3a2a 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemModelDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemModelDao.java
@@ -27,8 +27,9 @@ import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
+import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
-public class InvoiceItemModelDao extends EntityBase implements EntityModelDao<InvoiceItem> {
+public class InvoiceItemModelDao extends EntityModelDaoBase implements EntityModelDao<InvoiceItem> {
 
     private InvoiceItemType type;
     private UUID invoiceId;
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java
index e8cef2b..ae3cce6 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java
@@ -30,8 +30,9 @@ import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
+import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
-public class InvoiceModelDao extends EntityBase implements EntityModelDao<Invoice> {
+public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDao<Invoice> {
 
     private UUID accountId;
     private Integer invoiceNumber;
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentModelDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentModelDao.java
index 05e49f1..20a7c81 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentModelDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentModelDao.java
@@ -27,8 +27,9 @@ import org.killbill.billing.invoice.api.InvoicePaymentType;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
+import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
-public class InvoicePaymentModelDao extends EntityBase implements EntityModelDao<InvoicePayment> {
+public class InvoicePaymentModelDao extends EntityModelDaoBase implements EntityModelDao<InvoicePayment> {
 
     private InvoicePaymentType type;
     private UUID invoiceId;
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.java
index be618d6..3fa40da 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.java
@@ -65,7 +65,6 @@ public interface InvoicePaymentSqlDao extends EntitySqlDao<InvoicePaymentModelDa
                                       @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    @RegisterMapper(UUIDMapper.class)
     UUID getAccountIdFromInvoicePaymentId(@Bind("invoicePaymentId") final String invoicePaymentId,
                                           @BindBean final InternalTenantContext context);
 
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java
index 6b4d6d8..89a9905 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java
@@ -50,6 +50,8 @@ import org.killbill.billing.invoice.usage.SubscriptionConsumableInArrear;
 import org.killbill.billing.junction.BillingEvent;
 import org.killbill.billing.junction.BillingEventSet;
 import org.killbill.billing.usage.api.UsageUserApi;
+import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.config.InvoiceConfig;
 import org.killbill.billing.util.currency.KillBillMoney;
 import org.killbill.billing.util.dao.NonEntityDao;
@@ -72,13 +74,15 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
     private final InvoiceConfig config;
     private final UsageUserApi usageApi;
     private final NonEntityDao nonEntityDao;
+    private final CacheControllerDispatcher controllerDispatcher;
 
     @Inject
-    public DefaultInvoiceGenerator(final Clock clock, final UsageUserApi usageApi, final InvoiceConfig config, final NonEntityDao nonEntityDao) {
+    public DefaultInvoiceGenerator(final Clock clock, final UsageUserApi usageApi, final InvoiceConfig config, final NonEntityDao nonEntityDao, final CacheControllerDispatcher controllerDispatcher) {
         this.clock = clock;
         this.config = config;
         this.usageApi = usageApi;
         this.nonEntityDao = nonEntityDao;
+        this.controllerDispatcher = controllerDispatcher;
     }
 
     /*
@@ -113,7 +117,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
                                                         @Nullable final List<Invoice> existingInvoices, final LocalDate targetDate,
                                                         final InternalCallContext context) throws InvoiceApiException {
 
-        final UUID tenantId = nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT);
+        final UUID tenantId = nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT, controllerDispatcher.getCacheController(CacheType.OBJECT_ID));
         try {
 
             final List<InvoiceItem> items = Lists.newArrayList();
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
index 4cb1371..d91552c 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
@@ -70,6 +70,8 @@ import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
+import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.dao.NonEntityDao;
@@ -107,6 +109,7 @@ public class InvoiceDispatcher {
     private final PersistentBus eventBus;
     private final Clock clock;
     private final OSGIServiceRegistration<InvoicePluginApi> pluginRegistry;
+    private final CacheControllerDispatcher controllerDispatcher;
 
     @Inject
     public InvoiceDispatcher(final OSGIServiceRegistration<InvoicePluginApi> pluginRegistry,
@@ -118,7 +121,7 @@ public class InvoiceDispatcher {
                              final InvoiceNotifier invoiceNotifier,
                              final GlobalLocker locker,
                              final PersistentBus eventBus,
-                             final Clock clock) {
+                             final Clock clock, final CacheControllerDispatcher controllerDispatcher) {
         this.pluginRegistry = pluginRegistry;
         this.generator = generator;
         this.billingApi = billingApi;
@@ -130,6 +133,7 @@ public class InvoiceDispatcher {
         this.locker = locker;
         this.eventBus = eventBus;
         this.clock = clock;
+        this.controllerDispatcher = controllerDispatcher;
     }
 
     public void processSubscription(final EffectiveSubscriptionInternalEvent transition,
@@ -301,11 +305,11 @@ public class InvoiceDispatcher {
     }
 
     private TenantContext buildTenantContext(final InternalTenantContext context) {
-        return context.toTenantContext(nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT));
+        return context.toTenantContext(nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT, controllerDispatcher.getCacheController(CacheType.OBJECT_ID)));
     }
 
     private CallContext buildCallContext(final InternalCallContext context) {
-        return context.toCallContext(nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT));
+        return context.toCallContext(nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT, controllerDispatcher.getCacheController(CacheType.OBJECT_ID)));
     }
 
     private List<InvoicePluginApi> getInvoicePlugins() {
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
index 435b3d8..52a1112 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
@@ -107,7 +107,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
                 return false;
             }
         };
-        this.generator = new DefaultInvoiceGenerator(clock, null, invoiceConfig, null);
+        this.generator = new DefaultInvoiceGenerator(clock, null, invoiceConfig, null, controllerDispatcher);
     }
 
     @Test(groups = "fast")
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
index 4c0521b..cf40b6d 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
@@ -92,7 +92,7 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
         final InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
         final InvoiceDispatcher dispatcher = new InvoiceDispatcher(pluginRegistry, generator, accountApi, billingApi, subscriptionApi, invoiceDao,
                                                                    nonEntityDao, invoiceNotifier, locker, busService.getBus(),
-                                                                   clock);
+                                                                   clock, controllerDispatcher);
 
         Invoice invoice = dispatcher.processAccount(accountId, target, true, context);
         Assert.assertNotNull(invoice);
@@ -145,7 +145,7 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
         final InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
         final InvoiceDispatcher dispatcher = new InvoiceDispatcher(pluginRegistry, generator, accountApi, billingApi, subscriptionApi, invoiceDao,
                                                                    nonEntityDao, invoiceNotifier, locker, busService.getBus(),
-                                                                   clock);
+                                                                   clock, controllerDispatcher);
 
         final Invoice invoice = dispatcher.processAccount(account.getId(), new DateTime("2012-07-30T00:00:00.000Z"), false, context);
         Assert.assertNotNull(invoice);
@@ -203,7 +203,7 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
         final InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
         final InvoiceDispatcher dispatcher = new InvoiceDispatcher(pluginRegistry, generator, accountApi, billingApi, subscriptionApi, invoiceDao,
                                                                    nonEntityDao, invoiceNotifier, locker, busService.getBus(),
-                                                                   clock);
+                                                                   clock, controllerDispatcher);
 
         final Map<UUID, List<DateTime>> result = dispatcher.createNextFutureNotificationDate(Collections.singletonList(item), null, dateAndTimeZoneContext);
 
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
index b2935ff..6fd8ae8 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
@@ -70,6 +70,7 @@ import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.currency.KillBillMoney;
@@ -150,6 +151,7 @@ public class TestInvoiceHelper {
     private final InternalCallContext internalCallContext;
     private final NonEntityDao nonEntityDao;
     private final InternalCallContextFactory internalCallContextFactory;
+    private final CacheControllerDispatcher cacheControllerDispatcher;
 
     // Low level SqlDao used by the tests to directly insert rows
     private final InvoicePaymentSqlDao invoicePaymentSqlDao;
@@ -159,7 +161,7 @@ public class TestInvoiceHelper {
     public TestInvoiceHelper(final OSGIServiceRegistration<InvoicePluginApi> pluginRegistry, final InvoiceGenerator generator, final IDBI dbi,
                              final BillingInternalApi billingApi, final AccountInternalApi accountApi, final AccountUserApi accountUserApi, final SubscriptionBaseInternalApi subscriptionApi, final BusService busService,
                              final InvoiceDao invoiceDao, final GlobalLocker locker, final Clock clock, final NonEntityDao nonEntityDao, final InternalCallContext internalCallContext,
-                             final InternalCallContextFactory internalCallContextFactory) {
+                             final InternalCallContextFactory internalCallContextFactory, final CacheControllerDispatcher cacheControllerDispatcher) {
         this.pluginRegistry = pluginRegistry;
         this.generator = generator;
         this.billingApi = billingApi;
@@ -175,6 +177,7 @@ public class TestInvoiceHelper {
         this.internalCallContextFactory = internalCallContextFactory;
         this.invoiceItemSqlDao = dbi.onDemand(InvoiceItemSqlDao.class);
         this.invoicePaymentSqlDao = dbi.onDemand(InvoicePaymentSqlDao.class);
+        this.cacheControllerDispatcher = cacheControllerDispatcher;
     }
 
     public UUID generateRegularInvoice(final Account account, final DateTime targetDate, final CallContext callContext) throws Exception {
@@ -196,7 +199,7 @@ public class TestInvoiceHelper {
         final InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
         final InvoiceDispatcher dispatcher = new InvoiceDispatcher(pluginRegistry, generator, accountApi, billingApi, subscriptionApi,
                                                                    invoiceDao, nonEntityDao, invoiceNotifier, locker, busService.getBus(),
-                                                                   clock);
+                                                                   clock, cacheControllerDispatcher);
 
         Invoice invoice = dispatcher.processAccount(account.getId(), targetDate, true, internalCallContext);
         Assert.assertNotNull(invoice);
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java b/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
index 1e8b674..cf3e203 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
@@ -27,20 +27,15 @@ import javax.inject.Named;
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.joda.time.Period;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountInternalApi;
-import org.killbill.bus.api.PersistentBus;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.ProductCategory;
-import org.killbill.clock.Clock;
 import org.killbill.billing.entitlement.api.BlockingApiException;
 import org.killbill.billing.entitlement.api.BlockingStateType;
 import org.killbill.billing.entitlement.api.Entitlement;
@@ -62,6 +57,8 @@ import org.killbill.billing.overdue.notification.OverdueCheckNotifier;
 import org.killbill.billing.overdue.notification.OverduePoster;
 import org.killbill.billing.tag.TagInternalApi;
 import org.killbill.billing.util.api.TagApiException;
+import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.billing.util.email.DefaultEmailSender;
 import org.killbill.billing.util.email.EmailApiException;
@@ -69,6 +66,10 @@ import org.killbill.billing.util.email.EmailConfig;
 import org.killbill.billing.util.email.EmailSender;
 import org.killbill.billing.util.tag.ControlTagType;
 import org.killbill.billing.util.tag.Tag;
+import org.killbill.bus.api.PersistentBus;
+import org.killbill.clock.Clock;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
@@ -90,6 +91,7 @@ public class OverdueStateApplicator {
     private final TagInternalApi tagApi;
     private final EmailSender emailSender;
     private final NonEntityDao nonEntityDao;
+    private final CacheControllerDispatcher controllerDispatcher;
 
     @Inject
     public OverdueStateApplicator(final BlockingInternalApi accessApi,
@@ -101,7 +103,8 @@ public class OverdueStateApplicator {
                                   final EmailConfig config,
                                   final PersistentBus bus,
                                   final NonEntityDao nonEntityDao,
-                                  final TagInternalApi tagApi) {
+                                  final TagInternalApi tagApi,
+                                  final CacheControllerDispatcher controllerDispatcher) {
 
         this.blockingApi = accessApi;
         this.accountApi = accountApi;
@@ -113,6 +116,7 @@ public class OverdueStateApplicator {
         this.nonEntityDao = nonEntityDao;
         this.emailSender = new DefaultEmailSender(config);
         this.bus = bus;
+        this.controllerDispatcher = controllerDispatcher;
     }
 
     public void apply(final OverdueStateSet overdueStateSet, final BillingState billingState,
@@ -294,7 +298,7 @@ public class OverdueStateApplicator {
             final List<Entitlement> toBeCancelled = new LinkedList<Entitlement>();
             computeEntitlementsToCancel(account, toBeCancelled, context);
 
-            final UUID tenantId = nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT);
+            final UUID tenantId = nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT, controllerDispatcher.getCacheController(CacheType.OBJECT_ID));
             for (final Entitlement cur : toBeCancelled) {
                 try {
                     cur.cancelEntitlementWithDateOverrideBillingPolicy(new LocalDate(clock.getUTCNow(), account.getTimeZone()), actionPolicy, context.toCallContext(tenantId));
@@ -311,7 +315,7 @@ public class OverdueStateApplicator {
     }
 
     private void computeEntitlementsToCancel(final Account account, final List<Entitlement> result, final InternalTenantContext context) throws EntitlementApiException {
-        final UUID tenantId = nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT);
+        final UUID tenantId = nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT, controllerDispatcher.getCacheController(CacheType.OBJECT_ID));
         final List<Entitlement> allEntitlementsForAccountId = entitlementApi.getAllEntitlementsForAccountId(account.getId(), context.toTenantContext(tenantId));
         // Entitlement is smart enough and will cancel the associated add-ons. See also discussion in https://github.com/killbill/killbill/issues/94
         final Collection<Entitlement> allEntitlementsButAddonsForAccountId = Collections2.<Entitlement>filter(allEntitlementsForAccountId,
diff --git a/payment/src/main/java/org/killbill/billing/payment/bus/InvoiceHandler.java b/payment/src/main/java/org/killbill/billing/payment/bus/InvoiceHandler.java
index cc236ce..a5fcee7 100644
--- a/payment/src/main/java/org/killbill/billing/payment/bus/InvoiceHandler.java
+++ b/payment/src/main/java/org/killbill/billing/payment/bus/InvoiceHandler.java
@@ -30,12 +30,12 @@ import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.events.InvoiceCreationInternalEvent;
-import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentApiException;
-import org.killbill.billing.payment.api.PaymentOptions;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.control.InvoicePaymentControlPluginApi;
 import org.killbill.billing.payment.core.PluginControlledPaymentProcessor;
+import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.CallOrigin;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
@@ -44,7 +44,6 @@ import org.killbill.billing.util.dao.NonEntityDao;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.collect.ImmutableList;
 import com.google.common.eventbus.Subscribe;
 import com.google.inject.Inject;
 
@@ -54,6 +53,7 @@ public class InvoiceHandler {
     private final InternalCallContextFactory internalCallContextFactory;
     private final PluginControlledPaymentProcessor pluginControlledPaymentProcessor;
     private final NonEntityDao nonEntityDao;
+    private final CacheControllerDispatcher controllerDispatcher;
 
     private static final Logger log = LoggerFactory.getLogger(InvoiceHandler.class);
 
@@ -61,11 +61,13 @@ public class InvoiceHandler {
     public InvoiceHandler(final AccountInternalApi accountApi,
                           final PluginControlledPaymentProcessor pluginControlledPaymentProcessor,
                           final NonEntityDao nonEntityDao,
-                          final InternalCallContextFactory internalCallContextFactory) {
+                          final InternalCallContextFactory internalCallContextFactory,
+                          final CacheControllerDispatcher controllerDispatcher) {
         this.accountApi = accountApi;
         this.internalCallContextFactory = internalCallContextFactory;
         this.pluginControlledPaymentProcessor = pluginControlledPaymentProcessor;
         this.nonEntityDao = nonEntityDao;
+        this.controllerDispatcher = controllerDispatcher;
     }
 
     @Subscribe
@@ -83,7 +85,7 @@ public class InvoiceHandler {
             final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, event.getInvoiceId().toString(), false);
             properties.add(prop1);
 
-            final CallContext callContext = internalContext.toCallContext(nonEntityDao.retrieveIdFromObject(internalContext.getTenantRecordId(), ObjectType.TENANT));
+            final CallContext callContext = internalContext.toCallContext(nonEntityDao.retrieveIdFromObject(internalContext.getTenantRecordId(), ObjectType.TENANT, controllerDispatcher.getCacheController(CacheType.OBJECT_ID)));
 
             final BigDecimal amountToBePaid = null; // We let the plugin compute how much should be paid
             pluginControlledPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amountToBePaid, account.getCurrency(), UUID.randomUUID().toString(), UUID.randomUUID().toString(),
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/Janitor.java b/payment/src/main/java/org/killbill/billing/payment/core/Janitor.java
index f8b3748..4a1e516 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/Janitor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/Janitor.java
@@ -17,7 +17,6 @@
 
 package org.killbill.billing.payment.core;
 
-import java.util.Collections;
 import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.CountDownLatch;
@@ -46,6 +45,8 @@ import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
 import org.killbill.billing.payment.dao.PluginPropertySerializer;
 import org.killbill.billing.payment.dao.PluginPropertySerializer.PluginPropertySerializerException;
 import org.killbill.billing.payment.glue.PaymentModule;
+import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.CallOrigin;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
@@ -78,9 +79,9 @@ public class Janitor {
     private final NonEntityDao nonEntityDao;
     private final PluginControlledPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner;
     private final RetryStateMachineHelper retrySMHelper;
+    private final CacheControllerDispatcher controllerDispatcher;
 
     private volatile boolean isStopped;
-    private CountDownLatch shutdownLatch;
 
     @Inject
     public Janitor(final AccountInternalApi accountInternalApi,
@@ -91,7 +92,8 @@ public class Janitor {
                    final InternalCallContextFactory internalCallContextFactory,
                    final PluginControlledPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner,
                    @Named(PaymentModule.JANITOR_EXECUTOR_NAMED) final ScheduledExecutorService janitorExecutor,
-                   final RetryStateMachineHelper retrySMHelper) {
+                   final RetryStateMachineHelper retrySMHelper,
+                   final CacheControllerDispatcher controllerDispatcher) {
         this.accountInternalApi = accountInternalApi;
         this.paymentDao = paymentDao;
         this.clock = clock;
@@ -101,6 +103,7 @@ public class Janitor {
         this.internalCallContextFactory = internalCallContextFactory;
         this.pluginControlledPaymentAutomatonRunner = pluginControlledPaymentAutomatonRunner;
         this.retrySMHelper = retrySMHelper;
+        this.controllerDispatcher = controllerDispatcher;
     }
 
     public void start() {
@@ -221,7 +224,7 @@ public class Janitor {
         public void doIteration(final PaymentAttemptModelDao attempt) {
             // STEPH seems a bit insane??
             final InternalTenantContext tenantContext = internalCallContextFactory.createInternalTenantContext(attempt.getAccountId(), attempt.getId(), ObjectType.PAYMENT_ATTEMPT);
-            final UUID tenantId = nonEntityDao.retrieveIdFromObject(tenantContext.getTenantRecordId(), ObjectType.TENANT);
+            final UUID tenantId = nonEntityDao.retrieveIdFromObject(tenantContext.getTenantRecordId(), ObjectType.TENANT, controllerDispatcher.getCacheController(CacheType.OBJECT_ID));
             final CallContext callContext = new DefaultCallContext(tenantId, "AttemptCompletionJanitorTask", CallOrigin.INTERNAL, UserType.SYSTEM, UUID.randomUUID(), clock);
             final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(attempt.getAccountId(), callContext);
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java
index c9231d0..37ad942 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java
@@ -43,6 +43,7 @@ import org.killbill.billing.payment.plugin.api.HostedPaymentPageFormDescriptor;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
 import org.killbill.billing.tag.TagInternalApi;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.config.PaymentConfig;
 import org.killbill.billing.util.dao.NonEntityDao;
@@ -75,8 +76,9 @@ public class PaymentGatewayProcessor extends ProcessorBase {
                                    final GlobalLocker locker,
                                    final PaymentConfig paymentConfig,
                                    @Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor,
-                                   final Clock clock) {
-        super(pluginRegistry, accountUserApi, paymentDao, nonEntityDao, tagUserApi, locker, executor, invoiceApi, clock);
+                                   final Clock clock,
+                                   final CacheControllerDispatcher controllerDispatcher) {
+        super(pluginRegistry, accountUserApi, paymentDao, nonEntityDao, tagUserApi, locker, executor, invoiceApi, clock, controllerDispatcher);
         final long paymentPluginTimeoutSec = TimeUnit.SECONDS.convert(paymentConfig.getPaymentPluginTimeout().getPeriod(), paymentConfig.getPaymentPluginTimeout().getUnit());
         this.paymentPluginFormDispatcher = new PluginDispatcher<HostedPaymentPageFormDescriptor>(paymentPluginTimeoutSec, executor);
         this.paymentPluginNotificationDispatcher = new PluginDispatcher<GatewayNotification>(paymentPluginTimeoutSec, executor);
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java
index e479bae..71b5434 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java
@@ -50,6 +50,7 @@ import org.killbill.billing.payment.provider.DefaultNoOpPaymentMethodPlugin;
 import org.killbill.billing.payment.provider.DefaultPaymentMethodInfoPlugin;
 import org.killbill.billing.payment.provider.ExternalPaymentProviderPlugin;
 import org.killbill.billing.tag.TagInternalApi;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.dao.NonEntityDao;
@@ -86,8 +87,9 @@ public class PaymentMethodProcessor extends ProcessorBase {
                                   final TagInternalApi tagUserApi,
                                   final GlobalLocker locker,
                                   @Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor,
-                                  final Clock clock) {
-        super(pluginRegistry, accountInternalApi, paymentDao, nonEntityDao, tagUserApi, locker, executor, invoiceApi, clock);
+                                  final Clock clock,
+                                  final CacheControllerDispatcher controllerDispatcher) {
+        super(pluginRegistry, accountInternalApi, paymentDao, nonEntityDao, tagUserApi, locker, executor, invoiceApi, clock, controllerDispatcher);
     }
 
     public UUID addPaymentMethod(final String paymentMethodExternalKey, final String paymentPluginServiceName, final Account account,
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
index 4ecddab..377da1f 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
@@ -54,6 +54,7 @@ import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
 import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
 import org.killbill.billing.tag.TagInternalApi;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
@@ -99,8 +100,9 @@ public class PaymentProcessor extends ProcessorBase {
                             @Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor,
                             final PaymentAutomatonRunner paymentAutomatonRunner,
                             final PaymentStateMachineHelper paymentSMHelper,
-                            final Clock clock) {
-        super(pluginRegistry, accountUserApi, paymentDao, nonEntityDao, tagUserApi, locker, executor, invoiceApi, clock);
+                            final Clock clock,
+                            final CacheControllerDispatcher controllerDispatcher) {
+        super(pluginRegistry, accountUserApi, paymentDao, nonEntityDao, tagUserApi, locker, executor, invoiceApi, clock, controllerDispatcher);
         this.paymentSMHelper = paymentSMHelper;
         this.internalCallContextFactory = internalCallContextFactory;
         this.paymentAutomatonRunner = paymentAutomatonRunner;
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PluginControlledPaymentProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PluginControlledPaymentProcessor.java
index 513caac..9f5fbda 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PluginControlledPaymentProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PluginControlledPaymentProcessor.java
@@ -46,9 +46,10 @@ import org.killbill.billing.payment.dao.PluginPropertySerializer;
 import org.killbill.billing.payment.dao.PluginPropertySerializer.PluginPropertySerializerException;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.tag.TagInternalApi;
+import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.dao.NonEntityDao;
-import org.killbill.bus.api.PersistentBus;
 import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLocker;
 
@@ -61,6 +62,7 @@ public class PluginControlledPaymentProcessor extends ProcessorBase {
 
     private final PluginControlledPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner;
     private final RetryStateMachineHelper retrySMHelper;
+    private final CacheControllerDispatcher controllerDispatcher;
 
     @Inject
     public PluginControlledPaymentProcessor(final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
@@ -73,10 +75,12 @@ public class PluginControlledPaymentProcessor extends ProcessorBase {
                                             @Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor,
                                             final PluginControlledPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner,
                                             final RetryStateMachineHelper retrySMHelper,
-                                            final Clock clock) {
-        super(pluginRegistry, accountInternalApi, paymentDao, nonEntityDao, tagUserApi, locker, executor, invoiceApi, clock);
+                                            final Clock clock,
+                                            final CacheControllerDispatcher controllerDispatcher) {
+        super(pluginRegistry, accountInternalApi, paymentDao, nonEntityDao, tagUserApi, locker, executor, invoiceApi, clock, controllerDispatcher);
         this.retrySMHelper = retrySMHelper;
         this.pluginControlledPaymentAutomatonRunner = pluginControlledPaymentAutomatonRunner;
+        this.controllerDispatcher = controllerDispatcher;
     }
 
     public Payment createAuthorization(final boolean isApiPayment, final Account account, final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency, final String paymentExternalKey, final String transactionExternalKey,
@@ -204,7 +208,7 @@ public class PluginControlledPaymentProcessor extends ProcessorBase {
 
             final Iterable<PluginProperty> pluginProperties = PluginPropertySerializer.deserialize(attempt.getPluginProperties());
             final Account account = accountInternalApi.getAccountById(attempt.getAccountId(), internalCallContext);
-            final UUID tenantId = nonEntityDao.retrieveIdFromObject(internalCallContext.getTenantRecordId(), ObjectType.TENANT);
+            final UUID tenantId = nonEntityDao.retrieveIdFromObject(internalCallContext.getTenantRecordId(), ObjectType.TENANT, controllerDispatcher.getCacheController(CacheType.OBJECT_ID));
             final CallContext callContext = internalCallContext.toCallContext(tenantId);
 
             final State state = retrySMHelper.getState(attempt.getStateName());
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java b/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
index 02295f3..3cc6a94 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
@@ -43,6 +43,8 @@ import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcher
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.tag.TagInternalApi;
 import org.killbill.billing.util.api.TagApiException;
+import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.billing.util.globallocker.LockerType;
@@ -72,6 +74,7 @@ public abstract class ProcessorBase {
     protected final NonEntityDao nonEntityDao;
     protected final TagInternalApi tagInternalApi;
     protected final Clock clock;
+    protected final CacheControllerDispatcher controllerDispatcher;
 
     protected static final Logger log = LoggerFactory.getLogger(ProcessorBase.class);
     protected final InvoiceInternalApi invoiceApi;
@@ -84,7 +87,8 @@ public abstract class ProcessorBase {
                          final GlobalLocker locker,
                          final ExecutorService executor,
                          final InvoiceInternalApi invoiceApi,
-                         final Clock clock) {
+                         final Clock clock,
+                         final CacheControllerDispatcher controllerDispatcher) {
         this.pluginRegistry = pluginRegistry;
         this.accountInternalApi = accountInternalApi;
         this.paymentDao = paymentDao;
@@ -94,6 +98,7 @@ public abstract class ProcessorBase {
         this.tagInternalApi = tagInternalApi;
         this.invoiceApi = invoiceApi;
         this.clock = clock;
+        this.controllerDispatcher = controllerDispatcher;
     }
 
     protected boolean isAccountAutoPayOff(final UUID accountId, final InternalTenantContext context) {
@@ -152,7 +157,7 @@ public abstract class ProcessorBase {
     }
 
     protected TenantContext buildTenantContext(final InternalTenantContext context) {
-        return context.toTenantContext(nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT));
+        return context.toTenantContext(nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT, controllerDispatcher.getCacheController(CacheType.OBJECT_ID)));
     }
 
     protected void validateUniqueTransactionExternalKey(@Nullable final String transactionExternalKey, final InternalTenantContext tenantContext) throws PaymentApiException {
@@ -174,9 +179,9 @@ public abstract class ProcessorBase {
         }
     }
 
-
     // TODO Rename - there is no lock!
     public interface WithAccountLockCallback<PluginDispatcherReturnType, ExceptionType extends Exception> {
+
         public PluginDispatcherReturnType doOperation() throws ExceptionType;
     }
 
@@ -186,7 +191,6 @@ public abstract class ProcessorBase {
         private final String accountExternalKey;
         private final WithAccountLockCallback<PluginDispatcherReturnType<ReturnType>, ExceptionType> callback;
 
-
         public CallableWithAccountLock(final GlobalLocker locker,
                                        final String accountExternalKey,
                                        final WithAccountLockCallback<PluginDispatcherReturnType<ReturnType>, ExceptionType> callback) {
@@ -203,7 +207,7 @@ public abstract class ProcessorBase {
 
     public static class WithAccountLock<ReturnType, ExceptionType extends Exception> {
 
-        public PluginDispatcherReturnType<ReturnType> processAccountWithLock(final GlobalLocker locker, final String accountExternalKey, final WithAccountLockCallback<PluginDispatcherReturnType<ReturnType>, ExceptionType > callback)
+        public PluginDispatcherReturnType<ReturnType> processAccountWithLock(final GlobalLocker locker, final String accountExternalKey, final WithAccountLockCallback<PluginDispatcherReturnType<ReturnType>, ExceptionType> callback)
                 throws ExceptionType, LockFailedException {
             GlobalLock lock = null;
             try {
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptModelDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptModelDao.java
index 815a6d2..fdc1354 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptModelDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptModelDao.java
@@ -29,8 +29,9 @@ import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.entity.Entity;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
+import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
-public class PaymentAttemptModelDao extends EntityBase implements EntityModelDao<Entity> {
+public class PaymentAttemptModelDao extends EntityModelDaoBase implements EntityModelDao<Entity> {
 
     private UUID accountId;
     private UUID paymentMethodId;
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentMethodModelDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentMethodModelDao.java
index 38b9adb..7e284de 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentMethodModelDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentMethodModelDao.java
@@ -26,10 +26,11 @@ import org.killbill.billing.payment.api.PaymentMethod;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
+import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
 import com.google.common.base.Objects;
 
-public class PaymentMethodModelDao extends EntityBase implements EntityModelDao<PaymentMethod> {
+public class PaymentMethodModelDao extends EntityModelDaoBase implements EntityModelDao<PaymentMethod> {
 
     private String externalKey;
     private UUID accountId;
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentModelDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentModelDao.java
index 35d3f4b..1fffb07 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentModelDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentModelDao.java
@@ -25,10 +25,11 @@ import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
+import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
 import com.google.common.base.Objects;
 
-public class PaymentModelDao extends EntityBase implements EntityModelDao<Payment> {
+public class PaymentModelDao extends EntityModelDaoBase implements EntityModelDao<Payment> {
 
     public static final Integer INVALID_PAYMENT_NUMBER = new Integer(-17);
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentTransactionModelDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentTransactionModelDao.java
index cb88241..1f88513 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentTransactionModelDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentTransactionModelDao.java
@@ -29,10 +29,11 @@ import org.killbill.billing.payment.api.TransactionStatus;
 import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
+import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
 import com.google.common.base.Objects;
 
-public class PaymentTransactionModelDao extends EntityBase implements EntityModelDao<PaymentTransaction> {
+public class PaymentTransactionModelDao extends EntityModelDaoBase implements EntityModelDao<PaymentTransaction> {
 
     private UUID attemptId;
     private UUID paymentId;
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java
index 43c2e13..5acf9df 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java
@@ -200,7 +200,8 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
                                                          executor,
                                                          runner,
                                                          retrySMHelper,
-                                                         clock);
+                                                         clock,
+                                                         cacheControllerDispatcher);
 
     }
 
diff --git a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java
index f3a3b48..69fa238 100644
--- a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java
@@ -34,6 +34,8 @@ import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
 import org.killbill.billing.payment.retry.DefaultRetryService;
 import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.killbill.billing.util.cache.CacheController;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.config.PaymentConfig;
 import org.killbill.bus.api.PersistentBus;
 import org.killbill.commons.profiling.Profiling;
@@ -76,6 +78,8 @@ public abstract class PaymentTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     protected PluginControlledPaymentAutomatonRunner retryablePaymentAutomatonRunner;
     @Inject
     protected DefaultRetryService retryService;
+    @Inject
+    protected CacheControllerDispatcher cacheControllerDispatcher;
 
     @Override
     protected KillbillConfigSource getConfigSource() {
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
index d82a0e1..29b1b20 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
@@ -25,6 +25,8 @@ import java.util.UUID;
 import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
+import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -80,6 +82,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
 
     private final AddonUtils addonUtils;
     private final NonEntityDao nonEntityDao;
+    private final CacheControllerDispatcher controllerDispatcher;
 
     @Inject
     public DefaultSubscriptionInternalApi(final SubscriptionDao dao,
@@ -87,10 +90,12 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
                                           final Clock clock,
                                           final CatalogService catalogService,
                                           final AddonUtils addonUtils,
-                                          final NonEntityDao nonEntityDao) {
+                                          final NonEntityDao nonEntityDao,
+                                          final CacheControllerDispatcher controllerDispatcher) {
         super(dao, apiService, clock, catalogService);
         this.addonUtils = addonUtils;
         this.nonEntityDao = nonEntityDao;
+        this.controllerDispatcher = controllerDispatcher;
     }
 
     @Override
@@ -151,7 +156,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
                                                                   plan.getProduct().getCategory().toString()));
             }
 
-            final UUID tenantId = nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT);
+            final UUID tenantId = nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT, controllerDispatcher.getCacheController(CacheType.OBJECT_ID));
             return apiService.createPlan(new SubscriptionBuilder()
                                                  .setId(UUID.randomUUID())
                                                  .setBundleId(bundleId)
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionBundleModelDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionBundleModelDao.java
index df7d34f..4b1f2c4 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionBundleModelDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionBundleModelDao.java
@@ -25,8 +25,9 @@ import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
+import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
-public class SubscriptionBundleModelDao extends EntityBase implements EntityModelDao<SubscriptionBaseBundle> {
+public class SubscriptionBundleModelDao extends EntityModelDaoBase implements EntityModelDao<SubscriptionBaseBundle> {
 
     private String externalKey;
     private UUID accountId;
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionEventModelDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionEventModelDao.java
index dde70c9..2809ea8 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionEventModelDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionEventModelDao.java
@@ -41,8 +41,9 @@ import org.killbill.billing.subscription.exceptions.SubscriptionBaseError;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
+import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
-public class SubscriptionEventModelDao extends EntityBase implements EntityModelDao<SubscriptionBaseEvent> {
+public class SubscriptionEventModelDao extends EntityModelDaoBase implements EntityModelDao<SubscriptionBaseEvent> {
 
     private long totalOrdering;
     private EventType eventType;
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionModelDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionModelDao.java
index 0e09ec5..d9d778c 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionModelDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionModelDao.java
@@ -27,8 +27,9 @@ import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
+import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
-public class SubscriptionModelDao extends EntityBase implements EntityModelDao<SubscriptionBase> {
+public class SubscriptionModelDao extends EntityModelDaoBase implements EntityModelDao<SubscriptionBase> {
 
     private UUID bundleId;
     private ProductCategory category;
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantKVModelDao.java b/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantKVModelDao.java
index 04e72a0..014c9e0 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantKVModelDao.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantKVModelDao.java
@@ -24,8 +24,9 @@ import org.killbill.billing.tenant.api.TenantKV;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
+import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
-public class TenantKVModelDao extends EntityBase implements EntityModelDao<TenantKV> {
+public class TenantKVModelDao extends EntityModelDaoBase implements EntityModelDao<TenantKV> {
 
     private String tenantKey;
     private String tenantValue;
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantModelDao.java b/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantModelDao.java
index 5cb998e..8d6c92e 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantModelDao.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantModelDao.java
@@ -24,8 +24,9 @@ import org.killbill.billing.tenant.api.Tenant;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
+import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
-public class TenantModelDao extends EntityBase implements EntityModelDao<Tenant> {
+public class TenantModelDao extends EntityModelDaoBase implements EntityModelDao<Tenant> {
 
     private String externalKey;
     private String apiKey;
diff --git a/util/src/main/java/org/killbill/billing/util/cache/AccountRecordIdCacheLoader.java b/util/src/main/java/org/killbill/billing/util/cache/AccountRecordIdCacheLoader.java
index 906f452..6c7e01e 100644
--- a/util/src/main/java/org/killbill/billing/util/cache/AccountRecordIdCacheLoader.java
+++ b/util/src/main/java/org/killbill/billing/util/cache/AccountRecordIdCacheLoader.java
@@ -30,7 +30,7 @@ import org.killbill.billing.util.dao.NonEntityDao;
 import net.sf.ehcache.loader.CacheLoader;
 
 @Singleton
-public class AccountRecordIdCacheLoader extends BaseCacheLoader implements CacheLoader {
+public class AccountRecordIdCacheLoader extends BaseIdCacheLoader implements CacheLoader {
 
     @Inject
     public AccountRecordIdCacheLoader(final IDBI dbi, final NonEntityDao nonEntityDao) {
@@ -43,19 +43,7 @@ public class AccountRecordIdCacheLoader extends BaseCacheLoader implements Cache
     }
 
     @Override
-    public Object load(final Object key, final Object argument) {
-        checkCacheLoaderStatus();
-
-        if (!(key instanceof String)) {
-            throw new IllegalArgumentException("Unexpected key type of " + key.getClass().getName());
-        }
-        if (!(argument instanceof CacheLoaderArgument)) {
-            throw new IllegalArgumentException("Unexpected key type of " + argument.getClass().getName());
-        }
-
-        final String objectId = (String) key;
-        final ObjectType objectType = ((CacheLoaderArgument) argument).getObjectType();
-
-        return nonEntityDao.retrieveAccountRecordIdFromObject(UUID.fromString(objectId), objectType, null);
+    protected Object doRetrieveOperation(final String rawKey, final ObjectType objectType) {
+        return  nonEntityDao.retrieveAccountRecordIdFromObject(UUID.fromString(rawKey), objectType, null);
     }
 }
diff --git a/util/src/main/java/org/killbill/billing/util/cache/BaseIdCacheLoader.java b/util/src/main/java/org/killbill/billing/util/cache/BaseIdCacheLoader.java
new file mode 100644
index 0000000..3ff9822
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/cache/BaseIdCacheLoader.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ * Copyright 2014 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.util.cache;
+
+import org.killbill.billing.ObjectType;
+import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.killbill.billing.util.dao.NonEntityDao;
+import org.skife.jdbi.v2.IDBI;
+
+public abstract class BaseIdCacheLoader extends BaseCacheLoader {
+
+    protected BaseIdCacheLoader(final IDBI dbi, final NonEntityDao nonEntityDao) {
+        super(dbi, nonEntityDao);
+    }
+
+    @Override
+    public abstract CacheType getCacheType();
+
+
+    protected abstract Object doRetrieveOperation(final String rawKey, final ObjectType objectType);
+
+    @Override
+    public Object load(final Object key, final Object argument) {
+        checkCacheLoaderStatus();
+
+        if (!(key instanceof String)) {
+            throw new IllegalArgumentException("Unexpected key type of " + key.getClass().getName());
+        }
+        if (!(argument instanceof CacheLoaderArgument)) {
+            throw new IllegalArgumentException("Unexpected key type of " + argument.getClass().getName());
+        }
+
+        final String rawKey;
+        if (getCacheType().isKeyPrefixedWithTableName()) {
+            String [] parts = ((String) key).split(CacheControllerDispatcher.CACHE_KEY_SEPARATOR);
+            rawKey = parts[1];
+        } else {
+            rawKey = (String) key;
+        }
+        final ObjectType objectType = ((CacheLoaderArgument) argument).getObjectType();
+        return doRetrieveOperation(rawKey, objectType);
+    }
+}
diff --git a/util/src/main/java/org/killbill/billing/util/cache/Cachable.java b/util/src/main/java/org/killbill/billing/util/cache/Cachable.java
index d9ffbe2..6ede4f6 100644
--- a/util/src/main/java/org/killbill/billing/util/cache/Cachable.java
+++ b/util/src/main/java/org/killbill/billing/util/cache/Cachable.java
@@ -28,6 +28,7 @@ public @interface Cachable {
     public final String RECORD_ID_CACHE_NAME = "record-id";
     public final String ACCOUNT_RECORD_ID_CACHE_NAME = "account-record-id";
     public final String TENANT_RECORD_ID_CACHE_NAME = "tenant-record-id";
+    public final String OBJECT_ID_CACHE_NAME = "object-id";
     public final String AUDIT_LOG_CACHE_NAME = "audit-log";
     public final String AUDIT_LOG_VIA_HISTORY_CACHE_NAME = "audit-log-via-history";
 
@@ -35,30 +36,37 @@ public @interface Cachable {
 
     public enum CacheType {
         /* Mapping from object 'id (UUID)' -> object 'recordId (Long' */
-        RECORD_ID(RECORD_ID_CACHE_NAME),
+        RECORD_ID(RECORD_ID_CACHE_NAME, false),
 
         /* Mapping from object 'id (UUID)' -> matching account object 'accountRecordId (Long)' */
-        ACCOUNT_RECORD_ID(ACCOUNT_RECORD_ID_CACHE_NAME),
+        ACCOUNT_RECORD_ID(ACCOUNT_RECORD_ID_CACHE_NAME, false),
 
         /* Mapping from object 'id (UUID)' -> matching object 'tenantRecordId (Long)' */
-        TENANT_RECORD_ID(TENANT_RECORD_ID_CACHE_NAME),
+        TENANT_RECORD_ID(TENANT_RECORD_ID_CACHE_NAME, false),
+
+        /* Mapping from object 'recordId (Long') -> object 'id (UUID)'  */
+        OBJECT_ID(OBJECT_ID_CACHE_NAME, true),
 
         /* Mapping from object 'tableName::targetRecordId' -> matching objects 'Iterable<AuditLog>' */
-        AUDIT_LOG(AUDIT_LOG_CACHE_NAME),
+        AUDIT_LOG(AUDIT_LOG_CACHE_NAME, true),
 
         /* Mapping from object 'tableName::historyTableName::targetRecordId' -> matching objects 'Iterable<AuditLog>' */
-        AUDIT_LOG_VIA_HISTORY(AUDIT_LOG_VIA_HISTORY_CACHE_NAME);
+        AUDIT_LOG_VIA_HISTORY(AUDIT_LOG_VIA_HISTORY_CACHE_NAME, true);
 
         private final String cacheName;
+        private final boolean isKeyPrefixedWithTableName;
 
-        CacheType(final String cacheName) {
+        CacheType(final String cacheName, final boolean isKeyPrefixedWithTableName) {
             this.cacheName = cacheName;
+            this.isKeyPrefixedWithTableName = isKeyPrefixedWithTableName;
         }
 
         public String getCacheName() {
             return cacheName;
         }
 
+        public boolean isKeyPrefixedWithTableName() { return isKeyPrefixedWithTableName; }
+
         public static CacheType findByName(final String input) {
             for (final CacheType cacheType : CacheType.values()) {
                 if (cacheType.cacheName.equals(input)) {
diff --git a/util/src/main/java/org/killbill/billing/util/cache/CacheController.java b/util/src/main/java/org/killbill/billing/util/cache/CacheController.java
index 1b357ea..5743f65 100644
--- a/util/src/main/java/org/killbill/billing/util/cache/CacheController.java
+++ b/util/src/main/java/org/killbill/billing/util/cache/CacheController.java
@@ -16,8 +16,12 @@
 
 package org.killbill.billing.util.cache;
 
+import org.killbill.billing.util.cache.Cachable.CacheType;
+
 public interface CacheController<K, V> {
 
+    public void add(K key, V value);
+
     public V get(K key, CacheLoaderArgument objectType);
 
     public boolean remove(K key);
@@ -25,4 +29,6 @@ public interface CacheController<K, V> {
     public int size();
 
     void removeAll();
+
+    CacheType getCacheType();
 }
diff --git a/util/src/main/java/org/killbill/billing/util/cache/CacheControllerDispatcher.java b/util/src/main/java/org/killbill/billing/util/cache/CacheControllerDispatcher.java
index 11c44e3..bf2f6b1 100644
--- a/util/src/main/java/org/killbill/billing/util/cache/CacheControllerDispatcher.java
+++ b/util/src/main/java/org/killbill/billing/util/cache/CacheControllerDispatcher.java
@@ -26,6 +26,8 @@ import org.killbill.billing.util.cache.Cachable.CacheType;
 // Kill Bill generic cache dispatcher
 public class CacheControllerDispatcher {
 
+    public static final String CACHE_KEY_SEPARATOR = "::";
+
     private final Map<CacheType, CacheController<Object, Object>> caches;
 
     @Inject
diff --git a/util/src/main/java/org/killbill/billing/util/cache/CacheControllerDispatcherProvider.java b/util/src/main/java/org/killbill/billing/util/cache/CacheControllerDispatcherProvider.java
index e216033..c41cddb 100644
--- a/util/src/main/java/org/killbill/billing/util/cache/CacheControllerDispatcherProvider.java
+++ b/util/src/main/java/org/killbill/billing/util/cache/CacheControllerDispatcherProvider.java
@@ -29,6 +29,7 @@ import com.google.common.base.Function;
 import com.google.common.collect.Collections2;
 import net.sf.ehcache.Cache;
 import net.sf.ehcache.CacheManager;
+import net.sf.ehcache.Element;
 import net.sf.ehcache.loader.CacheLoader;
 
 // Build the abstraction layer between EhCache and Kill Bill
@@ -47,25 +48,23 @@ public class CacheControllerDispatcherProvider implements Provider<CacheControll
         for (final String cacheName : cacheManager.getCacheNames()) {
             final CacheType cacheType = CacheType.findByName(cacheName);
 
-            final Collection<EhCacheBasedCacheController<Object, Object>> cacheControllersForCacheName = getCacheControllersForCacheName(cacheName);
+            final Collection<EhCacheBasedCacheController<Object, Object>> cacheControllersForCacheName = getCacheControllersForCacheName(cacheName, cacheType);
             // EhCache supports multiple cache loaders per type, but not Kill Bill - take the first one
             if (cacheControllersForCacheName.size() > 0) {
                 final EhCacheBasedCacheController<Object, Object> ehCacheBasedCacheController = cacheControllersForCacheName.iterator().next();
                 cacheControllers.put(cacheType, ehCacheBasedCacheController);
             }
         }
-
         return new CacheControllerDispatcher(cacheControllers);
     }
 
-    public Collection<EhCacheBasedCacheController<Object, Object>> getCacheControllersForCacheName(final String name) {
+    private Collection<EhCacheBasedCacheController<Object, Object>> getCacheControllersForCacheName(final String name, final CacheType cacheType) {
         final Cache cache = cacheManager.getCache(name);
-
         // The CacheLoaders were registered in EhCacheCacheManagerProvider
         return Collections2.transform(cache.getRegisteredCacheLoaders(), new Function<CacheLoader, EhCacheBasedCacheController<Object, Object>>() {
             @Override
             public EhCacheBasedCacheController<Object, Object> apply(final CacheLoader input) {
-                return new EhCacheBasedCacheController<Object, Object>(cache);
+                return new EhCacheBasedCacheController<Object, Object>(cache, cacheType);
             }
         });
     }
diff --git a/util/src/main/java/org/killbill/billing/util/cache/EhCacheBasedCacheController.java b/util/src/main/java/org/killbill/billing/util/cache/EhCacheBasedCacheController.java
index d12b5d6..eb20466 100644
--- a/util/src/main/java/org/killbill/billing/util/cache/EhCacheBasedCacheController.java
+++ b/util/src/main/java/org/killbill/billing/util/cache/EhCacheBasedCacheController.java
@@ -16,15 +16,24 @@
 
 package org.killbill.billing.util.cache;
 
+import org.killbill.billing.util.cache.Cachable.CacheType;
+
 import net.sf.ehcache.Cache;
 import net.sf.ehcache.Element;
 
 public class EhCacheBasedCacheController<K, V> implements CacheController<K, V> {
 
     private final Cache cache;
+    private final CacheType cacheType;
 
-    public EhCacheBasedCacheController(final Cache cache) {
+    public EhCacheBasedCacheController(final Cache cache, final CacheType cacheType) {
         this.cache = cache;
+        this.cacheType = cacheType;
+    }
+
+    @Override
+    public void add(final K key, final V value) {
+        cache.putIfAbsent(new Element(key, value));
     }
 
     @Override
@@ -50,4 +59,9 @@ public class EhCacheBasedCacheController<K, V> implements CacheController<K, V> 
     public void removeAll() {
         cache.removeAll();
     }
+
+    @Override
+    public CacheType getCacheType() {
+        return cacheType;
+    }
 }
diff --git a/util/src/main/java/org/killbill/billing/util/cache/EhCacheCacheManagerProvider.java b/util/src/main/java/org/killbill/billing/util/cache/EhCacheCacheManagerProvider.java
index 2be36ce..c8d7578 100644
--- a/util/src/main/java/org/killbill/billing/util/cache/EhCacheCacheManagerProvider.java
+++ b/util/src/main/java/org/killbill/billing/util/cache/EhCacheCacheManagerProvider.java
@@ -43,12 +43,14 @@ public class EhCacheCacheManagerProvider implements Provider<CacheManager> {
                                        final RecordIdCacheLoader recordIdCacheLoader,
                                        final AccountRecordIdCacheLoader accountRecordIdCacheLoader,
                                        final TenantRecordIdCacheLoader tenantRecordIdCacheLoader,
+                                       final ObjectIdCacheLoader objectIdCacheLoader,
                                        final AuditLogCacheLoader auditLogCacheLoader,
                                        final AuditLogViaHistoryCacheLoader auditLogViaHistoryCacheLoader) {
         this.cacheConfig = cacheConfig;
         cacheLoaders.add(recordIdCacheLoader);
         cacheLoaders.add(accountRecordIdCacheLoader);
         cacheLoaders.add(tenantRecordIdCacheLoader);
+        cacheLoaders.add(objectIdCacheLoader);
         cacheLoaders.add(auditLogCacheLoader);
         cacheLoaders.add(auditLogViaHistoryCacheLoader);
     }
diff --git a/util/src/main/java/org/killbill/billing/util/cache/ObjectIdCacheLoader.java b/util/src/main/java/org/killbill/billing/util/cache/ObjectIdCacheLoader.java
new file mode 100644
index 0000000..f770184
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/cache/ObjectIdCacheLoader.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ * Copyright 2014 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.util.cache;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.killbill.billing.ObjectType;
+import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.killbill.billing.util.dao.NonEntityDao;
+import org.skife.jdbi.v2.IDBI;
+
+import net.sf.ehcache.CacheException;
+import net.sf.ehcache.loader.CacheLoader;
+
+@Singleton
+public class ObjectIdCacheLoader extends BaseIdCacheLoader implements CacheLoader {
+
+    @Inject
+    public ObjectIdCacheLoader(final IDBI dbi, final NonEntityDao nonEntityDao) {
+        super(dbi, nonEntityDao);
+    }
+
+    @Override
+    public CacheType getCacheType() {
+        return CacheType.OBJECT_ID;
+    }
+
+    @Override
+    protected Object doRetrieveOperation(final String rawKey, final ObjectType objectType) {
+        final Long recordId = Long.valueOf(rawKey);
+        return nonEntityDao.retrieveIdFromObject(recordId, objectType, null);
+    }
+}
diff --git a/util/src/main/java/org/killbill/billing/util/cache/RecordIdCacheLoader.java b/util/src/main/java/org/killbill/billing/util/cache/RecordIdCacheLoader.java
index a2fb825..8398911 100644
--- a/util/src/main/java/org/killbill/billing/util/cache/RecordIdCacheLoader.java
+++ b/util/src/main/java/org/killbill/billing/util/cache/RecordIdCacheLoader.java
@@ -31,7 +31,7 @@ import net.sf.ehcache.CacheException;
 import net.sf.ehcache.loader.CacheLoader;
 
 @Singleton
-public class RecordIdCacheLoader extends BaseCacheLoader implements CacheLoader {
+public class RecordIdCacheLoader extends BaseIdCacheLoader implements CacheLoader {
 
     @Inject
     public RecordIdCacheLoader(final IDBI dbi, final NonEntityDao nonEntityDao) {
@@ -44,19 +44,7 @@ public class RecordIdCacheLoader extends BaseCacheLoader implements CacheLoader 
     }
 
     @Override
-    public Object load(final Object key, final Object argument) throws CacheException {
-        checkCacheLoaderStatus();
-
-        if (!(key instanceof String)) {
-            throw new IllegalArgumentException("Unexpected key type of " + key.getClass().getName());
-        }
-        if (!(argument instanceof CacheLoaderArgument)) {
-            throw new IllegalArgumentException("Unexpected key type of " + argument.getClass().getName());
-        }
-
-        final String objectId = (String) key;
-        final ObjectType objectType = ((CacheLoaderArgument) argument).getObjectType();
-
-        return nonEntityDao.retrieveRecordIdFromObject(UUID.fromString(objectId), objectType, null);
+    protected Object doRetrieveOperation(final String rawKey, final ObjectType objectType) {
+        return nonEntityDao.retrieveRecordIdFromObject(UUID.fromString(rawKey), objectType, null);
     }
 }
diff --git a/util/src/main/java/org/killbill/billing/util/cache/TenantRecordIdCacheLoader.java b/util/src/main/java/org/killbill/billing/util/cache/TenantRecordIdCacheLoader.java
index 753a7fb..008fc52 100644
--- a/util/src/main/java/org/killbill/billing/util/cache/TenantRecordIdCacheLoader.java
+++ b/util/src/main/java/org/killbill/billing/util/cache/TenantRecordIdCacheLoader.java
@@ -30,7 +30,7 @@ import org.killbill.billing.util.dao.NonEntityDao;
 import net.sf.ehcache.loader.CacheLoader;
 
 @Singleton
-public class TenantRecordIdCacheLoader extends BaseCacheLoader implements CacheLoader {
+public class TenantRecordIdCacheLoader extends BaseIdCacheLoader implements CacheLoader {
 
     @Inject
     public TenantRecordIdCacheLoader(final IDBI dbi, final NonEntityDao nonEntityDao) {
@@ -43,19 +43,7 @@ public class TenantRecordIdCacheLoader extends BaseCacheLoader implements CacheL
     }
 
     @Override
-    public Object load(final Object key, final Object argument) {
-        checkCacheLoaderStatus();
-
-        if (!(key instanceof String)) {
-            throw new IllegalArgumentException("Unexpected key type of " + key.getClass().getName());
-        }
-        if (!(argument instanceof CacheLoaderArgument)) {
-            throw new IllegalArgumentException("Unexpected key type of " + argument.getClass().getName());
-        }
-
-        final String objectId = (String) key;
-        final ObjectType objectType = ((CacheLoaderArgument) argument).getObjectType();
-
-        return nonEntityDao.retrieveTenantRecordIdFromObject(UUID.fromString(objectId), objectType, null);
+    protected Object doRetrieveOperation(final String rawKey, final ObjectType objectType) {
+        return nonEntityDao.retrieveTenantRecordIdFromObject(UUID.fromString(rawKey), objectType, null);
     }
 }
diff --git a/util/src/main/java/org/killbill/billing/util/customfield/dao/CustomFieldModelDao.java b/util/src/main/java/org/killbill/billing/util/customfield/dao/CustomFieldModelDao.java
index 3987b3e..7a96c92 100644
--- a/util/src/main/java/org/killbill/billing/util/customfield/dao/CustomFieldModelDao.java
+++ b/util/src/main/java/org/killbill/billing/util/customfield/dao/CustomFieldModelDao.java
@@ -25,8 +25,9 @@ import org.killbill.billing.util.customfield.CustomField;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
+import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
-public class CustomFieldModelDao extends EntityBase implements EntityModelDao<CustomField> {
+public class CustomFieldModelDao extends EntityModelDaoBase implements EntityModelDao<CustomField> {
 
     private String fieldName;
     private String fieldValue;
diff --git a/util/src/main/java/org/killbill/billing/util/dao/DefaultNonEntityDao.java b/util/src/main/java/org/killbill/billing/util/dao/DefaultNonEntityDao.java
index d5783c9..e68e0d1 100644
--- a/util/src/main/java/org/killbill/billing/util/dao/DefaultNonEntityDao.java
+++ b/util/src/main/java/org/killbill/billing/util/dao/DefaultNonEntityDao.java
@@ -21,6 +21,9 @@ import java.util.UUID;
 import javax.annotation.Nullable;
 import javax.inject.Inject;
 
+import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.commons.profiling.Profiling;
 import org.killbill.commons.profiling.Profiling.WithProfilingCallback;
 import org.killbill.commons.profiling.ProfilingFeature.ProfilingFeatureType;
@@ -33,34 +36,32 @@ import org.killbill.billing.util.cache.CacheLoaderArgument;
 public class DefaultNonEntityDao implements NonEntityDao {
 
     private final NonEntitySqlDao nonEntitySqlDao;
-    private final WithCaching containedCall;
-    private final Profiling<Long> prof;
+    private final WithCaching<UUID, Long> withCachingObjectId;
+    private final WithCaching<Long, UUID> withCachingRecordId;
 
     @Inject
     public DefaultNonEntityDao(final IDBI dbi) {
         this.nonEntitySqlDao = dbi.onDemand(NonEntitySqlDao.class);
-        this.containedCall = new WithCaching();
-        this.prof = new Profiling<Long>();
+        this.withCachingObjectId = new WithCaching<UUID, Long>();
+        this.withCachingRecordId = new WithCaching<Long, UUID>();
     }
 
 
     public Long retrieveRecordIdFromObject(@Nullable final UUID objectId, final ObjectType objectType, @Nullable final CacheController<Object, Object> cache) {
-
-        return containedCall.withCaching(new OperationRetrieval<Long>() {
+        final TableName tableName = TableName.fromObjectType(objectType);
+        return withCachingObjectId.withCaching(new OperationRetrieval<UUID, Long>() {
             @Override
-            public Long doRetrieve(final UUID objectId, final ObjectType objectType) {
-                final TableName tableName = TableName.fromObjectType(objectType);
+            public Long doRetrieve(final UUID objectOrRecordId, final ObjectType objectType) {
                 return nonEntitySqlDao.getRecordIdFromObject(objectId.toString(), tableName.getTableName());
             }
-        }, objectId, objectType, cache);
-
+        }, objectId, objectType, tableName, cache);
     }
 
     public Long retrieveAccountRecordIdFromObject(@Nullable final UUID objectId, final ObjectType objectType, @Nullable final CacheController<Object, Object> cache) {
-        return containedCall.withCaching(new OperationRetrieval<Long>() {
+        final TableName tableName = TableName.fromObjectType(objectType);
+        return withCachingObjectId.withCaching(new OperationRetrieval<UUID, Long>() {
             @Override
             public Long doRetrieve(final UUID objectId, final ObjectType objectType) {
-                final TableName tableName = TableName.fromObjectType(objectType);
                 switch (tableName) {
                     case TENANT:
                     case TAG_DEFINITIONS:
@@ -74,29 +75,42 @@ public class DefaultNonEntityDao implements NonEntityDao {
                         return nonEntitySqlDao.getAccountRecordIdFromObjectOtherThanAccount(objectId.toString(), tableName.getTableName());
                 }
             }
-        }, objectId, objectType, cache);
+        }, objectId, objectType, tableName, cache);
     }
 
     public Long retrieveTenantRecordIdFromObject(@Nullable final UUID objectId, final ObjectType objectType, @Nullable final CacheController<Object, Object> cache) {
-
-
-        return containedCall.withCaching(new OperationRetrieval<Long>() {
+        final TableName tableName = TableName.fromObjectType(objectType);
+        return withCachingObjectId.withCaching(new OperationRetrieval<UUID, Long>() {
             @Override
             public Long doRetrieve(final UUID objectId, final ObjectType objectType) {
-                final TableName tableName = TableName.fromObjectType(objectType);
                 switch (tableName) {
                     case TENANT:
-                        return nonEntitySqlDao.getTenantRecordIdFromTenant(objectId.toString());
+                        return objectId == null ? 0L : nonEntitySqlDao.getTenantRecordIdFromTenant(objectId.toString());
 
                     default:
                         return nonEntitySqlDao.getTenantRecordIdFromObjectOtherThanTenant(objectId.toString(), tableName.getTableName());
                 }
 
             }
-        }, objectId, objectType, cache);
+        }, objectId, objectType, tableName, cache);
     }
 
     @Override
+    public UUID retrieveIdFromObject(final Long recordId, final ObjectType objectType, @Nullable final CacheController<Object, Object> cache) {
+        if (objectType == ObjectType.TENANT && recordId == InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID) {
+            return null;
+        }
+        final TableName tableName = TableName.fromObjectType(objectType);
+        return withCachingRecordId.withCaching(new OperationRetrieval<Long, UUID>() {
+            @Override
+            public UUID doRetrieve(final Long objectOrRecordId, final ObjectType objectType) {
+                return nonEntitySqlDao.getIdFromObject(recordId, tableName.getTableName());
+            }
+        }, recordId, objectType, tableName, cache);
+    }
+
+
+    @Override
     public Long retrieveLastHistoryRecordIdFromTransaction(@Nullable final Long targetRecordId, final TableName tableName, final NonEntitySqlDao transactional) {
         // There is no caching here because the value returned changes as we add more history records, and so we would need some cache invalidation
         return transactional.getLastHistoryRecordId(targetRecordId, tableName.getTableName());
@@ -107,33 +121,32 @@ public class DefaultNonEntityDao implements NonEntityDao {
         return nonEntitySqlDao.getHistoryTargetRecordId(recordId, tableName.getTableName());
     }
 
-    @Override
-    public UUID retrieveIdFromObject(final Long recordId, final ObjectType objectType) {
-        final TableName tableName = TableName.fromObjectType(objectType);
-        return nonEntitySqlDao.getIdFromObject(recordId, tableName.getTableName());
-    }
 
-    private interface OperationRetrieval<T> {
-        public T doRetrieve(final UUID objectId, final ObjectType objectType);
+    private interface OperationRetrieval<TypeIn, TypeOut> {
+        public TypeOut doRetrieve(final TypeIn objectOrRecordId, final ObjectType objectType);
     }
 
     // 'cache' will be null for the CacheLoader classes -- or if cache is not configured.
-    private class WithCaching {
+    private class WithCaching<TypeIn, TypeOut> {
 
-        private Long withCaching(final OperationRetrieval<Long> op, @Nullable final UUID objectId, final ObjectType objectType, @Nullable final CacheController<Object, Object> cache) {
-            if (objectId == null) {
+        private TypeOut withCaching(final OperationRetrieval<TypeIn, TypeOut> op, @Nullable final TypeIn objectOrRecordId, final ObjectType objectType, final TableName tableName, @Nullable final CacheController<Object, Object> cache) {
+
+            final Profiling<TypeOut> prof = new Profiling<TypeOut>();
+            if (objectOrRecordId == null) {
                 return null;
             }
-
             if (cache != null) {
-                return (Long) cache.get(objectId.toString(), new CacheLoaderArgument(objectType));
+                final String key = (cache.getCacheType().isKeyPrefixedWithTableName()) ?
+                                   tableName + CacheControllerDispatcher.CACHE_KEY_SEPARATOR + objectOrRecordId.toString() :
+                                   objectOrRecordId.toString();
+                return (TypeOut) cache.get(key, new CacheLoaderArgument(objectType));
             }
-            final Long result;
+            final TypeOut result;
             try {
-                result = prof.executeWithProfiling(ProfilingFeatureType.DAO_DETAILS,  "NonEntityDao (type = " +  objectType + ") cache miss", new WithProfilingCallback() {
+                result = prof.executeWithProfiling(ProfilingFeatureType.DAO_DETAILS,  "NonEntityDao (type = " +  objectType + ") cache miss", new WithProfilingCallback<TypeOut>() {
                     @Override
-                    public Long execute() throws Throwable {
-                        return op.doRetrieve(objectId, objectType);
+                    public <ExceptionType extends Throwable> TypeOut execute() throws ExceptionType {
+                        return op.doRetrieve(objectOrRecordId, objectType);
                     }
                 });
                 return result;
diff --git a/util/src/main/java/org/killbill/billing/util/dao/EntityAudit.java b/util/src/main/java/org/killbill/billing/util/dao/EntityAudit.java
index 0cd4568..e48775f 100644
--- a/util/src/main/java/org/killbill/billing/util/dao/EntityAudit.java
+++ b/util/src/main/java/org/killbill/billing/util/dao/EntityAudit.java
@@ -22,8 +22,9 @@ import org.joda.time.DateTime;
 
 import org.killbill.billing.util.audit.ChangeType;
 import org.killbill.billing.entity.EntityBase;
+import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
-public class EntityAudit extends EntityBase {
+public class EntityAudit extends EntityModelDaoBase {
     
     private final TableName tableName;
     private final Long targetRecordId;
diff --git a/util/src/main/java/org/killbill/billing/util/dao/NonEntityDao.java b/util/src/main/java/org/killbill/billing/util/dao/NonEntityDao.java
index dcfee7a..e3dec90 100644
--- a/util/src/main/java/org/killbill/billing/util/dao/NonEntityDao.java
+++ b/util/src/main/java/org/killbill/billing/util/dao/NonEntityDao.java
@@ -26,21 +26,17 @@ import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 
 public interface NonEntityDao {
 
-    //
-    // TODO should we check for InternalCallContext?
-    // That seems difficult because those APIs are called when creating a callcontext or from the cache loaders which also dpn't know anything about callcontext
-    //
     public Long retrieveRecordIdFromObject(final UUID objectId, final ObjectType objectType, @Nullable final CacheController<Object, Object> cache);
 
     public Long retrieveAccountRecordIdFromObject(final UUID objectId, final ObjectType objectType, @Nullable final CacheController<Object, Object> cache);
 
     public Long retrieveTenantRecordIdFromObject(final UUID objectId, final ObjectType objectType, @Nullable final CacheController<Object, Object> cache);
 
+    public UUID retrieveIdFromObject(final Long recordId, final ObjectType objectType, @Nullable final CacheController<Object, Object> cache);
+
     // This retrieves from the history table the latest record for which targetId matches the one we are passing
     public Long retrieveLastHistoryRecordIdFromTransaction(final Long targetRecordId, final TableName tableName, final NonEntitySqlDao transactional);
 
     // This is the reverse from retrieveLastHistoryRecordIdFromTransaction; this retrieves the record_id of the object matching a given history row
     public Long retrieveHistoryTargetRecordId(final Long recordId, final TableName tableName);
-
-    public UUID retrieveIdFromObject(final Long recordId, final ObjectType objectType);
 }
diff --git a/util/src/main/java/org/killbill/billing/util/entity/dao/EntityModelDao.java b/util/src/main/java/org/killbill/billing/util/entity/dao/EntityModelDao.java
index fff329f..1880644 100644
--- a/util/src/main/java/org/killbill/billing/util/entity/dao/EntityModelDao.java
+++ b/util/src/main/java/org/killbill/billing/util/entity/dao/EntityModelDao.java
@@ -28,6 +28,12 @@ import org.killbill.billing.util.entity.Entity;
 @SuppressWarnings("UnusedDeclaration")
 public interface EntityModelDao<E extends Entity> extends Entity {
 
+    public Long getRecordId();
+
+    public Long getAccountRecordId();
+
+    public Long getTenantRecordId();
+
     /**
      * Retrieve the TableName associated with this entity. This is used in
      * EntitySqlDaoWrapperInvocationHandler for history and auditing purposes.
diff --git a/util/src/main/java/org/killbill/billing/util/entity/dao/EntityModelDaoBase.java b/util/src/main/java/org/killbill/billing/util/entity/dao/EntityModelDaoBase.java
new file mode 100644
index 0000000..824935f
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/entity/dao/EntityModelDaoBase.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ * Copyright 2014 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.util.entity.dao;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.entity.EntityBase;
+
+public class EntityModelDaoBase extends EntityBase {
+
+    private Long recordId;
+    private Long accountRecordId;
+    private Long tenantRecordId;
+
+    public EntityModelDaoBase(final UUID id) {
+        super(id);
+    }
+
+    public EntityModelDaoBase() {
+    }
+
+    public EntityModelDaoBase(final UUID id, final DateTime createdDate, final DateTime updatedDate) {
+        super(id, createdDate, updatedDate);
+    }
+
+    public EntityModelDaoBase(final EntityBase target) {
+        super(target);
+    }
+
+    public Long getRecordId() {
+        return recordId;
+    }
+
+    public void setRecordId(final Long recordId) {
+        this.recordId = recordId;
+    }
+
+    public Long getAccountRecordId() {
+        return accountRecordId;
+    }
+
+    public void setAccountRecordId(final Long accountRecordId) {
+        this.accountRecordId = accountRecordId;
+    }
+
+    public Long getTenantRecordId() {
+        return tenantRecordId;
+    }
+
+    public void setTenantRecordId(final Long tenantRecordId) {
+        this.tenantRecordId = tenantRecordId;
+    }
+}
diff --git a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
index 4f5e778..1432259 100644
--- a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
+++ b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
@@ -33,6 +33,7 @@ import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.util.audit.ChangeType;
+import org.killbill.billing.util.cache.BaseCacheLoader;
 import org.killbill.billing.util.cache.Cachable;
 import org.killbill.billing.util.cache.Cachable.CacheType;
 import org.killbill.billing.util.cache.CachableKey;
@@ -74,7 +75,6 @@ import com.google.common.collect.Iterables;
  */
 public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>, M extends EntityModelDao<E>, E extends Entity> implements InvocationHandler {
 
-    public static final String CACHE_KEY_SEPARATOR = "::";
 
     private final Logger logger = LoggerFactory.getLogger(EntitySqlDaoWrapperInvocationHandler.class);
 
@@ -180,15 +180,28 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>, 
         } else if (cachableAnnotation != null) {
             return invokeWithCaching(cachableAnnotation, method, args);
         } else {
-            return prof.executeWithProfiling(ProfilingFeatureType.DAO_DETAILS, sqlDaoClass.getSimpleName() + " (raw):" + method.getName(), new WithProfilingCallback() {
-                @Override
-                public Object execute() throws Throwable {
-                    return method.invoke(sqlDao, args);
-                }
-            });
+            return invokeRaw(method, args);
         }
     }
 
+    private Object invokeRaw(final Method method, final Object[] args) throws Throwable  {
+        return prof.executeWithProfiling(ProfilingFeatureType.DAO_DETAILS, sqlDaoClass.getSimpleName() + " (raw):" + method.getName(), new WithProfilingCallback() {
+            @Override
+            public Object execute() throws Throwable {
+                Object result = method.invoke(sqlDao, args);
+                // This is *almost* the default invocation except that we want to intercept getById calls to populate the caches; the pattern is to always fetch
+                // the object after it was created, which means this method is (by pattern) first called right after object creation and contains all the goodies we care
+                // about (record_id, account_record_id, object_id, tenant_record_id)
+                //
+                if (result != null && method.getName().equals("getById")) {
+                    populateCacheOnGetByIdInvocation((M) result);
+                }
+                return result;
+            }
+        });
+    }
+
+
     private Object invokeWithCaching(final Cachable cachableAnnotation, final Method method, final Object[] args)
             throws Throwable {
         final ObjectType objectType = getObjectType();
@@ -314,6 +327,31 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>, 
         return obj;
     }
 
+    private void populateCacheOnGetByIdInvocation(M model) {
+
+        final CacheController<Object, Object> cacheRecordId = cacheControllerDispatcher.getCacheController(CacheType.RECORD_ID);
+        cacheRecordId.add(getKey(model.getId().toString(), CacheType.RECORD_ID, model.getTableName()), model.getRecordId());
+
+        final CacheController<Object, Object> cacheObjectId = cacheControllerDispatcher.getCacheController(CacheType.OBJECT_ID);
+        cacheObjectId.add(getKey(model.getRecordId().toString(), CacheType.OBJECT_ID, model.getTableName()), model.getId());
+
+        if (model.getTenantRecordId() != null) {
+            final CacheController<Object, Object> cacheTenantRecordId = cacheControllerDispatcher.getCacheController(CacheType.TENANT_RECORD_ID);
+            cacheTenantRecordId.add(getKey(model.getId().toString(), CacheType.TENANT_RECORD_ID, model.getTableName()), model.getTenantRecordId());
+        }
+
+        if (model.getAccountRecordId() != null) {
+            final CacheController<Object, Object> cacheAccountRecordId = cacheControllerDispatcher.getCacheController(CacheType.ACCOUNT_RECORD_ID);
+            cacheAccountRecordId.add(getKey(model.getId().toString(), CacheType.ACCOUNT_RECORD_ID, model.getTableName()), model.getAccountRecordId());
+        }
+    }
+
+    private String getKey(final String rawKey, final CacheType cacheType, final TableName tableName) {
+        return cacheType.isKeyPrefixedWithTableName() ?
+               tableName + CacheControllerDispatcher.CACHE_KEY_SEPARATOR + rawKey :
+               rawKey;
+    }
+
     private void updateHistoryAndAudit(final String entityId, final Map<String, M> entities, final Map<String, Long> entityRecordIds,
                                        final ChangeType changeType, final InternalCallContext context) throws Throwable {
 
@@ -448,7 +486,7 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>, 
             final String str = String.valueOf(keyPieces.get(i)).toUpperCase();
             cacheKey.append(str);
             if (i < keyPieces.size() - 1) {
-                cacheKey.append(CACHE_KEY_SEPARATOR);
+                cacheKey.append(CacheControllerDispatcher.CACHE_KEY_SEPARATOR);
             }
         }
         return cacheKey.toString();
diff --git a/util/src/main/java/org/killbill/billing/util/tag/dao/TagDefinitionModelDao.java b/util/src/main/java/org/killbill/billing/util/tag/dao/TagDefinitionModelDao.java
index 85d9f49..dbde12c 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/dao/TagDefinitionModelDao.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/dao/TagDefinitionModelDao.java
@@ -23,10 +23,11 @@ import org.joda.time.DateTime;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
+import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 import org.killbill.billing.util.tag.ControlTagType;
 import org.killbill.billing.util.tag.TagDefinition;
 
-public class TagDefinitionModelDao extends EntityBase implements EntityModelDao<TagDefinition> {
+public class TagDefinitionModelDao extends EntityModelDaoBase implements EntityModelDao<TagDefinition> {
 
     private String name;
     private String description;
diff --git a/util/src/main/java/org/killbill/billing/util/tag/dao/TagModelDao.java b/util/src/main/java/org/killbill/billing/util/tag/dao/TagModelDao.java
index a3b5353..2b80de8 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/dao/TagModelDao.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/dao/TagModelDao.java
@@ -24,9 +24,10 @@ import org.killbill.billing.ObjectType;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
+import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 import org.killbill.billing.util.tag.Tag;
 
-public class TagModelDao extends EntityBase implements EntityModelDao<Tag> {
+public class TagModelDao extends EntityModelDaoBase implements EntityModelDao<Tag> {
 
     private UUID tagDefinitionId;
     private UUID objectId;
diff --git a/util/src/main/resources/ehcache.xml b/util/src/main/resources/ehcache.xml
index fb82f50..ed79009 100644
--- a/util/src/main/resources/ehcache.xml
+++ b/util/src/main/resources/ehcache.xml
@@ -71,6 +71,21 @@
                 properties=""/>
     </cache>
 
+    <cache name="object-id"
+           maxElementsInMemory="100000"
+           maxElementsOnDisk="0"
+           eternal="true"
+           overflowToDisk="false"
+           diskPersistent="false"
+           memoryStoreEvictionPolicy="LFU"
+           statistics="true"
+            >
+        <cacheEventListenerFactory
+                class="org.killbill.billing.util.cache.ExpirationListenerFactory"
+                properties=""/>
+    </cache>
+
+
     <cache name="audit-log"
            maxElementsInMemory="500000"
            maxElementsOnDisk="0"
diff --git a/util/src/test/java/org/killbill/billing/dao/MockNonEntityDao.java b/util/src/test/java/org/killbill/billing/dao/MockNonEntityDao.java
index da53649..997ee41 100644
--- a/util/src/test/java/org/killbill/billing/dao/MockNonEntityDao.java
+++ b/util/src/test/java/org/killbill/billing/dao/MockNonEntityDao.java
@@ -54,7 +54,7 @@ public class MockNonEntityDao implements NonEntityDao {
     }
 
     @Override
-    public UUID retrieveIdFromObject(final Long recordId, final ObjectType objectType) {
+    public UUID retrieveIdFromObject(final Long recordId, final ObjectType objectType, final CacheController<Object, Object> cache) {
         return null;
     }
 }
diff --git a/util/src/test/java/org/killbill/billing/util/cache/TestCache.java b/util/src/test/java/org/killbill/billing/util/cache/TestCache.java
index 0a797a9..9b7f436 100644
--- a/util/src/test/java/org/killbill/billing/util/cache/TestCache.java
+++ b/util/src/test/java/org/killbill/billing/util/cache/TestCache.java
@@ -18,33 +18,23 @@ package org.killbill.billing.util.cache;
 
 import java.util.UUID;
 
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.util.UtilTestSuiteWithEmbeddedDB;
 import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.entity.dao.EntitySqlDao;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import org.killbill.billing.util.tag.dao.TagModelDao;
 import org.killbill.billing.util.tag.dao.TagSqlDao;
+import org.testng.Assert;
+import org.testng.annotations.Test;
 
 public class TestCache extends UtilTestSuiteWithEmbeddedDB {
 
     private EntitySqlDaoTransactionalJdbiWrapper transactionalSqlDao;
 
-    private void insertTag(final TagModelDao modelDao) {
-        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
-            @Override
-            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                entitySqlDaoWrapperFactory.become(TagSqlDao.class).create(modelDao, internalCallContext);
-                return null;
-            }
-        });
-    }
-
     private Long getTagRecordId(final UUID tagId) {
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Long>() {
             @Override
@@ -54,8 +44,8 @@ public class TestCache extends UtilTestSuiteWithEmbeddedDB {
         });
     }
 
-    private int getCacheSize() {
-        final CacheController<Object, Object> cache = controlCacheDispatcher.getCacheController(CacheType.RECORD_ID);
+    private int getCacheSize(CacheType cacheType) {
+        final CacheController<Object, Object> cache = controlCacheDispatcher.getCacheController(cacheType);
         return cache != null ? cache.size() : 0;
     }
 
@@ -75,15 +65,15 @@ public class TestCache extends UtilTestSuiteWithEmbeddedDB {
         final TagModelDao tag = new TagModelDao(clock.getUTCNow(), UUID.randomUUID(), UUID.randomUUID(), ObjectType.TAG);
 
         // Verify we start with nothing in the cache
-        Assert.assertEquals(getCacheSize(), 0);
+        Assert.assertEquals(getCacheSize(CacheType.RECORD_ID), 0);
         insertTag(tag);
 
         // Verify we still have nothing after insert in the cache
-        Assert.assertEquals(getCacheSize(), 0);
+        Assert.assertEquals(getCacheSize(CacheType.RECORD_ID), 0);
 
         final Long tagRecordId = getTagRecordId(tag.getId());
         // Verify we now have something  in the cache
-        Assert.assertEquals(getCacheSize(), 1);
+        Assert.assertEquals(getCacheSize(CacheType.RECORD_ID), 1);
 
         final Long recordIdFromCache = retrieveRecordIdFromCache(tag.getId());
         Assert.assertNotNull(recordIdFromCache);
@@ -91,6 +81,60 @@ public class TestCache extends UtilTestSuiteWithEmbeddedDB {
         Assert.assertEquals(recordIdFromCache, new Long(1));
         Assert.assertEquals(tagRecordId, new Long(1));
 
-        Assert.assertEquals(getCacheSize(), 1);
+        Assert.assertEquals(getCacheSize(CacheType.RECORD_ID), 1);
+    }
+
+    @Test(groups = "slow")
+    public void testAllCachesAfterGetById() throws Exception {
+        this.transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, controlCacheDispatcher, nonEntityDao);
+        final TagModelDao tag = new TagModelDao(clock.getUTCNow(), UUID.randomUUID(), UUID.randomUUID(), ObjectType.TAG);
+
+        insertTag(tag);
+
+        // Verify we start with nothing in the cache
+        Assert.assertEquals(getCacheSize(CacheType.RECORD_ID), 0);
+        Assert.assertEquals(getCacheSize(CacheType.ACCOUNT_RECORD_ID), 0);
+        Assert.assertEquals(getCacheSize(CacheType.TENANT_RECORD_ID), 0);
+        Assert.assertEquals(getCacheSize(CacheType.OBJECT_ID), 0);
+
+        final TagModelDao result = getById(tag.getId());
+
+        Assert.assertEquals(getCacheSize(CacheType.RECORD_ID), 1);
+        Assert.assertEquals(getCacheSize(CacheType.ACCOUNT_RECORD_ID), 1);
+        Assert.assertEquals(getCacheSize(CacheType.TENANT_RECORD_ID), 1);
+        Assert.assertEquals(getCacheSize(CacheType.OBJECT_ID), 1);
+
+        final Long recordId = (Long) controlCacheDispatcher.getCacheController(CacheType.RECORD_ID).get(tag.getId().toString(), new CacheLoaderArgument(ObjectType.TAG));
+        Assert.assertEquals(recordId, result.getRecordId());
+
+        final Long tenantRecordId = (Long) controlCacheDispatcher.getCacheController(CacheType.TENANT_RECORD_ID).get(tag.getId().toString(), new CacheLoaderArgument(ObjectType.TAG));
+        Assert.assertEquals(tenantRecordId, result.getTenantRecordId());
+
+        final UUID objectId = (UUID) controlCacheDispatcher.getCacheController(CacheType.OBJECT_ID).get(TableName.TAG + CacheControllerDispatcher.CACHE_KEY_SEPARATOR  + recordId, new CacheLoaderArgument(ObjectType.TAG));
+        Assert.assertEquals(objectId, result.getId());
+
+        final Long accountRecordId = (Long) controlCacheDispatcher.getCacheController(CacheType.ACCOUNT_RECORD_ID).get(tag.getId().toString(), new CacheLoaderArgument(ObjectType.TAG));
+        Assert.assertEquals(accountRecordId, result.getAccountRecordId());
+
+    }
+
+    private void insertTag(final TagModelDao modelDao) {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                entitySqlDaoWrapperFactory.become(TagSqlDao.class).create(modelDao, internalCallContext);
+                return null;
+            }
+        });
     }
+
+    private TagModelDao getById(final UUID id) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<TagModelDao>() {
+            @Override
+            public TagModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(TagSqlDao.class).getById(id.toString(), internalCallContext);
+            }
+        });
+    }
+
 }