diff --git a/src/main/java/com/avaje/ebean/common/BeanSet.java b/src/main/java/com/avaje/ebean/common/BeanSet.java index e76b85b60..78da3941a 100644 --- a/src/main/java/com/avaje/ebean/common/BeanSet.java +++ b/src/main/java/com/avaje/ebean/common/BeanSet.java @@ -94,7 +94,7 @@ public final class BeanSet extends AbstractBeanCollection implements Set extends AbstractBeanCollection implements Set extends AbstractBeanCollection implements Set -1; + return naturalKeyUpdate; } public Object getNaturalKey() { - return data[naturalKeyUpdate]; + return naturalKey; + } + + public Object getOldNaturalKey() { + return oldNaturalKey; } public boolean containsProperty(int propIndex) { @@ -38,12 +70,6 @@ public class CachedBeanData { return data[i]; } - public Object[] copyData() { - Object[] dest = new Object[data.length]; - System.arraycopy(data, 0, dest, 0, data.length); - return dest; - } - public boolean isLoaded(int i) { return loaded[i]; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataFromBean.java b/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataFromBean.java index 67bc7cec9..c6b6797c2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataFromBean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataFromBean.java @@ -7,50 +7,41 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty; public class CachedBeanDataFromBean { - private final BeanDescriptor desc; - private final EntityBean bean; - private final EntityBeanIntercept ebi; public static CachedBeanData extract(BeanDescriptor desc, EntityBean bean) { - return new CachedBeanDataFromBean(desc, bean, bean._ebean_getIntercept()).extract(); - } - - private CachedBeanDataFromBean(BeanDescriptor desc, EntityBean bean, EntityBeanIntercept ebi) { - this.desc = desc; - this.bean = bean; - this.ebi = ebi; - } - - private CachedBeanData extract() { + EntityBeanIntercept ebi = bean._ebean_getIntercept(); + Object[] data = new Object[desc.getPropertyCount()]; boolean[] loaded = new boolean[desc.getPropertyCount()]; BeanProperty[] props = desc.propertiesNonMany(); - int naturalKeyUpdate = -1; + Object naturalKey = null; + for (int i = 0; i < props.length; i++) { BeanProperty prop = props[i]; - if (isLoaded(prop)) { + if (ebi.isLoadedProperty(prop.getPropertyIndex())) { int propertyIndex = prop.getPropertyIndex(); data[propertyIndex] = prop.getCacheDataValue(bean); loaded[propertyIndex] = true; if (prop.isNaturalKey()) { - naturalKeyUpdate = propertyIndex; + naturalKey = prop.getValue(bean); } } } - EntityBean sharableBean = createSharableBean(); + EntityBean sharableBean = createSharableBean(desc, bean, ebi); - return new CachedBeanData(sharableBean, loaded, data, naturalKeyUpdate); + return new CachedBeanData(sharableBean, loaded, data, naturalKey, null); } - private EntityBean createSharableBean() { - if (!desc.isCacheSharableBeans() || !ebi.isFullyLoadedBean()) { + private static EntityBean createSharableBean(BeanDescriptor desc, EntityBean bean, EntityBeanIntercept beanEbi) { + + if (!desc.isCacheSharableBeans() || !beanEbi.isFullyLoadedBean()) { return null; } - if (ebi.isReadOnly()) { + if (beanEbi.isReadOnly()) { return bean; } @@ -66,14 +57,11 @@ public class CachedBeanDataFromBean { Object v = propertiesNonTransient[i].getValue(bean); propertiesNonTransient[i].setValue(sharableBean, v); } - EntityBeanIntercept ebi = ((EntityBean) sharableBean)._ebean_intercept(); - ebi.setReadOnly(true); - ebi.setLoaded(); + EntityBeanIntercept intercept = sharableBean._ebean_intercept(); + intercept.setReadOnly(true); + intercept.setLoaded(); return sharableBean; } - private boolean isLoaded(BeanProperty prop) { - return ebi.isLoadedProperty(prop.getPropertyIndex()); - } } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataToBean.java b/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataToBean.java index db3082753..9167d3a00 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataToBean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataToBean.java @@ -8,25 +8,10 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; public class CachedBeanDataToBean { - private final BeanDescriptor desc; - private final EntityBean bean; - private final EntityBeanIntercept ebi; - private final CachedBeanData cacheBeanData; - //private final boolean readOnly; - public static boolean load(BeanDescriptor desc, EntityBean bean, CachedBeanData cacheBeandata) { - return new CachedBeanDataToBean(desc, bean, ((EntityBean) bean)._ebean_getIntercept(), cacheBeandata).load(); - } - - private CachedBeanDataToBean(BeanDescriptor desc, EntityBean bean, EntityBeanIntercept ebi, CachedBeanData cacheBeandata) { - this.desc = desc; - this.bean = bean; - this.ebi = ebi; - this.cacheBeanData = cacheBeandata; - //this.readOnly = ebi.isReadOnly(); - } - - private boolean load() { + public static boolean load(BeanDescriptor desc, EntityBean bean, CachedBeanData cacheBeanData) { + + EntityBeanIntercept ebi = bean._ebean_getIntercept(); BeanProperty[] props = desc.propertiesNonMany(); for (int i = 0; i < props.length; i++) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataUpdate.java b/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataUpdate.java index dbe0a8d94..e274b5d4a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataUpdate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataUpdate.java @@ -1,50 +1,44 @@ package com.avaje.ebeaninternal.server.cache; -import java.util.HashSet; -import java.util.Set; - -import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.bean.EntityBeanIntercept; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; import com.avaje.ebeaninternal.server.deploy.BeanProperty; +/** + * Create a new CachedBeanData based on the existing CachedBeanData and the updated bean. + */ public class CachedBeanDataUpdate { - public static CachedBeanData update(BeanDescriptor desc, CachedBeanData data, PersistRequestBean updateRequest){ + /** + * Create a new CachedBeanData based on the existing CachedBeanData and the updated bean. + */ + public static CachedBeanData update(BeanDescriptor desc, CachedBeanData existingData, EntityBean updateBean) { -// -// Set loadedProperties = data.getLoadedProperties(); -// Object[] copyOfData = data.copyData(); -// -// Object updateBean = updateRequest.getBean(); -// Set updatedProperties = updateRequest.getUpdatedProperties(); -// -// int naturalKeyUpdate = -1; -// boolean mergeProperties = false; -// BeanProperty[] props = desc.propertiesNonMany(); -// for (int i = 0; i < props.length; i++) { -// if (updatedProperties.contains(props[i].getName())){ -// if (props[i].isNaturalKey()){ -// naturalKeyUpdate = i; -// } -// copyOfData[i] = props[i].getCacheDataValue(updateBean); -// if (loadedProperties != null && !mergeProperties && !loadedProperties.contains(props[i].getName())){ -// mergeProperties = true; -// } -// } -// } -// -// if (mergeProperties){ -// HashSet mergeProps = new HashSet(); -// mergeProps.addAll(loadedProperties); -// mergeProps.addAll(updatedProperties); -// loadedProperties = mergeProps; -// } -// -// return new CachedBeanData(null, loadedProperties, copyOfData, naturalKeyUpdate); - - return null; + // take a copy of the raw data and loaded status + boolean[] copyLoaded = existingData.copyLoaded(); + Object[] copyData = existingData.copyData(); + + EntityBeanIntercept ebi = updateBean._ebean_getIntercept(); + + Object newNaturalKey = null; + Object oldNaturalKey = existingData.getNaturalKey(); + + BeanProperty[] props = desc.propertiesNonMany(); + for (int i = 0; i < props.length; i++) { + // check if the properties was in the update + int propertyIndex = props[i].getPropertyIndex(); + if (ebi.isLoadedProperty(propertyIndex)) { + if (props[i].isNaturalKey()) { + newNaturalKey = updateBean._ebean_getField(i); + } + // set the cache safe value for the property and mark it as loaded + copyData[propertyIndex] = props[i].getCacheDataValue(updateBean); + copyLoaded[propertyIndex] = true; + } } - - + + return new CachedBeanData(null, copyLoaded, copyData, newNaturalKey, oldNaturalKey); + } } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/cache/CachedManyIds.java b/src/main/java/com/avaje/ebeaninternal/server/cache/CachedManyIds.java index 3f123efa7..2513ee382 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cache/CachedManyIds.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cache/CachedManyIds.java @@ -2,16 +2,22 @@ package com.avaje.ebeaninternal.server.cache; import java.util.List; +/** + * The cached data for O2M and M2M relationships. + *

+ * This is effectively just the Id values for each of the beans in the collection. + *

+ */ public class CachedManyIds { - private final List idList; - - public CachedManyIds(List idList) { - this.idList = idList; - } + private final List idList; - public List getIdList() { - return idList; - } + public CachedManyIds(List idList) { + this.idList = idList; + } + + public List getIdList() { + return idList; + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java index 514ab722c..6c12c50e0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java @@ -136,7 +136,7 @@ public class DefaultBeanLoader { } } else if (loadRequest.isLoadCache()) { Object parentId = desc.getId(bc.getOwnerBean()); - desc.cachePutMany(many, bc, parentId); + desc.cacheManyPropPut(many, bc, parentId); } } } @@ -179,13 +179,13 @@ public class DefaultBeanLoader { pc.put(parentId, parentBean); } - boolean useManyIdCache = beanCollection != null && parentDesc.cacheIsUseManyId(); + boolean useManyIdCache = beanCollection != null && parentDesc.isManyPropCaching(); if (useManyIdCache) { Boolean readOnly = null; if (ebi != null && ebi.isReadOnly()) { readOnly = Boolean.TRUE; } - if (parentDesc.cacheLoadMany(many, beanCollection, parentId, readOnly)) { + if (parentDesc.cacheManyPropLoad(many, beanCollection, parentId, readOnly)) { return; } } @@ -238,7 +238,7 @@ public class DefaultBeanLoader { logger.debug("BeanCollection after load was empty. Owner:" + beanCollection.getOwnerBean()); } } else if (useManyIdCache) { - parentDesc.cachePutMany(many, beanCollection, parentId); + parentDesc.cacheManyPropPut(many, beanCollection, parentId); } } } @@ -312,7 +312,7 @@ public class DefaultBeanLoader { if (loadRequest.isLoadCache()) { for (int i = 0; i < list.size(); i++) { - desc.cachePutBeanData((EntityBean)list.get(i)); + desc.cacheBeanPutData((EntityBean)list.get(i)); } } @@ -351,7 +351,7 @@ public class DefaultBeanLoader { if (ebi != null) { if (SpiQuery.Mode.LAZYLOAD_BEAN.equals(mode) && desc.isBeanCaching()) { // lazy loading and the bean cache is active - if (desc.loadFromCache((EntityBean)bean, ebi, id)) { + if (desc.cacheBeanLoad((EntityBean)bean, ebi, id)) { return; } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java index a8f2e4f37..91ef80d96 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java @@ -1180,7 +1180,7 @@ public final class DefaultServer implements SpiEbeanServer { } // Hit the L2 bean cache - Object cachedBean = beanDescriptor.cacheGetBean(query.getId(), query.isReadOnly()); + Object cachedBean = beanDescriptor.cacheBeanGet(query.getId(), query.isReadOnly()); if (cachedBean != null) { if (context == null) { context = new DefaultPersistenceContext(); @@ -1249,7 +1249,7 @@ public final class DefaultServer implements SpiEbeanServer { // check if it is a find by unique id NaturalKeyBindParam keyBindParam = q.getNaturalKeyBindParam(); if (keyBindParam != null && desc.cacheIsNaturalKey(keyBindParam.getName())) { - Object id2 = desc.cacheGetNaturalKeyId(keyBindParam.getValue()); + Object id2 = desc.cacheNaturalKeyLookup(keyBindParam.getValue()); if (id2 != null) { SpiQuery copy = q.copy(); copy.convertWhereNaturalKeyToId(id2); diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java index 1201bf44c..206739ba7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java @@ -151,10 +151,10 @@ public class PersistRequestBean extends PersistRequest implements BeanPersist if (notifyCache) { switch (type) { case INSERT: - beanDescriptor.cacheInsert(idValue, this); + beanDescriptor.cacheHandleInsert(idValue, this); break; case UPDATE: - beanDescriptor.cacheUpdate(idValue, this); + beanDescriptor.cacheHandleUpdate(idValue, this); break; case DELETE: // Bean deleted from cache early via postDelete() @@ -436,7 +436,7 @@ public class PersistRequestBean extends PersistRequest implements BeanPersist // Delete the bean from the PersistenceContent transaction.getPersistenceContext().clear(beanDescriptor.getBeanType(), idValue); // Delete from cache early even if transaction fails - beanDescriptor.cacheDelete(idValue, this); + beanDescriptor.cacheHandleDelete(idValue, this); } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java index d02d95377..54932257f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java @@ -3,7 +3,6 @@ package com.avaje.ebeaninternal.server.deploy; import java.lang.reflect.Modifier; import java.sql.SQLException; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; @@ -16,7 +15,9 @@ import java.util.concurrent.ConcurrentHashMap; import javax.persistence.PersistenceException; -import com.avaje.ebean.Query; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.avaje.ebean.SqlUpdate; import com.avaje.ebean.Transaction; import com.avaje.ebean.annotation.ConcurrencyMode; @@ -24,8 +25,6 @@ import com.avaje.ebean.bean.BeanCollection; import com.avaje.ebean.bean.EntityBean; import com.avaje.ebean.bean.EntityBeanIntercept; import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebean.cache.ServerCache; -import com.avaje.ebean.cache.ServerCacheManager; import com.avaje.ebean.config.EncryptKey; import com.avaje.ebean.config.dbplatform.IdGenerator; import com.avaje.ebean.config.dbplatform.IdType; @@ -43,9 +42,6 @@ import com.avaje.ebeaninternal.api.SpiQuery; import com.avaje.ebeaninternal.api.SpiUpdatePlan; import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD; import com.avaje.ebeaninternal.server.cache.CachedBeanData; -import com.avaje.ebeaninternal.server.cache.CachedBeanDataFromBean; -import com.avaje.ebeaninternal.server.cache.CachedBeanDataToBean; -import com.avaje.ebeaninternal.server.cache.CachedBeanDataUpdate; import com.avaje.ebeaninternal.server.cache.CachedManyIds; import com.avaje.ebeaninternal.server.core.CacheOptions; import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate; @@ -76,9 +72,6 @@ import com.avaje.ebeaninternal.util.SortByClause; import com.avaje.ebeaninternal.util.SortByClause.Property; import com.avaje.ebeaninternal.util.SortByClauseParser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * Describes Beans including their deployment information. */ @@ -130,11 +123,6 @@ public class BeanDescriptor implements MetaBeanInfo { private final boolean autoFetchTunable; - /** - * Flag indicating this bean has no relationships. - */ - private final boolean cacheSharableBeans; - private final String lazyFetchIncludes; /** @@ -227,8 +215,6 @@ public class BeanDescriptor implements MetaBeanInfo { private final BeanProperty versionProperty; private final int versionPropertyIndex; - private final BeanProperty propertiesNaturalKey; - /** * Properties local to this type (not from a super type). */ @@ -330,29 +316,23 @@ public class BeanDescriptor implements MetaBeanInfo { */ private final boolean updateChangesOnly; - private final ServerCacheManager cacheManager; - - private final CacheOptions cacheOptions; + private final boolean cacheSharableBeans; + + private final BeanDescriptorCacheHelp cacheHelp; private final String defaultSelectClause; private final Set defaultSelectClauseSet; private final String descriptorId; - private SpiEbeanServer ebeanServer; - private ServerCache beanCache; - private ServerCache naturalKeyCache; - private ServerCache queryCache; - /** * Construct the BeanDescriptor. */ public BeanDescriptor(BeanDescriptorMap owner, TypeManager typeManager, DeployBeanDescriptor deploy, String descriptorId) { this.owner = owner; - this.cacheManager = owner.getCacheManager(); this.serverName = owner.getServerName(); this.entityType = deploy.getEntityType(); this.properties = deploy.getProperties(); @@ -373,7 +353,6 @@ public class BeanDescriptor implements MetaBeanInfo { this.persistController = deploy.getPersistController(); this.persistListener = deploy.getPersistListener(); this.queryAdapter = deploy.getQueryAdapter(); - this.cacheOptions = deploy.getCacheOptions(); this.defaultSelectClause = deploy.getDefaultSelectClause(); this.defaultSelectClauseSet = deploy.parseDefaultSelectClause(defaultSelectClause); @@ -408,7 +387,6 @@ public class BeanDescriptor implements MetaBeanInfo { this.propertiesNonTransient = listHelper.getNonTransients(); this.propertiesBaseScalar = listHelper.getBaseScalar(); this.propertiesBaseCompound = listHelper.getBaseCompound(); - this.propertiesNaturalKey = listHelper.getNaturalKey(); this.propertiesEmbedded = listHelper.getEmbedded(); this.propertiesLocal = listHelper.getLocal(); this.unidirectional = listHelper.getUnidirectional(); @@ -425,14 +403,18 @@ public class BeanDescriptor implements MetaBeanInfo { this.propertiesManySave = listHelper.getManySave(); this.propertiesManyDelete = listHelper.getManyDelete(); this.propertiesManyToMany = listHelper.getManyToMany(); - boolean noRelationships = propertiesOne.length + propertiesMany.length == 0; - this.cacheSharableBeans = noRelationships && cacheOptions.isReadOnly(); this.namesOfManyProps = deriveManyPropNames(); this.namesOfManyPropsHash = namesOfManyProps.hashCode(); this.derivedTableJoins = listHelper.getTableJoin(); + boolean noRelationships = propertiesOne.length + propertiesMany.length == 0; + + this.cacheSharableBeans = noRelationships && deploy.getCacheOptions().isReadOnly(); + this.cacheHelp = new BeanDescriptorCacheHelp(this, owner.getCacheManager(), deploy.getCacheOptions(), cacheSharableBeans, propertiesOneImported); + + // Check if there are no cascade save associated beans ( subject to change // in initialiseOther()). Note that if we are in an inheritance hierarchy // then we also need to check every BeanDescriptors in the InheritInfo as @@ -615,7 +597,6 @@ public class BeanDescriptor implements MetaBeanInfo { namedUpdate.initialise(parser); } } - } public void initInheritInfo() { @@ -634,12 +615,7 @@ public class BeanDescriptor implements MetaBeanInfo { * Initialise the cache once the server has started. */ public void cacheInitialise() { - if (cacheOptions.isUseNaturalKeyCache()) { - this.naturalKeyCache = cacheManager.getNaturalKeyCache(beanType); - } - if (cacheOptions.isUseCache()) { - this.beanCache = cacheManager.getBeanCache(beanType); - } + cacheHelp.initialise(); } protected boolean hasInheritance() { @@ -713,7 +689,7 @@ public class BeanDescriptor implements MetaBeanInfo { * Return the cache options. */ public CacheOptions getCacheOptions() { - return cacheOptions; + return cacheHelp.getCacheOptions(); } /** @@ -734,21 +710,7 @@ public class BeanDescriptor implements MetaBeanInfo { * Execute the warming cache query (if defined) and load the cache. */ public void runCacheWarming() { - if (cacheOptions == null) { - return; - } - String warmingQuery = cacheOptions.getWarmingQuery(); - if (warmingQuery != null && warmingQuery.trim().length() > 0) { - Query query = ebeanServer.createQuery(beanType, warmingQuery); - query.setUseCache(true); - query.setReadOnly(true); - query.setLoadBeanCache(true); - List list = query.findList(); - if (logger.isInfoEnabled()) { - String msg = "Loaded " + beanType + " cache with [" + list.size() + "] beans"; - logger.info(msg); - } - } + cacheHelp.runCacheWarming(ebeanServer); } /** @@ -785,17 +747,17 @@ public class BeanDescriptor implements MetaBeanInfo { * Return true if there is currently query caching for this type of bean. */ public boolean isQueryCaching() { - return queryCache != null; + return cacheHelp.isQueryCaching(); } /** * Return true if there is currently bean caching for this type of bean. */ public boolean isBeanCaching() { - return beanCache != null; + return cacheHelp.isBeanCaching(); } - public boolean cacheIsUseManyId() { + public boolean isManyPropCaching() { return isBeanCaching(); } @@ -803,271 +765,145 @@ public class BeanDescriptor implements MetaBeanInfo { * Return true if the persist request needs to notify the cache. */ public boolean isCacheNotify() { - - if (isBeanCaching() || isQueryCaching()) { - return true; - } - for (int i = 0; i < propertiesOneImported.length; i++) { - if (propertiesOneImported[i].getTargetDescriptor().isBeanCaching()) { - return true; - } - } - return false; - } - - /** - * Return true if there is L2 bean caching for this bean type. - */ - public boolean isUsingL2Cache() { - return isBeanCaching(); - } - - /** - * Invalidate parts of cache due to SqlUpdate or external modification etc. - */ - public void cacheNotify(TableIUD tableIUD) { - // inserts don't invalidate the bean cache - if (tableIUD.isUpdateOrDelete()) { - cacheClear(); - } - // any change invalidates the query cache - queryCacheClear(); + return cacheHelp.isCacheNotify(); } /** * Clear the query cache. */ public void queryCacheClear() { - if (queryCache != null) { - queryCache.clear(); - } + cacheHelp.queryCacheClear(); } /** * Get a query result from the query cache. */ - @SuppressWarnings("unchecked") public BeanCollection queryCacheGet(Object id) { - if (queryCache == null) { - return null; - } else { - return (BeanCollection) queryCache.get(id); - } + return cacheHelp.queryCacheGet(id); } /** * Put a query result into the query cache. */ public void queryCachePut(Object id, BeanCollection query) { - if (queryCache == null) { - queryCache = cacheManager.getQueryCache(beanType); - } - queryCache.put(id, query); + cacheHelp.queryCachePut(id, query); } - private ServerCache getBeanCache() { - if (beanCache == null) { - beanCache = cacheManager.getBeanCache(beanType); - } - return beanCache; + + + /** + * Try to load the beanCollection from cache return true if successful. + */ + public boolean cacheManyPropLoad(BeanPropertyAssocMany many, BeanCollection bc, Object parentId, Boolean readOnly) { + return cacheHelp.manyPropLoad(many, bc, parentId, readOnly); + } + + /** + * Put the beanCollection into the cache. + */ + public void cacheManyPropPut(BeanPropertyAssocMany many, BeanCollection bc, Object parentId) { + cacheHelp.manyPropPut(many, bc, parentId); + } + + public void cacheManyPropRemove(Object parentId, String propertyName) { + cacheHelp.manyPropRemove(parentId, propertyName); + } + + public void cacheManyPropClear(String propertyName) { + cacheHelp.manyPropClear(propertyName); + } + + /** + * Return the CachedManyIds for a given bean and property. Returns null if not in the cache. + */ + public CachedManyIds cacheManyPropGet(Object parentId, String propertyName) { + return cacheHelp.manyPropGet(parentId, propertyName); + } + + /** + * Put the CachedManyIds into the cache. + */ + public void cacheManyPropPutEntry(Object parentId, String propertyName, CachedManyIds ids) { + cacheHelp.manyPropPutEntry(parentId, propertyName, ids); } /** * Clear the bean cache. */ - public void cacheClear() { - if (beanCache != null) { - beanCache.clear(); - } + public void cacheBeanClear() { + cacheHelp.beanCacheClear(); } - public void cachePutBean(T bean) { - cachePutBeanData((EntityBean)bean); + public void cacheBeanPut(T bean) { + cacheBeanPutData((EntityBean)bean); } /** * Put a bean into the bean cache. */ - public void cachePutBeanData(EntityBean bean) { - - CachedBeanData beanData = CachedBeanDataFromBean.extract(this, bean); - - Object id = getId(bean); - getBeanCache().put(id, beanData); - if (beanData.isNaturalKeyUpdate() && naturalKeyCache != null) { - Object naturalKey = beanData.getNaturalKey(); - if (naturalKey != null) { - naturalKeyCache.put(naturalKey, id); - } - } + public void cacheBeanPutData(EntityBean bean) { + cacheHelp.beanCachePut(bean); } - - public boolean cacheLoadMany(BeanPropertyAssocMany many, BeanCollection bc, Object parentId, Boolean readOnly) { - - CachedManyIds ids = cacheGetCachedManyIds(parentId, many.getName()); - if (ids == null) { - return false; - } - - Object ownerBean = bc.getOwnerBean(); - EntityBeanIntercept ebi = ((EntityBean) ownerBean)._ebean_getIntercept(); - PersistenceContext persistenceContext = ebi.getPersistenceContext(); - - BeanDescriptor targetDescriptor = many.getTargetDescriptor(); - - List idList = ids.getIdList(); - bc.checkEmptyLazyLoad(); - for (int i = 0; i < idList.size(); i++) { - Object id = idList.get(i); - Object refBean = targetDescriptor.createReference(readOnly, id); - EntityBeanIntercept refEbi = ((EntityBean) refBean)._ebean_getIntercept(); - - many.add(bc, (EntityBean)refBean); - persistenceContext.put(id, refBean); - refEbi.setPersistenceContext(persistenceContext); - } - return true; - } - - public void cachePutMany(BeanPropertyAssocMany many, BeanCollection bc, Object parentId) { - BeanDescriptor targetDescriptor = many.getTargetDescriptor(); - ArrayList idList = new ArrayList(); - - // get the underlying collection of beans (in the List, Set or Map) - Collection actualDetails = bc.getActualDetails(); - for (Object bean : actualDetails) { - // Collect the id values - idList.add(targetDescriptor.getId((EntityBean)bean)); - } - CachedManyIds ids = new CachedManyIds(idList); - cachePutCachedManyIds(parentId, many.getName(), ids); - } - - public void cacheRemoveCachedManyIds(Object parentId, String propertyName) { - ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName); - collectionIdsCache.remove(parentId); - } - - public void cacheClearCachedManyIds(String propertyName) { - ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName); - collectionIdsCache.clear(); - } - - public CachedManyIds cacheGetCachedManyIds(Object parentId, String propertyName) { - ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName); - return (CachedManyIds) collectionIdsCache.get(parentId); - } - - public void cachePutCachedManyIds(Object parentId, String propertyName, CachedManyIds ids) { - ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName); - collectionIdsCache.put(parentId, ids); - } - + /** * Return a bean from the bean cache. */ - @SuppressWarnings("unchecked") - public T cacheGetBean(Object id, Boolean readOnly) { - - CachedBeanData d = (CachedBeanData) getBeanCache().get(id); - if (d == null) { - return null; - } - if (cacheSharableBeans && !Boolean.FALSE.equals(readOnly)) { - Object bean = d.getSharableBean(); - if (bean != null) { - return (T) bean; - } - } - - EntityBean bean = createBean(); - convertSetId(id, bean); - if (Boolean.TRUE.equals(readOnly)) { - bean._ebean_getIntercept().setReadOnly(true); - } - - CachedBeanDataToBean.load(this, bean, d); - return (T)bean; + public T cacheBeanGet(Object id, Boolean readOnly) { + return cacheHelp.beanCacheGet(id, readOnly); } + /** + * Remove a bean from the cache given its Id. + */ + public void cacheBeanRemove(Object id) { + cacheHelp.beanCacheRemove(id); + } + + public boolean cacheBeanLoad(EntityBean bean, EntityBeanIntercept ebi, Object id) { + return cacheHelp.beanCacheLoad(bean, ebi, id); + } + + public boolean cacheBeanLoad(EntityBeanIntercept ebi) { + EntityBean bean = ebi.getOwner(); + Object id = getId(bean); + + return cacheBeanLoad(bean, ebi, id); + } + + public boolean cacheIsNaturalKey(String propName) { - return propName != null && propName.equals(cacheOptions.getNaturalKey()); + return cacheHelp.isNaturalKey(propName); } - public Object cacheGetNaturalKeyId(Object uniqueKeyValue) { - if (naturalKeyCache != null) { - return naturalKeyCache.get(uniqueKeyValue); - } - return null; + public Object cacheNaturalKeyLookup(Object uniqueKeyValue) { + return cacheHelp.naturalKeyLookup(uniqueKeyValue); + } + + /** + * Invalidate parts of cache due to SqlUpdate or external modification etc. + */ + public void cacheHandleBulkUpdate(TableIUD tableIUD) { + cacheHelp.handleBulkUpdate(tableIUD); } /** * Remove a bean from the cache given its Id. */ - public void cacheRemove(Object id) { - if (beanCache != null) { - beanCache.remove(id); - } - for (int i = 0; i < propertiesOneImported.length; i++) { - propertiesOneImported[i].cacheClear(); - } + public void cacheHandleDelete(Object id, PersistRequestBean deleteRequest) { + cacheHelp.handleDelete(id, deleteRequest); } - /** - * Remove a bean from the cache given its Id. - */ - public void cacheDelete(Object id, PersistRequestBean deleteRequest) { - if (queryCache != null) { - queryCache.clear(); - } - if (beanCache != null) { - beanCache.remove(id); - } - for (int i = 0; i < propertiesOneImported.length; i++) { - BeanPropertyAssocMany many = propertiesOneImported[i].getRelationshipProperty(); - if (many != null) { - propertiesOneImported[i].cacheDelete(true, deleteRequest.getEntityBean()); - } - } - } - - public void cacheInsert(Object id, PersistRequestBean insertRequest) { - if (queryCache != null) { - queryCache.clear(); - } - for (int i = 0; i < propertiesOneImported.length; i++) { - propertiesOneImported[i].cacheDelete(false, insertRequest.getEntityBean()); - } + public void cacheHandleInsert(Object id, PersistRequestBean insertRequest) { + cacheHelp.handleInsert(id, insertRequest); } /** * Update the cached bean data. */ - public void cacheUpdate(Object id, PersistRequestBean updateRequest) { - - if (queryCache != null) { - queryCache.clear(); - } - - ServerCache cache = getBeanCache(); - CachedBeanData cd = (CachedBeanData) cache.get(id); - if (cd != null) { - CachedBeanData newCd = CachedBeanDataUpdate.update(this, cd, updateRequest); - cache.put(id, newCd); - if (newCd.isNaturalKeyUpdate() && naturalKeyCache != null) { - //FIXME: natural key invalidate old value - //Object oldKey = propertiesNaturalKey.getValue(updateRequest.getOldValues()); - Object newKey = propertiesNaturalKey.getValue(updateRequest.getEntityBean()); - //if (oldKey != null) { - // naturalKeyCache.remove(oldKey); - //} - if (newKey != null) { - naturalKeyCache.put(newKey, id); - } - } - } + public void cacheHandleUpdate(Object id, PersistRequestBean updateRequest) { + cacheHelp.handleUpdate(id, updateRequest); } - + /** * Return the base table alias. This is always the first letter of the bean * name. @@ -1076,28 +912,6 @@ public class BeanDescriptor implements MetaBeanInfo { return baseTableAlias; } - public boolean loadFromCache(EntityBeanIntercept ebi) { - EntityBean bean = ebi.getOwner(); - Object id = getId(bean); - - return loadFromCache(bean, ebi, id); - } - - public boolean loadFromCache(EntityBean bean, EntityBeanIntercept ebi, Object id) { - - CachedBeanData cacheData = (CachedBeanData) getBeanCache().get(id); - if (cacheData == null) { - return false; - } - int lazyLoadProperty = ebi.getLazyLoadPropertyIndex(); - if (lazyLoadProperty > -1 && !cacheData.containsProperty(lazyLoadProperty)) { - return false; - } - - CachedBeanDataToBean.load(this, bean, cacheData); - return true; - } - public void preAllocateIds(int batchSize) { if (idGenerator != null) { idGenerator.preAllocateIds(batchSize); @@ -1327,10 +1141,10 @@ public class BeanDescriptor implements MetaBeanInfo { * Create a reference bean based on the id. */ @SuppressWarnings("unchecked") - public T createReference(Boolean readOnly, Object id) {//, Object parent) { + public T createReference(Boolean readOnly, Object id) { if (cacheSharableBeans && !Boolean.FALSE.equals(readOnly)) { - CachedBeanData d = (CachedBeanData) getBeanCache().get(id); + CachedBeanData d = (CachedBeanData) cacheHelp.beanCacheGetData(id); if (d != null) { Object shareableBean = d.getSharableBean(); if (shareableBean != null) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java new file mode 100644 index 000000000..c2ed3f5c0 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java @@ -0,0 +1,411 @@ +package com.avaje.ebeaninternal.server.deploy; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.Query; +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.bean.EntityBeanIntercept; +import com.avaje.ebean.bean.PersistenceContext; +import com.avaje.ebean.cache.ServerCache; +import com.avaje.ebean.cache.ServerCacheManager; +import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD; +import com.avaje.ebeaninternal.server.cache.CachedBeanData; +import com.avaje.ebeaninternal.server.cache.CachedBeanDataFromBean; +import com.avaje.ebeaninternal.server.cache.CachedBeanDataToBean; +import com.avaje.ebeaninternal.server.cache.CachedBeanDataUpdate; +import com.avaje.ebeaninternal.server.cache.CachedManyIds; +import com.avaje.ebeaninternal.server.core.CacheOptions; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; + +/** + * Helper for BeanDescriptor that manages the bean, query and collection caches. + * + * @param The entity bean type + */ +public final class BeanDescriptorCacheHelp { + + private static final Logger logger = LoggerFactory.getLogger(BeanDescriptorCacheHelp.class); + + private final BeanDescriptor desc; + + private final ServerCacheManager cacheManager; + + private final CacheOptions cacheOptions; + + /** + * Flag indicating this bean has no relationships. + */ + private final boolean cacheSharableBeans; + + private final Class beanType; + + private final BeanPropertyAssocOne[] propertiesOneImported; + + private ServerCache beanCache; + private ServerCache naturalKeyCache; + private ServerCache queryCache; + + public BeanDescriptorCacheHelp(BeanDescriptor desc, ServerCacheManager cacheManager, CacheOptions cacheOptions, + boolean cacheSharableBeans, BeanPropertyAssocOne[] propertiesOneImported) { + + this.desc = desc; + this.beanType = desc.getBeanType(); + this.cacheManager = cacheManager; + this.cacheOptions = cacheOptions; + this.cacheSharableBeans = cacheSharableBeans; + this.propertiesOneImported = propertiesOneImported; + } + + /** + * Initialise the cache once the server has started. + */ + public void initialise() { + if (cacheOptions.isUseNaturalKeyCache()) { + this.naturalKeyCache = cacheManager.getNaturalKeyCache(beanType); + } + if (cacheOptions.isUseCache()) { + this.beanCache = cacheManager.getBeanCache(beanType); + } + } + + /** + * Execute the warming cache query (if defined) and load the cache. + */ + public void runCacheWarming(EbeanServer ebeanServer) { + if (cacheOptions == null) { + return; + } + String warmingQuery = cacheOptions.getWarmingQuery(); + if (warmingQuery != null && warmingQuery.trim().length() > 0) { + Query query = ebeanServer.createQuery(beanType, warmingQuery); + query.setUseCache(true); + query.setReadOnly(true); + query.setLoadBeanCache(true); + List list = query.findList(); + if (logger.isInfoEnabled()) { + String msg = "Loaded " + beanType + " cache with [" + list.size() + "] beans"; + logger.info(msg); + } + } + } + + /** + * Return true if there is currently query caching for this type of bean. + */ + public boolean isQueryCaching() { + return queryCache != null; + } + + /** + * Return true if there is currently bean caching for this type of bean. + */ + public boolean isBeanCaching() { + return beanCache != null; + } + + /** + * Return true if the persist request needs to notify the cache. + */ + public boolean isCacheNotify() { + + if (isBeanCaching() || isQueryCaching()) { + return true; + } + for (int i = 0; i < propertiesOneImported.length; i++) { + if (propertiesOneImported[i].getTargetDescriptor().isBeanCaching()) { + return true; + } + } + return false; + } + + public CacheOptions getCacheOptions() { + return cacheOptions; + } + + /** + * Clear the query cache. + */ + public void queryCacheClear() { + if (queryCache != null) { + queryCache.clear(); + } + } + + + /** + * Get a query result from the query cache. + */ + @SuppressWarnings("unchecked") + public BeanCollection queryCacheGet(Object id) { + if (queryCache == null) { + return null; + } else { + return (BeanCollection) queryCache.get(id); + } + } + + /** + * Put a query result into the query cache. + */ + public void queryCachePut(Object id, BeanCollection query) { + if (queryCache == null) { + queryCache = cacheManager.getQueryCache(beanType); + } + queryCache.put(id, query); + } + + + public void manyPropRemove(Object parentId, String propertyName) { + ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName); + collectionIdsCache.remove(parentId); + } + + public void manyPropClear(String propertyName) { + ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName); + collectionIdsCache.clear(); + } + + /** + * Return the CachedManyIds for a given bean many property. Returns null if not in the cache. + */ + public CachedManyIds manyPropGet(Object parentId, String propertyName) { + ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName); + return (CachedManyIds) collectionIdsCache.get(parentId); + } + + /** + * Put the CachedManyIds into the cache. + */ + public void manyPropPutEntry(Object parentId, String propertyName, CachedManyIds ids) { + ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName); + collectionIdsCache.put(parentId, ids); + } + + /** + * Try to load the bean collection from cache return true if successful. + */ + public boolean manyPropLoad(BeanPropertyAssocMany many, BeanCollection bc, Object parentId, Boolean readOnly) { + + CachedManyIds ids = manyPropGet(parentId, many.getName()); + if (ids == null) { + // not in cache so return unsuccessful + return false; + } + + Object ownerBean = bc.getOwnerBean(); + EntityBeanIntercept ebi = ((EntityBean) ownerBean)._ebean_getIntercept(); + PersistenceContext persistenceContext = ebi.getPersistenceContext(); + + BeanDescriptor targetDescriptor = many.getTargetDescriptor(); + + List idList = ids.getIdList(); + bc.checkEmptyLazyLoad(); + for (int i = 0; i < idList.size(); i++) { + Object id = idList.get(i); + Object refBean = targetDescriptor.createReference(readOnly, id); + EntityBeanIntercept refEbi = ((EntityBean) refBean)._ebean_getIntercept(); + + many.add(bc, (EntityBean) refBean); + persistenceContext.put(id, refBean); + refEbi.setPersistenceContext(persistenceContext); + } + return true; + } + + /** + * Put the beanCollection into the cache. + */ + public void manyPropPut(BeanPropertyAssocMany many, BeanCollection bc, Object parentId) { + + BeanDescriptor targetDescriptor = many.getTargetDescriptor(); + ArrayList idList = new ArrayList(); + + // get the underlying collection of beans (in the List, Set or Map) + Collection actualDetails = bc.getActualDetails(); + for (Object bean : actualDetails) { + // Collect the id values + idList.add(targetDescriptor.getId((EntityBean) bean)); + } + CachedManyIds ids = new CachedManyIds(idList); + manyPropPutEntry(parentId, many.getName(), ids); + } + + + public boolean isNaturalKey(String propName) { + return propName != null && propName.equals(cacheOptions.getNaturalKey()); + } + + public Object naturalKeyLookup(Object uniqueKeyValue) { + if (naturalKeyCache != null) { + return naturalKeyCache.get(uniqueKeyValue); + } + return null; + } + + private ServerCache getBeanCache() { + if (beanCache == null) { + beanCache = cacheManager.getBeanCache(beanType); + } + return beanCache; + } + + /** + * Clear the bean cache. + */ + public void beanCacheClear() { + if (beanCache != null) { + beanCache.clear(); + } + } + + /** + * Put a bean into the bean cache. + */ + public void beanCachePut(EntityBean bean) { + + CachedBeanData beanData = CachedBeanDataFromBean.extract(desc, bean); + + Object id = desc.getId(bean); + getBeanCache().put(id, beanData); + if (beanData.isNaturalKeyUpdate() && naturalKeyCache != null) { + Object naturalKey = beanData.getNaturalKey(); + if (naturalKey != null) { + naturalKeyCache.put(naturalKey, id); + } + } + } + + public CachedBeanData beanCacheGetData(Object id) { + return (CachedBeanData) getBeanCache().get(id); + } + + /** + * Return a bean from the bean cache. + */ + @SuppressWarnings("unchecked") + public T beanCacheGet(Object id, Boolean readOnly) { + + CachedBeanData d = (CachedBeanData) getBeanCache().get(id); + if (d == null) { + return null; + } + if (cacheSharableBeans && !Boolean.FALSE.equals(readOnly)) { + Object bean = d.getSharableBean(); + if (bean != null) { + return (T) bean; + } + } + + EntityBean bean = desc.createBean(); + desc.convertSetId(id, bean); + if (Boolean.TRUE.equals(readOnly)) { + bean._ebean_getIntercept().setReadOnly(true); + } + + CachedBeanDataToBean.load(desc, bean, d); + return (T) bean; + } + + + /** + * Remove a bean from the cache given its Id. + */ + public void beanCacheRemove(Object id) { + if (beanCache != null) { + beanCache.remove(id); + } + for (int i = 0; i < propertiesOneImported.length; i++) { + propertiesOneImported[i].cacheClear(); + } + } + + public boolean beanCacheLoad(EntityBean bean, EntityBeanIntercept ebi, Object id) { + + CachedBeanData cacheData = (CachedBeanData) getBeanCache().get(id); + if (cacheData == null) { + return false; + } + int lazyLoadProperty = ebi.getLazyLoadPropertyIndex(); + if (lazyLoadProperty > -1 && !cacheData.containsProperty(lazyLoadProperty)) { + return false; + } + + CachedBeanDataToBean.load(desc, bean, cacheData); + return true; + } + + /** + * Remove a bean from the cache given its Id. + */ + public void handleDelete(Object id, PersistRequestBean deleteRequest) { + if (queryCache != null) { + queryCache.clear(); + } + if (beanCache != null) { + beanCache.remove(id); + } + for (int i = 0; i < propertiesOneImported.length; i++) { + BeanPropertyAssocMany many = propertiesOneImported[i].getRelationshipProperty(); + if (many != null) { + propertiesOneImported[i].cacheDelete(true, deleteRequest.getEntityBean()); + } + } + } + + public void handleInsert(Object id, PersistRequestBean insertRequest) { + if (queryCache != null) { + queryCache.clear(); + } + for (int i = 0; i < propertiesOneImported.length; i++) { + propertiesOneImported[i].cacheDelete(false, insertRequest.getEntityBean()); + } + } + + /** + * Update the cached bean data. + */ + public void handleUpdate(Object id, PersistRequestBean updateRequest) { + + if (queryCache != null) { + queryCache.clear(); + } + + ServerCache cache = getBeanCache(); + CachedBeanData existingData = (CachedBeanData) cache.get(id); + if (existingData != null) { + CachedBeanData newData = CachedBeanDataUpdate.update(desc, existingData, updateRequest.getEntityBean()); + cache.put(id, newData); + if (newData.isNaturalKeyUpdate() && naturalKeyCache != null) { + + Object oldKey = newData.getOldNaturalKey(); + Object newKey = newData.getNaturalKey(); + if (oldKey != null) { + naturalKeyCache.remove(oldKey); + } + if (newKey != null) { + naturalKeyCache.put(newKey, id); + } + } + } + } + + /** + * Invalidate parts of cache due to SqlUpdate or external modification etc. + */ + public void handleBulkUpdate(TableIUD tableIUD) { + // inserts don't invalidate the bean cache + if (tableIUD.isUpdateOrDelete()) { + beanCacheClear(); + } + // any change invalidates the query cache + queryCacheClear(); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java index f9fbab992..e1aba5ba1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java @@ -281,7 +281,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap { List> list = getBeanDescriptors(tableIUD.getTableName()); if (list != null) { for (int i = 0; i < list.size(); i++) { - list.get(i).cacheNotify(tableIUD); + list.get(i).cacheHandleBulkUpdate(tableIUD); } } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java index e9756f7ed..102dcddc5 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java @@ -118,7 +118,7 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc { public void cacheClear() { if (targetDescriptor.isBeanCaching() && relationshipProperty != null) { - targetDescriptor.cacheClearCachedManyIds(relationshipProperty.getName()); + targetDescriptor.cacheManyPropClear(relationshipProperty.getName()); } } @@ -128,12 +128,12 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc { if (assocBean != null) { Object parentId = targetDescriptor.getId((EntityBean)assocBean); if (parentId != null) { - targetDescriptor.cacheRemoveCachedManyIds(parentId, relationshipProperty.getName()); + targetDescriptor.cacheManyPropRemove(parentId, relationshipProperty.getName()); return; } } if (clearOnNull) { - targetDescriptor.cacheClearCachedManyIds(relationshipProperty.getName()); + targetDescriptor.cacheManyPropClear(relationshipProperty.getName()); } } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java index b39aa6b2e..49a816638 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java @@ -36,6 +36,7 @@ public class DeployBeanPropertyLists { private final ArrayList local = new ArrayList(); private final ArrayList manys = new ArrayList(); + private final ArrayList nonManys = new ArrayList(); private final ArrayList ones = new ArrayList(); @@ -177,14 +178,6 @@ public class DeployBeanPropertyLists { return (BeanPropertyCompound[]) baseCompound.toArray(new BeanPropertyCompound[baseCompound.size()]); } - public BeanProperty getNaturalKey() { - String naturalKey = desc.getCacheOptions().getNaturalKey(); - if (naturalKey != null) { - return propertyMap.get(naturalKey); - } - return null; - } - public BeanProperty getId() { if (ids.size() > 1) { String msg = "Ebean does not support multiple @Id properties. You need to convert to using an @EmbeddedId." diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java index b3d590643..10bd7d196 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java @@ -156,7 +156,7 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex return; } - if (context.hitCache && context.desc.loadFromCache(ebi)) { + if (context.hitCache && context.desc.cacheBeanLoad(ebi)) { // successfully hit the L2 cache so don't invoke DB lazy loading list.remove(ebi); return; @@ -167,7 +167,7 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex Iterator iterator = list.iterator(); while (iterator.hasNext()) { EntityBeanIntercept bean = iterator.next(); - if (context.desc.loadFromCache(bean)) { + if (context.desc.cacheBeanLoad(bean)) { iterator.remove(); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java index 57c51fac0..394c4abdc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java @@ -178,7 +178,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex EntityBean ownerBean = bc.getOwnerBean(); BeanDescriptor parentDesc = context.desc.getBeanDescriptor(ownerBean.getClass()); Object parentId = parentDesc.getId(ownerBean); - if (parentDesc.cacheLoadMany(context.property, bc, parentId, context.parent.isReadOnly())) { + if (parentDesc.cacheManyPropLoad(context.property, bc, parentId, context.parent.isReadOnly())) { // we loaded the bean from cache list.remove(bc); return; diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java b/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java index 67cc3a264..78edd7678 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java @@ -412,7 +412,7 @@ public final class DefaultPersister implements Persister { BeanPropertyAssocOne[] expOnes = descriptor.propertiesOneExportedDelete(); for (int i = 0; i < expOnes.length; i++) { BeanDescriptor targetDesc = expOnes[i].getTargetDescriptor(); - if (targetDesc.isDeleteRecurseSkippable() && !targetDesc.isUsingL2Cache()) { + if (targetDesc.isDeleteRecurseSkippable() && !targetDesc.isBeanCaching()) { SqlUpdate sqlDelete = expOnes[i].deleteByParentId(id, idList); executeSqlUpdate(sqlDelete, t); } else { @@ -425,7 +425,7 @@ public final class DefaultPersister implements Persister { BeanPropertyAssocMany[] manys = descriptor.propertiesManyDelete(); for (int i = 0; i < manys.length; i++) { BeanDescriptor targetDesc = manys[i].getTargetDescriptor(); - if (targetDesc.isDeleteRecurseSkippable() && !targetDesc.isUsingL2Cache()) { + if (targetDesc.isDeleteRecurseSkippable() && !targetDesc.isBeanCaching()) { // we can just delete children with a single statement SqlUpdate sqlDelete = manys[i].deleteByParentId(id, idList); executeSqlUpdate(sqlDelete, t); @@ -1053,7 +1053,7 @@ public final class DefaultPersister implements Persister { if (many.getCascadeInfo().isDelete()) { // cascade delete the beans in the collection BeanDescriptor targetDesc = many.getTargetDescriptor(); - if (targetDesc.isDeleteRecurseSkippable() && !targetDesc.isUsingL2Cache()) { + if (targetDesc.isDeleteRecurseSkippable() && !targetDesc.isBeanCaching()) { // Just delete all the children with one statement IntersectionRow intRow = many.buildManyDeleteChildren(parentBean, excludeDetailIds); SqlUpdate sqlDelete = intRow.createDelete(server); diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/DefaultOrmQueryEngine.java b/src/main/java/com/avaje/ebeaninternal/server/query/DefaultOrmQueryEngine.java index 6bf155f1e..e46f92ebb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/DefaultOrmQueryEngine.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/DefaultOrmQueryEngine.java @@ -83,7 +83,7 @@ public class DefaultOrmQueryEngine implements OrmQueryEngine { BeanDescriptor descriptor = request.getBeanDescriptor(); Collection c = result.getActualDetails(); for (T bean : c) { - descriptor.cachePutBeanData((EntityBean)bean); + descriptor.cacheBeanPutData((EntityBean)bean); } } @@ -120,7 +120,7 @@ public class DefaultOrmQueryEngine implements OrmQueryEngine { } if (result != null && request.isUseBeanCache()){ - request.getBeanDescriptor().cachePutBeanData((EntityBean)result); + request.getBeanDescriptor().cacheBeanPutData((EntityBean)result); } return result; diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanPersistIds.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanPersistIds.java index eea4f3f5e..ceb40920e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanPersistIds.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanPersistIds.java @@ -264,7 +264,7 @@ public class BeanPersistIds implements Serializable { Serializable id = updateIds.get(i); // remove from cache - beanDescriptor.cacheRemove(id); + beanDescriptor.cacheBeanRemove(id); if (listener != null) { // notify listener listener.remoteInsert(id); @@ -276,7 +276,7 @@ public class BeanPersistIds implements Serializable { Serializable id = deleteIds.get(i); // remove from cache - beanDescriptor.cacheRemove(id); + beanDescriptor.cacheBeanRemove(id); if (listener != null) { // notify listener listener.remoteInsert(id); diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/DeleteByIdMap.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/DeleteByIdMap.java index 8a7b9f7d7..7a58e12ca 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/DeleteByIdMap.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/DeleteByIdMap.java @@ -27,7 +27,7 @@ public final class DeleteByIdMap { if (idValues != null){ d.queryCacheClear(); for (int i = 0; i < idValues.size(); i++) { - d.cacheRemove(idValues.get(i)); + d.cacheBeanRemove(idValues.get(i)); } } } diff --git a/src/test/java/com/avaje/ebeaninternal/server/cache/TestCacheBeanData.java b/src/test/java/com/avaje/ebeaninternal/server/cache/TestCacheBeanData.java index a99a3589d..17a693e87 100644 --- a/src/test/java/com/avaje/ebeaninternal/server/cache/TestCacheBeanData.java +++ b/src/test/java/com/avaje/ebeaninternal/server/cache/TestCacheBeanData.java @@ -19,7 +19,7 @@ import com.avaje.tests.model.basic.Customer.Status; public class TestCacheBeanData extends BaseTestCase { @Test - public void test() { + public void testCacheBeanExtractAndLoad() { SpiEbeanServer server = (SpiEbeanServer)Ebean.getServer(null); BeanDescriptor desc = server.getBeanDescriptor(Customer.class); @@ -42,8 +42,7 @@ public class TestCacheBeanData extends BaseTestCase { ((EntityBean)c)._ebean_getIntercept().setNewBeanForUpdate(); CachedBeanData cacheData = CachedBeanDataFromBean.extract(desc, (EntityBean)c); - //BeanProperty idProperty = desc.getBeanProperty("id"); - //Assert.assertTrue(cacheData.isLoaded(idProperty.getPropertyIndex())); + Assert.assertNotNull(cacheData); @@ -54,6 +53,19 @@ public class TestCacheBeanData extends BaseTestCase { Assert.assertEquals(c.getId(), newCustomer.getId()); Assert.assertEquals(c.getName(), newCustomer.getName()); Assert.assertEquals(c.getStatus(), newCustomer.getStatus()); + Assert.assertEquals(c.getSmallnote(), newCustomer.getSmallnote()); + Assert.assertEquals(c.getCretime(), newCustomer.getCretime()); + Assert.assertEquals(c.getUpdtime(), newCustomer.getUpdtime()); + Assert.assertEquals(c.getBillingAddress().getId(), newCustomer.getBillingAddress().getId()); + + Assert.assertNotNull(newCustomer.getId()); + Assert.assertNotNull(newCustomer.getName()); + Assert.assertNotNull(newCustomer.getStatus()); + Assert.assertNotNull(newCustomer.getSmallnote()); + Assert.assertNotNull(newCustomer.getCretime()); + Assert.assertNotNull(newCustomer.getUpdtime()); + Assert.assertNotNull(newCustomer.getBillingAddress()); + Assert.assertNotNull(newCustomer.getBillingAddress().getId()); } } diff --git a/src/test/java/com/avaje/tests/basic/delete/TestDeleteByIdList.java b/src/test/java/com/avaje/tests/basic/delete/TestDeleteByIdList.java index 0a76b7b36..0ddbae880 100644 --- a/src/test/java/com/avaje/tests/basic/delete/TestDeleteByIdList.java +++ b/src/test/java/com/avaje/tests/basic/delete/TestDeleteByIdList.java @@ -22,7 +22,7 @@ public class TestDeleteByIdList extends BaseTestCase { OrderDetail dummy = Ebean.getReference(OrderDetail.class, 1); SpiEbeanServer server = (SpiEbeanServer) Ebean.getServer(null); - server.getBeanDescriptor(OrderDetail.class).cachePutBean(dummy); + server.getBeanDescriptor(OrderDetail.class).cacheBeanPut(dummy); Customer c0 = ResetBasicData.createCustAndOrder("DelIdList-0"); Assert.assertNotNull(c0); diff --git a/src/test/java/com/avaje/tests/basic/delete/TestDeleteCascadeById.java b/src/test/java/com/avaje/tests/basic/delete/TestDeleteCascadeById.java index 319aecf8c..aa4553bf5 100644 --- a/src/test/java/com/avaje/tests/basic/delete/TestDeleteCascadeById.java +++ b/src/test/java/com/avaje/tests/basic/delete/TestDeleteCascadeById.java @@ -22,7 +22,7 @@ public class TestDeleteCascadeById extends BaseTestCase { OrderDetail dummy = Ebean.getReference(OrderDetail.class, 1); SpiEbeanServer server = (SpiEbeanServer) Ebean.getServer(null); - server.getBeanDescriptor(OrderDetail.class).cachePutBean(dummy); + server.getBeanDescriptor(OrderDetail.class).cacheBeanPut(dummy); Customer cust = ResetBasicData.createCustAndOrder("DelCas"); Assert.assertNotNull(cust); diff --git a/src/test/java/com/avaje/tests/cache/TestCacheCollectionIds.java b/src/test/java/com/avaje/tests/cache/TestCacheCollectionIds.java index 6e8f802c7..b374aecb2 100644 --- a/src/test/java/com/avaje/tests/cache/TestCacheCollectionIds.java +++ b/src/test/java/com/avaje/tests/cache/TestCacheCollectionIds.java @@ -2,29 +2,35 @@ package com.avaje.tests.cache; import java.util.List; -import com.avaje.ebeaninternal.server.cache.CachedManyIds; -import com.avaje.tests.model.basic.*; import org.junit.Assert; import org.junit.Test; import com.avaje.ebean.BaseTestCase; import com.avaje.ebean.Ebean; import com.avaje.ebean.cache.ServerCache; +import com.avaje.ebean.cache.ServerCacheManager; +import com.avaje.ebeaninternal.server.cache.CachedManyIds; +import com.avaje.tests.model.basic.Contact; +import com.avaje.tests.model.basic.Country; +import com.avaje.tests.model.basic.Customer; +import com.avaje.tests.model.basic.OCachedBean; +import com.avaje.tests.model.basic.ResetBasicData; public class TestCacheCollectionIds extends BaseTestCase { + ServerCacheManager cacheManager = Ebean.getServerCacheManager(); + @Test public void test() { ResetBasicData.reset(); - ServerCache custCache = Ebean.getServerCacheManager().getBeanCache(Customer.class); - ServerCache contactCache = Ebean.getServerCacheManager().getBeanCache(Contact.class); - ServerCache custManyIdsCache = Ebean.getServerCacheManager().getCollectionIdsCache( - Customer.class, "contacts"); + ServerCache custCache = cacheManager.getBeanCache(Customer.class); + ServerCache contactCache = cacheManager.getBeanCache(Contact.class); + ServerCache custManyIdsCache = cacheManager.getCollectionIdsCache(Customer.class, "contacts"); - // Ebean.getServerCacheManager().setCaching(Customer.class, true); - // Ebean.getServerCacheManager().setCaching(Contact.class, true); + // cacheManager.setCaching(Customer.class, true); + // cacheManager.setCaching(Contact.class, true); custCache.clear(); custManyIdsCache.clear(); @@ -94,21 +100,21 @@ public class TestCacheCollectionIds extends BaseTestCase { // arrange ResetBasicData.reset(); - CachedBean cachedBean = new CachedBean(); + OCachedBean cachedBean = new OCachedBean(); cachedBean.getCountries().add(Ebean.find(Country.class, "NZ")); cachedBean.getCountries().add(Ebean.find(Country.class, "AU")); Ebean.save(cachedBean); // act - CachedBean loadedBean = Ebean.find(CachedBean.class, cachedBean.getId()); + OCachedBean loadedBean = Ebean.find(OCachedBean.class, cachedBean.getId()); loadedBean.getCountries().clear(); loadedBean.getCountries().add(Ebean.find(Country.class, "AU")); Ebean.save(cachedBean); - CachedBean result = Ebean.find(CachedBean.class, cachedBean.getId()); - ServerCache cachedBeanCountriesCache = Ebean.getServerCacheManager().getCollectionIdsCache(CachedBean.class, "countries"); + OCachedBean result = Ebean.find(OCachedBean.class, cachedBean.getId()); + ServerCache cachedBeanCountriesCache = cacheManager.getCollectionIdsCache(OCachedBean.class, "countries"); CachedManyIds cachedManyIds = (CachedManyIds) cachedBeanCountriesCache.get(result.getId()); @@ -127,21 +133,44 @@ public class TestCacheCollectionIds extends BaseTestCase { // arrange ResetBasicData.reset(); - CachedBean cachedBean = new CachedBean(); + OCachedBean cachedBean = new OCachedBean(); + cachedBean.setName("cachedBeanTest"); cachedBean.getCountries().add(Ebean.find(Country.class, "NZ")); cachedBean.getCountries().add(Ebean.find(Country.class, "AU")); Ebean.save(cachedBean); - // act - CachedBean update = new CachedBean(); + // clear the cache + ServerCache cachedBeanCountriesCache = cacheManager.getCollectionIdsCache(OCachedBean.class, "countries"); + cachedBeanCountriesCache.clear(); + Assert.assertEquals(0, cachedBeanCountriesCache.size()); + + // load the cache + OCachedBean dummyLoad = Ebean.find(OCachedBean.class, cachedBean.getId()); + List dummyCountries = dummyLoad.getCountries(); + Assert.assertEquals(2, dummyCountries.size()); + + // assert that the cache contains the expected entry + Assert.assertEquals("countries cache now loaded with 1 entry", 1, cachedBeanCountriesCache.size()); + CachedManyIds dummyEntry = (CachedManyIds) cachedBeanCountriesCache.get(dummyLoad.getId()); + Assert.assertNotNull(dummyEntry); + Assert.assertEquals("2 ids in the entry", 2, dummyEntry.getIdList().size()); + Assert.assertTrue(dummyEntry.getIdList().contains("NZ")); + Assert.assertTrue(dummyEntry.getIdList().contains("AU")); + + + // act - this should invalidate our cache entry + OCachedBean update = new OCachedBean(); update.setId(cachedBean.getId()); + update.setName("modified"); update.getCountries().add(Ebean.find(Country.class, "AU")); - Ebean.update(cachedBean); + Ebean.update(update); + + Assert.assertEquals("countries entry was invalidated", 0, cachedBeanCountriesCache.size()); + - CachedBean result = Ebean.find(CachedBean.class, cachedBean.getId()); - ServerCache cachedBeanCountriesCache = Ebean.getServerCacheManager().getCollectionIdsCache(CachedBean.class, "countries"); + OCachedBean result = Ebean.find(OCachedBean.class, cachedBean.getId()); CachedManyIds cachedManyIds = (CachedManyIds) cachedBeanCountriesCache.get(result.getId()); diff --git a/src/test/java/com/avaje/tests/model/basic/CachedBean.java b/src/test/java/com/avaje/tests/model/basic/OCachedBean.java similarity index 82% rename from src/test/java/com/avaje/tests/model/basic/CachedBean.java rename to src/test/java/com/avaje/tests/model/basic/OCachedBean.java index 5cf3da58a..631af520b 100644 --- a/src/test/java/com/avaje/tests/model/basic/CachedBean.java +++ b/src/test/java/com/avaje/tests/model/basic/OCachedBean.java @@ -1,14 +1,14 @@ package com.avaje.tests.model.basic; -import com.avaje.ebean.annotation.CacheStrategy; -import com.avaje.ebean.annotation.CacheTuning; +import java.util.ArrayList; +import java.util.List; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.Table; -import java.util.ArrayList; -import java.util.List; + +import com.avaje.ebean.annotation.CacheStrategy; /** * Cached bean for testing caching implementation. @@ -16,11 +16,13 @@ import java.util.List; @CacheStrategy @Entity @Table(name = "o_cached_bean") -public class CachedBean { +public class OCachedBean { @Id Long id; + String name; + @ManyToMany List countries = new ArrayList(); @@ -32,6 +34,14 @@ public class CachedBean { this.id = id; } + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + public List getCountries() { return countries; }