diff --git a/pom.xml b/pom.xml index 2d1f7c223..3c268889f 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ io.ebean ebean - 11.30.2-SNAPSHOT + 11.31.1-SNAPSHOT jar ebean diff --git a/src/main/java/io/ebeaninternal/server/cache/CacheChangeBeanUpdate.java b/src/main/java/io/ebeaninternal/server/cache/CacheChangeBeanUpdate.java index 5463eab5e..391e778c2 100644 --- a/src/main/java/io/ebeaninternal/server/cache/CacheChangeBeanUpdate.java +++ b/src/main/java/io/ebeaninternal/server/cache/CacheChangeBeanUpdate.java @@ -10,14 +10,14 @@ import java.util.Map; class CacheChangeBeanUpdate implements CacheChange { private final BeanDescriptor desc; - private final Object id; + private final String key; private final Map changes; private final boolean updateNaturalKey; private final long version; - CacheChangeBeanUpdate(BeanDescriptor desc, Object id, Map changes, boolean updateNaturalKey, long version) { + CacheChangeBeanUpdate(BeanDescriptor desc, String key, Map changes, boolean updateNaturalKey, long version) { this.desc = desc; - this.id = id; + this.key = key; this.changes = changes; this.updateNaturalKey = updateNaturalKey; this.version = version; @@ -25,6 +25,6 @@ class CacheChangeBeanUpdate implements CacheChange { @Override public void apply() { - desc.cacheApplyBeanUpdate(id, changes, updateNaturalKey, version); + desc.cacheApplyBeanUpdate(key, changes, updateNaturalKey, version); } } diff --git a/src/main/java/io/ebeaninternal/server/cache/CacheChangeNaturalKeyPut.java b/src/main/java/io/ebeaninternal/server/cache/CacheChangeNaturalKeyPut.java index 208e097bb..f90a23749 100644 --- a/src/main/java/io/ebeaninternal/server/cache/CacheChangeNaturalKeyPut.java +++ b/src/main/java/io/ebeaninternal/server/cache/CacheChangeNaturalKeyPut.java @@ -8,17 +8,17 @@ import io.ebeaninternal.server.deploy.BeanDescriptor; class CacheChangeNaturalKeyPut implements CacheChange { private final BeanDescriptor descriptor; - private final Object id; - private final Object newKey; + private final String key; + private final String newKey; - CacheChangeNaturalKeyPut(BeanDescriptor descriptor, Object id, Object newKey) { + CacheChangeNaturalKeyPut(BeanDescriptor descriptor, String key, String newKey) { this.descriptor = descriptor; - this.id = id; + this.key = key; this.newKey = newKey; } @Override public void apply() { - descriptor.cacheNaturalKeyPut(id, newKey); + descriptor.cacheNaturalKeyPut(key, newKey); } } diff --git a/src/main/java/io/ebeaninternal/server/cache/CacheChangeSet.java b/src/main/java/io/ebeaninternal/server/cache/CacheChangeSet.java index 5865d2bbc..9a058444e 100644 --- a/src/main/java/io/ebeaninternal/server/cache/CacheChangeSet.java +++ b/src/main/java/io/ebeaninternal/server/cache/CacheChangeSet.java @@ -152,16 +152,16 @@ public class CacheChangeSet { /** * Update a bean entry. */ - public void addBeanUpdate(BeanDescriptor desc, Object id, Map changes, boolean updateNaturalKey, long version) { + public void addBeanUpdate(BeanDescriptor desc, String key, Map changes, boolean updateNaturalKey, long version) { touchedTables.add(desc.getBaseTable()); - entries.add(new CacheChangeBeanUpdate(desc, id, changes, updateNaturalKey, version)); + entries.add(new CacheChangeBeanUpdate(desc, key, changes, updateNaturalKey, version)); } /** * Update a natural key. */ - public void addNaturalKeyPut(BeanDescriptor desc, Object id, Object val) { - entries.add(new CacheChangeNaturalKeyPut(desc, id, val)); + public void addNaturalKeyPut(BeanDescriptor desc, String key, String val) { + entries.add(new CacheChangeNaturalKeyPut(desc, key, val)); } /** diff --git a/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java b/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java index d2a93c9d6..8bf7ffd81 100644 --- a/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java +++ b/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java @@ -1405,6 +1405,8 @@ public final class PersistRequestBean extends PersistRequest implements BeanP boolean updateNaturalKey = false; + String key = beanDescriptor.cacheKey(idValue); + Map changes = new LinkedHashMap<>(); EntityBean bean = getEntityBean(); boolean[] dirtyProperties = getDirtyProperties(); @@ -1417,7 +1419,7 @@ public final class PersistRequestBean extends PersistRequest implements BeanP changes.put(property.getName(), val); if (property.isNaturalKey()) { updateNaturalKey = true; - changeSet.addNaturalKeyPut(beanDescriptor, idValue, val); + changeSet.addNaturalKeyPut(beanDescriptor, key, val.toString()); } } } @@ -1428,7 +1430,7 @@ public final class PersistRequestBean extends PersistRequest implements BeanP changes.putAll(collectionChanges); } - changeSet.addBeanUpdate(beanDescriptor, idValue, changes, updateNaturalKey, getVersion()); + changeSet.addBeanUpdate(beanDescriptor, key, changes, updateNaturalKey, getVersion()); } } diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java index 5c2f18a76..48985d2ee 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java @@ -1487,21 +1487,25 @@ public class BeanDescriptor implements BeanType, STreeType { * Return a bean from the bean cache (or null). */ public T cacheBeanGet(Object id, Boolean readOnly, PersistenceContext context) { - return cacheHelp.beanCacheGet(id, readOnly, context); + return cacheHelp.beanCacheGet(cacheKey(id), readOnly, context); } /** * Remove a collection of beans from the cache given the ids. */ public void cacheApplyInvalidate(Collection ids) { - cacheHelp.beanCacheApplyInvalidate(ids); + List keys = new ArrayList<>(ids.size()); + for (Object id : ids) { + keys.add(cacheKey(id)); + } + cacheHelp.beanCacheApplyInvalidate(keys); } /** * Returns true if it managed to populate/load the bean from the cache. */ public boolean cacheBeanLoad(EntityBean bean, EntityBeanIntercept ebi, Object id, PersistenceContext context) { - return cacheHelp.beanCacheLoad(bean, ebi, id, context); + return cacheHelp.beanCacheLoad(bean, ebi, cacheKey(id), context); } /** @@ -1520,8 +1524,8 @@ public class BeanDescriptor implements BeanType, STreeType { return cacheHelp.naturalKeyLookup(context, keys); } - public void cacheNaturalKeyPut(Object id, Object newKey) { - cacheHelp.cacheNaturalKeyPut(id, newKey); + public void cacheNaturalKeyPut(String key, String newKey) { + cacheHelp.cacheNaturalKeyPut(key, newKey); } /** @@ -1569,8 +1573,8 @@ public class BeanDescriptor implements BeanType, STreeType { /** * Apply the update to the cache. */ - public void cacheApplyBeanUpdate(Object id, Map changes, boolean updateNaturalKey, long version) { - cacheHelp.cacheBeanUpdate(id, changes, updateNaturalKey, version); + public void cacheApplyBeanUpdate(String key, Map changes, boolean updateNaturalKey, long version) { + cacheHelp.cacheBeanUpdate(key, changes, updateNaturalKey, version); } /** @@ -1989,7 +1993,7 @@ public class BeanDescriptor implements BeanType, STreeType { public T createReference(Boolean readOnly, boolean disableLazyLoad, Object id, PersistenceContext pc) { if (cacheSharableBeans && !disableLazyLoad && !Boolean.FALSE.equals(readOnly)) { - CachedBeanData d = cacheHelp.beanCacheGetData(id); + CachedBeanData d = cacheHelp.beanCacheGetData(cacheKey(id)); if (d != null) { Object shareableBean = d.getSharableBean(); if (shareableBean != null) { @@ -2270,6 +2274,20 @@ public class BeanDescriptor implements BeanType, STreeType { return (idProperty == null) ? null : idProperty.getValueIntercept(bean); } + /** + * Return the cache key for the given bean (based on id value). + */ + public String cacheKeyForBean(EntityBean bean) { + return cacheKey(idProperty.getValue(bean)); + } + + /** + * Return the cache key for the given id value. + */ + public String cacheKey(Object id) { + return idBinder.cacheKey(id); + } + @Override public Object beanId(Object bean) { return getId((EntityBean) bean); diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java index 56f951b98..ed5d7cdfb 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java @@ -282,7 +282,7 @@ final class BeanDescriptorCacheHelp { if (many.isElementCollection()) { // held as part of the bean cache so skip - return false; + return false; } CachedManyIds entry = manyPropGet(parentId, many.getName()); @@ -317,7 +317,7 @@ final class BeanDescriptorCacheHelp { try { // add as JSON to bean cache String asJson = many.jsonWriteCollection(details); - Map changes = new HashMap<>(); + Map changes = new HashMap<>(); changes.put(many.getName(), asJson); CachedBeanData newData = data.update(changes, data.getVersion()); @@ -475,7 +475,7 @@ final class BeanDescriptorCacheHelp { private Class theClassOf(Collection beans) { if (beans instanceof List) { - return ((List)beans).get(0).getClass(); + return ((List) beans).get(0).getClass(); } return beans.iterator().next().getClass(); } @@ -494,20 +494,20 @@ final class BeanDescriptorCacheHelp { void beanCachePutAllDirect(Collection beans) { - Map natKeys = null; + Map natKeys = null; if (naturalKey != null) { natKeys = new LinkedHashMap<>(); } - Map map = new LinkedHashMap<>(); + Map map = new LinkedHashMap<>(); for (EntityBean bean : beans) { CachedBeanData beanData = beanExtractData(desc, bean); - Object id = desc.getId(bean); - map.put(id, beanData); + String key = desc.cacheKeyForBean(bean); + map.put(key, beanData); if (naturalKey != null) { Object naturalKey = calculateNaturalKey(beanData); if (naturalKey != null) { - natKeys.put(naturalKey, id); + natKeys.put(naturalKey, key); } } } @@ -525,32 +525,33 @@ final class BeanDescriptorCacheHelp { } /** - * Put the bean into the bean cache. - */ + * Put the bean into the bean cache. + */ void beanCachePutDirect(EntityBean bean) { CachedBeanData beanData = beanExtractData(desc, bean); - Object id = desc.getId(bean); + String key = desc.cacheKeyForBean(bean); if (beanLog.isDebugEnabled()) { - beanLog.debug(" PUT {}({}) data:{}", cacheName, id, beanData); + beanLog.debug(" PUT {}({}) data:{}", cacheName, key, beanData); } - getBeanCache().put(id, beanData); + getBeanCache().put(key, beanData); if (naturalKey != null) { - Object naturalKey = calculateNaturalKey(beanData); + String naturalKey = calculateNaturalKey(beanData); if (naturalKey != null) { if (natLog.isDebugEnabled()) { - natLog.debug(" PUT {}({}, {})", cacheName, naturalKey, id); + natLog.debug(" PUT {}({}, {})", cacheName, naturalKey, key); } - naturalKeyCache.put(naturalKey, id); + naturalKeyCache.put(naturalKey, key); } } } - private Object calculateNaturalKey(CachedBeanData beanData) { + private String calculateNaturalKey(CachedBeanData beanData) { if (naturalKey.length == 1) { - return beanData.getData(naturalKey[0]); + Object data = beanData.getData(naturalKey[0]); + return (data == null) ? null : data.toString(); } StringBuilder sb = new StringBuilder(); for (String key : naturalKey) { @@ -563,12 +564,12 @@ final class BeanDescriptorCacheHelp { return sb.toString(); } - CachedBeanData beanCacheGetData(Object id) { - return (CachedBeanData) getBeanCache().get(id); + CachedBeanData beanCacheGetData(String key) { + return (CachedBeanData) getBeanCache().get(key); } - T beanCacheGet(Object id, Boolean readOnly, PersistenceContext context) { - T bean = beanCacheGetInternal(id, readOnly, context); + T beanCacheGet(String key, Boolean readOnly, PersistenceContext context) { + T bean = beanCacheGetInternal(key, readOnly, context); if (bean != null) { setupContext(bean, context); } @@ -578,19 +579,19 @@ final class BeanDescriptorCacheHelp { /** * Return a bean from the bean cache. */ - private T beanCacheGetInternal(Object id, Boolean readOnly, PersistenceContext context) { + private T beanCacheGetInternal(String key, Boolean readOnly, PersistenceContext context) { - CachedBeanData data = (CachedBeanData) getBeanCache().get(id); + CachedBeanData data = (CachedBeanData) getBeanCache().get(key); if (data == null) { if (beanLog.isTraceEnabled()) { - beanLog.trace(" GET {}({}) - cache miss", cacheName, id); + beanLog.trace(" GET {}({}) - cache miss", cacheName, key); } return null; } if (beanLog.isTraceEnabled()) { - beanLog.trace(" GET {}({}) - hit", cacheName, id); + beanLog.trace(" GET {}({}) - hit", cacheName, key); } - return convertToBean(id, readOnly, context, data); + return convertToBean(key, readOnly, context, data); } @SuppressWarnings("unchecked") @@ -686,12 +687,12 @@ final class BeanDescriptorCacheHelp { /** * Remove a bean from the cache given its Id. */ - void beanCacheApplyInvalidate(Collection ids) { + void beanCacheApplyInvalidate(Collection keys) { if (beanCache != null) { if (beanLog.isDebugEnabled()) { - beanLog.debug(" REMOVE {}({})", cacheName, ids); + beanLog.debug(" REMOVE {}({})", cacheName, keys); } - beanCache.removeAll(new HashSet<>(ids)); + beanCache.removeAll(new HashSet<>(keys)); } for (BeanPropertyAssocOne imported : propertiesOneImported) { imported.cacheClear(); @@ -701,26 +702,26 @@ final class BeanDescriptorCacheHelp { /** * Returns true if it managed to populate/load the bean from the cache. */ - boolean beanCacheLoad(EntityBean bean, EntityBeanIntercept ebi, Object id, PersistenceContext context) { + boolean beanCacheLoad(EntityBean bean, EntityBeanIntercept ebi, String key, PersistenceContext context) { - CachedBeanData cacheData = (CachedBeanData) getBeanCache().get(id); + CachedBeanData cacheData = (CachedBeanData) getBeanCache().get(key); if (cacheData == null) { if (beanLog.isTraceEnabled()) { - beanLog.trace(" LOAD {}({}) - cache miss", cacheName, id); + beanLog.trace(" LOAD {}({}) - cache miss", cacheName, key); } return false; } int lazyLoadProperty = ebi.getLazyLoadPropertyIndex(); if (lazyLoadProperty > -1 && !cacheData.isLoaded(ebi.getLazyLoadProperty())) { if (beanLog.isTraceEnabled()) { - beanLog.trace(" LOAD {}({}) - cache miss on property({})", cacheName, id, ebi.getLazyLoadProperty()); + beanLog.trace(" LOAD {}({}) - cache miss on property({})", cacheName, key, ebi.getLazyLoadProperty()); } return false; } CachedBeanDataToBean.load(desc, bean, cacheData, context); if (beanLog.isDebugEnabled()) { - beanLog.debug(" LOAD {}({}) - hit", cacheName, id); + beanLog.debug(" LOAD {}({}) - hit", cacheName, key); } return true; } @@ -736,7 +737,7 @@ final class BeanDescriptorCacheHelp { */ void persistDeleteIds(Collection ids, CacheChangeSet changeSet) { if (invalidateQueryCache) { - changeSet.addInvalidate(desc); + changeSet.addInvalidate(desc); } else { queryCacheClear(changeSet); if (beanCache != null) { @@ -833,42 +834,42 @@ final class BeanDescriptorCacheHelp { } } - void cacheNaturalKeyPut(Object id, Object newKey) { + void cacheNaturalKeyPut(String key, String newKey) { if (newKey != null) { - naturalKeyCache.put(newKey, id); + naturalKeyCache.put(newKey, key); } } /** * Apply changes to the bean cache entry. */ - void cacheBeanUpdate(Object id, Map changes, boolean updateNaturalKey, long version) { + void cacheBeanUpdate(String key, Map changes, boolean updateNaturalKey, long version) { ServerCache cache = getBeanCache(); - CachedBeanData existingData = (CachedBeanData) cache.get(id); + CachedBeanData existingData = (CachedBeanData) cache.get(key); if (existingData != null) { long currentVersion = existingData.getVersion(); if (version > 0 && version < currentVersion) { if (beanLog.isDebugEnabled()) { - beanLog.debug(" REMOVE {}({}) - version conflict old:{} new:{}", cacheName, id, currentVersion, version); + beanLog.debug(" REMOVE {}({}) - version conflict old:{} new:{}", cacheName, key, currentVersion, version); } - cache.remove(id); + cache.remove(key); } else { if (version == 0) { version = currentVersion; } CachedBeanData newData = existingData.update(changes, version); if (beanLog.isDebugEnabled()) { - beanLog.debug(" UPDATE {}({}) changes:{}", cacheName, id, changes); + beanLog.debug(" UPDATE {}({}) changes:{}", cacheName, key, changes); } - cache.put(id, newData); + cache.put(key, newData); } if (updateNaturalKey) { Object oldKey = calculateNaturalKey(existingData); if (oldKey != null) { if (natLog.isDebugEnabled()) { - natLog.debug(".. update {} REMOVE({}) - old key for ({})", cacheName, oldKey, id); + natLog.debug(".. update {} REMOVE({}) - old key for ({})", cacheName, oldKey, key); } naturalKeyCache.remove(oldKey); } diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java b/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java index 6ae467d44..a2c7b0086 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java @@ -827,6 +827,13 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { } } + /** + * Return the value in String format (for bean cache key). + */ + public String format(Object value) { + return scalarType.format(value); + } + /** * Read the value for this property from L2 cache entry and set it to the bean. *

diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java index bfba8ed9d..fdb38088c 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java @@ -972,8 +972,10 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST public void setCacheDataValue(EntityBean bean, Object cacheData, PersistenceContext context) { try { String asJson = (String) cacheData; - Object collection = jsonReadCollection(asJson); - setValue(bean, collection); + if (asJson != null && !asJson.isEmpty()) { + Object collection = jsonReadCollection(asJson); + setValue(bean, collection); + } } catch (Exception e) { logger.error("Error setting value from L2 cache", e); } @@ -999,7 +1001,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST public String jsonWriteCollection(Object value) throws IOException { StringWriter writer = new StringWriter(300); SpiJsonWriter ctx = descriptor.createJsonWriter(writer); - help.jsonWrite(ctx, null, value, false); + help.jsonWrite(ctx, null, value, true); ctx.flush(); return writer.toString(); } diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java index 9a5f7c1c2..4245235cd 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java @@ -413,6 +413,11 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc implements STr } } + @Override + public String format(Object value) { + return targetDescriptor.getIdBinder().cacheKey(value); + } + @Override public void setCacheDataValue(EntityBean bean, Object cacheData, PersistenceContext context) { if (cacheData == null) { diff --git a/src/main/java/io/ebeaninternal/server/deploy/id/IdBinder.java b/src/main/java/io/ebeaninternal/server/deploy/id/IdBinder.java index d3a018e10..c722af79e 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/id/IdBinder.java +++ b/src/main/java/io/ebeaninternal/server/deploy/id/IdBinder.java @@ -207,4 +207,10 @@ public interface IdBinder { * Cast or convert the Id value if necessary. */ Object convertId(Object idValue); + + /** + * Return a key to use for bean caches given the id value. + */ + String cacheKey(Object idValue); + } diff --git a/src/main/java/io/ebeaninternal/server/deploy/id/IdBinderEmbedded.java b/src/main/java/io/ebeaninternal/server/deploy/id/IdBinderEmbedded.java index c78e9ba25..6358a57c3 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/id/IdBinderEmbedded.java +++ b/src/main/java/io/ebeaninternal/server/deploy/id/IdBinderEmbedded.java @@ -479,4 +479,20 @@ public final class IdBinderEmbedded implements IdBinder { return idValue; } + + @Override + public String cacheKey(Object value) { + + EntityBean bean = (EntityBean)value; + StringBuilder sb = new StringBuilder(); + for (BeanProperty prop : props) { + Object val = prop.getValue(bean); + if (val != null) { + sb.append(prop.format(val)); + } + sb.append("|"); + } + return sb.toString(); + } + } diff --git a/src/main/java/io/ebeaninternal/server/deploy/id/IdBinderEmpty.java b/src/main/java/io/ebeaninternal/server/deploy/id/IdBinderEmpty.java index 01052b076..651c995d2 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/id/IdBinderEmpty.java +++ b/src/main/java/io/ebeaninternal/server/deploy/id/IdBinderEmpty.java @@ -178,4 +178,10 @@ public final class IdBinderEmpty implements IdBinder { public void writeData(DataOutput dataOutput, Object idValue) throws IOException { } + + @Override + public String cacheKey(Object bean) { + return null; + } + } diff --git a/src/main/java/io/ebeaninternal/server/deploy/id/IdBinderSimple.java b/src/main/java/io/ebeaninternal/server/deploy/id/IdBinderSimple.java index f424f4662..e76503b20 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/id/IdBinderSimple.java +++ b/src/main/java/io/ebeaninternal/server/deploy/id/IdBinderSimple.java @@ -262,4 +262,10 @@ public final class IdBinderSimple implements IdBinder { } return idValue; } + + @Override + public String cacheKey(Object value) { + return scalarType.format(value); + } + }