diff --git a/src/main/java/io/ebean/common/AbstractBeanCollection.java b/src/main/java/io/ebean/common/AbstractBeanCollection.java index ca5493d2a..33b30f3ac 100644 --- a/src/main/java/io/ebean/common/AbstractBeanCollection.java +++ b/src/main/java/io/ebean/common/AbstractBeanCollection.java @@ -63,7 +63,7 @@ abstract class AbstractBeanCollection implements BeanCollection { this.ebeanServerName = loader.getName(); this.ownerBean = ownerBean; this.propertyName = propertyName; - this.readOnly = ownerBean._ebean_getIntercept().isReadOnly(); + this.readOnly = ownerBean != null && ownerBean._ebean_getIntercept().isReadOnly(); } @Override diff --git a/src/main/java/io/ebeaninternal/api/SpiJsonContext.java b/src/main/java/io/ebeaninternal/api/SpiJsonContext.java index 4be1a3034..2700ed60e 100644 --- a/src/main/java/io/ebeaninternal/api/SpiJsonContext.java +++ b/src/main/java/io/ebeaninternal/api/SpiJsonContext.java @@ -1,9 +1,11 @@ package io.ebeaninternal.api; import com.fasterxml.jackson.core.JsonGenerator; +import io.ebean.plugin.BeanType; import io.ebean.text.json.JsonContext; import io.ebean.text.json.JsonWriteOptions; -import io.ebeaninternal.server.text.json.SpiJsonWriter; +import io.ebeaninternal.api.json.SpiJsonReader; +import io.ebeaninternal.api.json.SpiJsonWriter; import java.io.Writer; @@ -21,4 +23,10 @@ public interface SpiJsonContext extends JsonContext { * Create a Json Writer for writing beans as JSON supplying a writer. */ SpiJsonWriter createJsonWriter(Writer writer); + + /** + * Create a Json Reader for reading the JSON content. + */ + SpiJsonReader createJsonRead(BeanType beanType, String json); + } diff --git a/src/main/java/io/ebeaninternal/api/json/SpiJsonReader.java b/src/main/java/io/ebeaninternal/api/json/SpiJsonReader.java new file mode 100644 index 000000000..aaf4e2ce8 --- /dev/null +++ b/src/main/java/io/ebeaninternal/api/json/SpiJsonReader.java @@ -0,0 +1,36 @@ +package io.ebeaninternal.api.json; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.ebean.bean.EntityBean; +import io.ebean.bean.PersistenceContext; +import io.ebeaninternal.server.deploy.BeanDescriptor; + +import java.io.IOException; +import java.util.Map; + +public interface SpiJsonReader { + + PersistenceContext getPersistenceContext(); + + SpiJsonReader forJson(JsonParser moreJson, boolean resetContext); + + void persistenceContextPut(Object beanId, T currentBean); + + Object persistenceContextPutIfAbsent(Object id, EntityBean bean, BeanDescriptor beanDesc); + + ObjectMapper getObjectMapper(); + + JsonParser getParser(); + + JsonToken nextToken() throws IOException; + + void pushPath(String path); + + void popPath(); + + void beanVisitor(Object bean, Map unmappedProperties); + + Object readValueUsingObjectMapper(Class propertyType) throws IOException; +} diff --git a/src/main/java/io/ebeaninternal/server/text/json/SpiJsonWriter.java b/src/main/java/io/ebeaninternal/api/json/SpiJsonWriter.java similarity index 90% rename from src/main/java/io/ebeaninternal/server/text/json/SpiJsonWriter.java rename to src/main/java/io/ebeaninternal/api/json/SpiJsonWriter.java index ebcb88379..4fd44f183 100644 --- a/src/main/java/io/ebeaninternal/server/text/json/SpiJsonWriter.java +++ b/src/main/java/io/ebeaninternal/api/json/SpiJsonWriter.java @@ -1,9 +1,10 @@ -package io.ebeaninternal.server.text.json; +package io.ebeaninternal.api.json; import io.ebean.bean.EntityBean; import io.ebean.text.json.JsonWriter; import io.ebeaninternal.server.deploy.BeanDescriptor; +import java.io.IOException; import java.util.Collection; /** @@ -11,6 +12,11 @@ import java.util.Collection; */ public interface SpiJsonWriter extends JsonWriter { + /** + * Flush the buffer. + */ + void flush() throws IOException; + /** * Return true if the value is a parent bean. */ @@ -60,7 +66,7 @@ public interface SpiJsonWriter extends JsonWriter { * Write value using underlying Jaskson object mapper if available. */ void writeValueUsingObjectMapper(String name, Object value); - + /** * Write the bean properties. */ diff --git a/src/main/java/io/ebeaninternal/server/cache/CachedBeanDataFromBean.java b/src/main/java/io/ebeaninternal/server/cache/CachedBeanDataFromBean.java index 1de76f18a..e894a8710 100644 --- a/src/main/java/io/ebeaninternal/server/cache/CachedBeanDataFromBean.java +++ b/src/main/java/io/ebeaninternal/server/cache/CachedBeanDataFromBean.java @@ -4,6 +4,7 @@ import io.ebean.bean.EntityBean; import io.ebean.bean.EntityBeanIntercept; import io.ebeaninternal.server.deploy.BeanDescriptor; import io.ebeaninternal.server.deploy.BeanProperty; +import io.ebeaninternal.server.deploy.BeanPropertyAssocMany; import java.util.LinkedHashMap; import java.util.Map; @@ -33,6 +34,12 @@ public class CachedBeanDataFromBean { } } + for (BeanPropertyAssocMany prop : desc.propertiesMany()) { + if (prop.isElementCollection()) { + data.put(prop.getName(), prop.getCacheDataValue(bean)); + } + } + long version = desc.getVersion(bean); EntityBean sharableBean = createSharableBean(desc, bean, ebi); return new CachedBeanData(sharableBean, desc.getDiscValue(), data, version); diff --git a/src/main/java/io/ebeaninternal/server/cache/CachedBeanDataToBean.java b/src/main/java/io/ebeaninternal/server/cache/CachedBeanDataToBean.java index beee32400..05b2a0d1f 100644 --- a/src/main/java/io/ebeaninternal/server/cache/CachedBeanDataToBean.java +++ b/src/main/java/io/ebeaninternal/server/cache/CachedBeanDataToBean.java @@ -21,14 +21,16 @@ public class CachedBeanDataToBean { } // load the non-many properties - BeanProperty[] props = desc.propertiesNonMany(); - for (BeanProperty prop : props) { + for (BeanProperty prop : desc.propertiesNonMany()) { loadProperty(bean, cacheBeanData, ebi, prop, context); } - BeanPropertyAssocMany[] many = desc.propertiesMany(); - for (BeanPropertyAssocMany aMany : many) { - aMany.createReferenceIfNull(bean); + for (BeanPropertyAssocMany prop : desc.propertiesMany()) { + if (prop.isElementCollection()) { + loadProperty(bean, cacheBeanData, ebi, prop, context); + } else { + prop.createReferenceIfNull(bean); + } } ebi.setLoadedLazy(); diff --git a/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java b/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java index 283d426bd..07618f6b9 100644 --- a/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java +++ b/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java @@ -35,6 +35,7 @@ import javax.persistence.PersistenceException; import java.io.IOException; import java.sql.Statement; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -107,6 +108,11 @@ public final class PersistRequestBean extends PersistRequest implements BeanP */ private boolean updatedManysOnly; + /** + * Element collection change as part of bean cache. + */ + private Map collectionChanges; + /** * Many properties that were cascade saved (and hence might need caches updated later). */ @@ -938,10 +944,14 @@ public final class PersistRequestBean extends PersistRequest implements BeanP /** * Combine with the beans postUpdate event notification. */ - public void postElementCollectionUpdate() { + public boolean postElementCollectionUpdate() { if (controller != null) { pendingPostUpdateNotify += 2; } + if (!dirty) { + setNotifyCache(); + } + return notifyCache; } private void controllerPost() { @@ -1079,13 +1089,6 @@ public final class PersistRequestBean extends PersistRequest implements BeanP postUpdateNotify(); } - /** - * Return true if only many properties where updated. - */ - public boolean isUpdatedManysOnly() { - return updatedManysOnly; - } - /** * For requests that update document store add this event to either the list * of queue events or list of update events. @@ -1311,4 +1314,50 @@ public final class PersistRequestBean extends PersistRequest implements BeanP public boolean isInsertedParent() { return Flags.isInsert(flags); } + + /** + * Add an element collection change to L2 bean cache update. + */ + public void addCollectionChange(String name, Object value) { + if (collectionChanges == null) { + collectionChanges = new LinkedHashMap<>(); + } + collectionChanges.put(name, value); + } + + /** + * Build the bean update for the L2 cache. + */ + public void addBeanUpdate(CacheChangeSet changeSet) { + + if (!updatedManysOnly || collectionChanges != null) { + + boolean updateNaturalKey = false; + + Map changes = new LinkedHashMap<>(); + EntityBean bean = getEntityBean(); + boolean[] dirtyProperties = getDirtyProperties(); + if (dirtyProperties != null) { + for (int i = 0; i < dirtyProperties.length; i++) { + if (dirtyProperties[i]) { + BeanProperty property = beanDescriptor.propertyByIndex(i); + if (property.isCacheDataInclude()) { + Object val = property.getCacheDataValue(bean); + changes.put(property.getName(), val); + if (property.isNaturalKey()) { + updateNaturalKey = true; + changeSet.addNaturalKeyPut(beanDescriptor, idValue, val); + } + } + } + } + } + if (collectionChanges != null) { + // add element collection update + changes.putAll(collectionChanges); + } + + changeSet.addBeanUpdate(beanDescriptor, idValue, changes, updateNaturalKey, getVersion()); + } + } } diff --git a/src/main/java/io/ebeaninternal/server/deploy/BaseCollectionHelp.java b/src/main/java/io/ebeaninternal/server/deploy/BaseCollectionHelp.java index 4d8dbda97..576d9a12b 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BaseCollectionHelp.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BaseCollectionHelp.java @@ -3,7 +3,7 @@ package io.ebeaninternal.server.deploy; import io.ebean.bean.BeanCollection; import io.ebean.bean.BeanCollectionLoader; import io.ebean.bean.EntityBean; -import io.ebeaninternal.server.text.json.SpiJsonWriter; +import io.ebeaninternal.api.json.SpiJsonWriter; import java.io.IOException; import java.util.Collection; diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanChangeJson.java b/src/main/java/io/ebeaninternal/server/deploy/BeanChangeJson.java index ae77429a0..e2a6938ff 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanChangeJson.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanChangeJson.java @@ -2,7 +2,7 @@ package io.ebeaninternal.server.deploy; import io.ebean.PersistenceIOException; import io.ebean.bean.BeanDiffVisitor; -import io.ebeaninternal.server.text.json.SpiJsonWriter; +import io.ebeaninternal.api.json.SpiJsonWriter; import io.ebeaninternal.server.util.ArrayStack; import java.io.IOException; @@ -82,10 +82,10 @@ class BeanChangeJson implements BeanDiffVisitor { void flush() { try { newJson.writeEndObject(); - newJson.gen().flush(); + newJson.flush(); if (oldJson != null) { oldJson.writeEndObject(); - oldJson.gen().flush(); + oldJson.flush(); } } catch (IOException e) { throw new PersistenceIOException(e); diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanCollectionHelp.java b/src/main/java/io/ebeaninternal/server/deploy/BeanCollectionHelp.java index 569e22403..ede034939 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanCollectionHelp.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanCollectionHelp.java @@ -7,8 +7,8 @@ import io.ebean.bean.BeanCollection; import io.ebean.bean.BeanCollectionAdd; import io.ebean.bean.BeanCollectionLoader; import io.ebean.bean.EntityBean; +import io.ebeaninternal.api.json.SpiJsonWriter; import io.ebeaninternal.server.query.CQueryCollectionAdd; -import io.ebeaninternal.server.text.json.SpiJsonWriter; import java.io.IOException; import java.util.Collection; diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java index 620b6e1fd..1bc379e7e 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java @@ -41,6 +41,8 @@ import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.api.SpiQuery; import io.ebeaninternal.api.SpiUpdatePlan; import io.ebeaninternal.api.TransactionEventTable.TableIUD; +import io.ebeaninternal.api.json.SpiJsonReader; +import io.ebeaninternal.api.json.SpiJsonWriter; import io.ebeaninternal.server.cache.CacheChangeSet; import io.ebeaninternal.server.cache.CachedBeanData; import io.ebeaninternal.server.cache.CachedManyIds; @@ -71,8 +73,6 @@ import io.ebeaninternal.server.query.STreeType; import io.ebeaninternal.server.query.SqlBeanLoad; import io.ebeaninternal.server.querydefn.OrmQueryDetail; import io.ebeaninternal.server.rawsql.SpiRawSql; -import io.ebeaninternal.server.text.json.ReadJson; -import io.ebeaninternal.server.text.json.SpiJsonWriter; import io.ebeaninternal.server.type.DataBind; import io.ebeaninternal.server.type.ScalarType; import io.ebeaninternal.util.SortByClause; @@ -662,6 +662,10 @@ public class BeanDescriptor implements BeanType, STreeType { return properties; } + public BeanProperty propertyByIndex(int pos) { + return propertiesIndex[pos]; + } + /** * Initialise the Id properties first. *

@@ -945,7 +949,7 @@ public class BeanDescriptor implements BeanType, STreeType { SpiJsonWriter jsonWriter = createJsonWriter(writer); jsonWriteForInsert(jsonWriter, request.getEntityBean()); - jsonWriter.gen().flush(); + jsonWriter.flush(); return beanChange(ChangeType.INSERT, request.getBeanId(), writer.toString(), null); @@ -959,6 +963,10 @@ public class BeanDescriptor implements BeanType, STreeType { return ebeanServer.jsonExtended().createJsonWriter(writer); } + SpiJsonReader createJsonReader(String json) { + return ebeanServer.jsonExtended().createJsonRead(this, json); + } + /** * Populate the diff for inserts with flattened non-null property values. */ @@ -3355,7 +3363,7 @@ public class BeanDescriptor implements BeanType, STreeType { throw new IllegalStateException("Unexpected - expect Element override"); } - public Object jsonReadCollection(ReadJson readJson, EntityBean parentBean) throws IOException { + public Object jsonReadCollection(SpiJsonReader readJson, EntityBean parentBean) throws IOException { throw new IllegalStateException("Unexpected - expect Element override"); } @@ -3371,11 +3379,11 @@ public class BeanDescriptor implements BeanType, STreeType { jsonHelp.jsonWriteProperties(writeJson, bean); } - public T jsonRead(ReadJson jsonRead, String path) throws IOException { + public T jsonRead(SpiJsonReader jsonRead, String path) throws IOException { return jsonHelp.jsonRead(jsonRead, path); } - protected T jsonReadObject(ReadJson jsonRead, String path) throws IOException { + protected T jsonReadObject(SpiJsonReader jsonRead, String path) throws IOException { return jsonHelp.jsonReadObject(jsonRead, path); } diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java index ac131a884..51d6b9fbf 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java @@ -20,6 +20,7 @@ import io.ebeaninternal.server.transaction.DefaultPersistenceContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -272,6 +273,11 @@ final class BeanDescriptorCacheHelp { */ boolean manyPropLoad(BeanPropertyAssocMany many, BeanCollection bc, Object parentId, Boolean readOnly) { + if (many.isElementCollection()) { + // held as part of the bean cache so skip + return false; + } + CachedManyIds entry = manyPropGet(parentId, many.getName()); if (entry == null) { // not in cache so return unsuccessful @@ -298,9 +304,29 @@ final class BeanDescriptorCacheHelp { */ void manyPropPut(BeanPropertyAssocMany many, Object details, Object parentId) { - CachedManyIds entry = createManyIds(many, details); - if (entry != null) { - cachePutManyIds(parentId, many.getName(), entry); + if (many.isElementCollection()) { + CachedBeanData data = (CachedBeanData) beanCache.get(parentId); + if (data != null) { + try { + // add as JSON to bean cache + String asJson = many.jsonWriteCollection(details); + Map changes = new HashMap<>(); + changes.put(many.getName(), asJson); + + CachedBeanData newData = data.update(changes, data.getVersion()); + if (beanLog.isDebugEnabled()) { + beanLog.debug(" UPDATE {}({}) changes:{}", cacheName, parentId, changes); + } + beanCache.put(parentId, newData); + } catch (IOException e) { + logger.error("Error updating L2 cache", e); + } + } + } else { + CachedManyIds entry = createManyIds(many, details); + if (entry != null) { + cachePutManyIds(parentId, many.getName(), entry); + } } } @@ -730,38 +756,17 @@ final class BeanDescriptorCacheHelp { List> manyCollections = updateRequest.getUpdatedManyCollections(); if (manyCollections != null) { for (BeanPropertyAssocMany many : manyCollections) { - Object details = many.getValue(updateRequest.getEntityBean()); - CachedManyIds entry = createManyIds(many, details); - if (entry != null) { - changeSet.addManyPut(desc, many.getName(), id, entry); - } - } - } - - // check if the bean itself was updated - if (!updateRequest.isUpdatedManysOnly()) { - - boolean updateNaturalKey = false; - - Map changes = new LinkedHashMap<>(); - EntityBean bean = updateRequest.getEntityBean(); - boolean[] dirtyProperties = updateRequest.getDirtyProperties(); - for (int i = 0; i < dirtyProperties.length; i++) { - if (dirtyProperties[i]) { - BeanProperty property = desc.propertiesIndex[i]; - if (property.isCacheDataInclude()) { - Object val = property.getCacheDataValue(bean); - changes.put(property.getName(), val); - if (property.isNaturalKey()) { - updateNaturalKey = true; - changeSet.addNaturalKeyPut(desc, id, val); - } + if (!many.isElementCollection()) { + Object details = many.getValue(updateRequest.getEntityBean()); + CachedManyIds entry = createManyIds(many, details); + if (entry != null) { + changeSet.addManyPut(desc, many.getName(), id, entry); } } } - - changeSet.addBeanUpdate(desc, id, changes, updateNaturalKey, updateRequest.getVersion()); } + + updateRequest.addBeanUpdate(changeSet); } /** diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementEmbedded.java b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementEmbedded.java index cb17a9492..6d24e4569 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementEmbedded.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementEmbedded.java @@ -3,9 +3,9 @@ package io.ebeaninternal.server.deploy; import io.ebean.PersistenceIOException; import io.ebean.SqlUpdate; import io.ebean.bean.EntityBean; +import io.ebeaninternal.api.json.SpiJsonReader; +import io.ebeaninternal.api.json.SpiJsonWriter; import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; -import io.ebeaninternal.server.text.json.ReadJson; -import io.ebeaninternal.server.text.json.SpiJsonWriter; import java.io.IOException; @@ -56,12 +56,12 @@ class BeanDescriptorElementEmbedded extends BeanDescriptorElement { } @Override - public T jsonRead(ReadJson jsonRead, String path) throws IOException { + public T jsonRead(SpiJsonReader jsonRead, String path) throws IOException { return readJsonElement(jsonRead, path); } @SuppressWarnings("unchecked") - T readJsonElement(ReadJson jsonRead, String path) throws IOException { + T readJsonElement(SpiJsonReader jsonRead, String path) throws IOException { return (T)targetDescriptor.jsonRead(jsonRead, path); } diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementEmbeddedMap.java b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementEmbeddedMap.java index 49b85d699..c30ff9a33 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementEmbeddedMap.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementEmbeddedMap.java @@ -3,9 +3,9 @@ package io.ebeaninternal.server.deploy; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import io.ebean.bean.EntityBean; +import io.ebeaninternal.api.json.SpiJsonReader; +import io.ebeaninternal.api.json.SpiJsonWriter; import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; -import io.ebeaninternal.server.text.json.ReadJson; -import io.ebeaninternal.server.text.json.SpiJsonWriter; import io.ebeaninternal.server.type.ScalarType; import java.io.IOException; @@ -47,7 +47,7 @@ class BeanDescriptorElementEmbeddedMap extends BeanDescriptorElementEmbedded< } @Override - public Object jsonReadCollection(ReadJson readJson, EntityBean parentBean) throws IOException { + public Object jsonReadCollection(SpiJsonReader readJson, EntityBean parentBean) throws IOException { JsonParser parser = readJson.getParser(); ElementCollector add = elementHelp.createCollector(); diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementScalar.java b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementScalar.java index 1b7ac70ea..236d664d3 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementScalar.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementScalar.java @@ -5,9 +5,9 @@ import com.fasterxml.jackson.core.JsonToken; import io.ebean.PersistenceIOException; import io.ebean.SqlUpdate; import io.ebean.bean.EntityBean; +import io.ebeaninternal.api.json.SpiJsonReader; +import io.ebeaninternal.api.json.SpiJsonWriter; import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; -import io.ebeaninternal.server.text.json.ReadJson; -import io.ebeaninternal.server.text.json.SpiJsonWriter; import io.ebeaninternal.server.type.ScalarType; import java.io.IOException; @@ -38,7 +38,7 @@ class BeanDescriptorElementScalar extends BeanDescriptorElement { } @Override - public Object jsonReadCollection(ReadJson readJson, EntityBean parentBean) throws IOException { + public Object jsonReadCollection(SpiJsonReader readJson, EntityBean parentBean) throws IOException { JsonParser parser = readJson.getParser(); ElementCollector add = elementHelp.createCollector(); diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementScalarMap.java b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementScalarMap.java index 9eb87ba0b..96cb172f0 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementScalarMap.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorElementScalarMap.java @@ -3,9 +3,9 @@ package io.ebeaninternal.server.deploy; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import io.ebean.bean.EntityBean; +import io.ebeaninternal.api.json.SpiJsonReader; +import io.ebeaninternal.api.json.SpiJsonWriter; import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; -import io.ebeaninternal.server.text.json.ReadJson; -import io.ebeaninternal.server.text.json.SpiJsonWriter; import io.ebeaninternal.server.type.ScalarType; import java.io.IOException; @@ -51,7 +51,7 @@ class BeanDescriptorElementScalarMap extends BeanDescriptorElement { } @Override - public Object jsonReadCollection(ReadJson readJson, EntityBean parentBean) throws IOException { + public Object jsonReadCollection(SpiJsonReader readJson, EntityBean parentBean) throws IOException { JsonParser parser = readJson.getParser(); ElementCollector add = elementHelp.createCollector(); diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java index 8d90a3af5..ca895e92a 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java @@ -5,8 +5,8 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import io.ebean.bean.EntityBean; import io.ebean.text.json.EJson; -import io.ebeaninternal.server.text.json.ReadJson; -import io.ebeaninternal.server.text.json.SpiJsonWriter; +import io.ebeaninternal.api.json.SpiJsonReader; +import io.ebeaninternal.api.json.SpiJsonWriter; import java.io.IOException; import java.util.LinkedHashMap; @@ -70,7 +70,7 @@ public class BeanDescriptorJsonHelp { } @SuppressWarnings("unchecked") - public T jsonRead(ReadJson jsonRead, String path) throws IOException { + public T jsonRead(SpiJsonReader jsonRead, String path) throws IOException { JsonParser parser = jsonRead.getParser(); //noinspection StatementWithEmptyBody @@ -116,14 +116,14 @@ public class BeanDescriptorJsonHelp { return (T) inheritInfo.readType(discValue).desc().jsonReadObject(jsonRead, path); } - protected T jsonReadObject(ReadJson readJson, String path) throws IOException { + protected T jsonReadObject(SpiJsonReader readJson, String path) throws IOException { EntityBean bean = desc.createEntityBeanForJson(); return jsonReadProperties(readJson, bean, path); } @SuppressWarnings("unchecked") - protected T jsonReadProperties(ReadJson readJson, EntityBean bean, String path) throws IOException { + protected T jsonReadProperties(SpiJsonReader readJson, EntityBean bean, String path) throws IOException { if (path != null) { readJson.pushPath(path); diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanListHelp.java b/src/main/java/io/ebeaninternal/server/deploy/BeanListHelp.java index 338bfb5a9..9b751a16e 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanListHelp.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanListHelp.java @@ -7,7 +7,7 @@ import io.ebean.bean.BeanCollection; import io.ebean.bean.BeanCollectionAdd; import io.ebean.bean.EntityBean; import io.ebean.common.BeanList; -import io.ebeaninternal.server.text.json.SpiJsonWriter; +import io.ebeaninternal.api.json.SpiJsonWriter; import java.io.IOException; import java.util.ArrayList; diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanListHelpElement.java b/src/main/java/io/ebeaninternal/server/deploy/BeanListHelpElement.java index 8191bd0c6..0e473e4eb 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanListHelpElement.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanListHelpElement.java @@ -2,7 +2,7 @@ package io.ebeaninternal.server.deploy; import io.ebean.bean.BeanCollection; import io.ebean.bean.EntityBean; -import io.ebeaninternal.server.text.json.SpiJsonWriter; +import io.ebeaninternal.api.json.SpiJsonWriter; /** * Helper for element collection List. diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanMapHelp.java b/src/main/java/io/ebeaninternal/server/deploy/BeanMapHelp.java index b45bbc98b..72feb6d80 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanMapHelp.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanMapHelp.java @@ -7,7 +7,7 @@ import io.ebean.bean.BeanCollection; import io.ebean.bean.BeanCollectionAdd; import io.ebean.bean.EntityBean; import io.ebean.common.BeanMap; -import io.ebeaninternal.server.text.json.SpiJsonWriter; +import io.ebeaninternal.api.json.SpiJsonWriter; import java.io.IOException; import java.util.LinkedHashMap; diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanMapHelpElement.java b/src/main/java/io/ebeaninternal/server/deploy/BeanMapHelpElement.java index 0531839e0..71cdfb3fa 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanMapHelpElement.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanMapHelpElement.java @@ -3,7 +3,7 @@ package io.ebeaninternal.server.deploy; import io.ebean.bean.BeanCollection; import io.ebean.bean.EntityBean; import io.ebean.common.BeanMap; -import io.ebeaninternal.server.text.json.SpiJsonWriter; +import io.ebeaninternal.api.json.SpiJsonWriter; public class BeanMapHelpElement extends BeanMapHelp { diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java b/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java index 5ea987763..8fa2b9af8 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java @@ -13,6 +13,8 @@ import io.ebean.util.SplitName; import io.ebean.util.StringHelper; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.api.SpiQuery; +import io.ebeaninternal.api.json.SpiJsonReader; +import io.ebeaninternal.api.json.SpiJsonWriter; import io.ebeaninternal.server.core.InternString; import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; import io.ebeaninternal.server.deploy.generatedproperty.GeneratedWhenCreated; @@ -25,8 +27,6 @@ import io.ebeaninternal.server.properties.BeanPropertySetter; import io.ebeaninternal.server.query.STreeProperty; import io.ebeaninternal.server.query.SqlBeanLoad; import io.ebeaninternal.server.query.SqlJoinType; -import io.ebeaninternal.server.text.json.ReadJson; -import io.ebeaninternal.server.text.json.SpiJsonWriter; import io.ebeaninternal.server.type.DataBind; import io.ebeaninternal.server.type.ScalarType; import io.ebeaninternal.server.type.ScalarTypeBoolean; @@ -1453,7 +1453,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { } } - public void jsonRead(ReadJson ctx, EntityBean bean) throws IOException { + public void jsonRead(SpiJsonReader ctx, EntityBean bean) throws IOException { JsonToken event = ctx.nextToken(); if (JsonToken.VALUE_NULL == event) { diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java index 2ef769116..d3c3e8e8f 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocMany.java @@ -1,5 +1,7 @@ package io.ebeaninternal.server.deploy; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; import io.ebean.SqlUpdate; import io.ebean.Transaction; import io.ebean.bean.BeanCollection; @@ -7,22 +9,24 @@ import io.ebean.bean.BeanCollection.ModifyListenMode; import io.ebean.bean.BeanCollectionAdd; import io.ebean.bean.BeanCollectionLoader; import io.ebean.bean.EntityBean; +import io.ebean.bean.PersistenceContext; import io.ebean.text.PathProperties; import io.ebeaninternal.api.SpiExpressionRequest; import io.ebeaninternal.api.SpiQuery; +import io.ebeaninternal.api.json.SpiJsonReader; +import io.ebeaninternal.api.json.SpiJsonWriter; import io.ebeaninternal.server.deploy.id.ImportedId; import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany; import io.ebeaninternal.server.el.ElPropertyChainBuilder; import io.ebeaninternal.server.el.ElPropertyValue; import io.ebeaninternal.server.query.STreePropertyAssocMany; import io.ebeaninternal.server.query.SqlBeanLoad; -import io.ebeaninternal.server.text.json.ReadJson; -import io.ebeaninternal.server.text.json.SpiJsonWriter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.persistence.PersistenceException; import java.io.IOException; +import java.io.StringWriter; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; @@ -804,7 +808,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST if (value != null) { ctx.pushParentBeanMany(bean); if (help != null) { - help.jsonWrite(ctx, name, value, include != null); + jsonWriteCollection(ctx, name, value, include != null); } else { if (isTransient && targetDescriptor == null) { ctx.writeValueUsingObjectMapper(name, value); @@ -820,7 +824,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST } @Override - public void jsonRead(ReadJson readJson, EntityBean parentBean) throws IOException { + public void jsonRead(SpiJsonReader readJson, EntityBean parentBean) throws IOException { jsonHelp.jsonRead(readJson, parentBean); } @@ -909,10 +913,69 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc implements ST elementDescriptor.jsonWriteElement(ctx, element); } + /** + * A Many (element collection) property in bean cache to held as JSON. + */ + @Override + public void setCacheDataValue(EntityBean bean, Object cacheData, PersistenceContext context) { + try { + String asJson = (String) cacheData; + Object collection = jsonReadCollection(asJson); + setValue(bean, collection); + } catch (Exception e) { + logger.error("Error setting value from L2 cache", e); + } + } + + @Override + public Object getCacheDataValue(EntityBean bean) { + try { + Object collection = getValue(bean); + if (collection == null) { + return null; + } + return jsonWriteCollection(collection); + } catch (Exception e) { + logger.error("Error building value element collection json for L2 cache", e); + return null; + } + } + + /** + * Write the collection to JSON. + */ + public String jsonWriteCollection(Object value) throws IOException { + StringWriter writer = new StringWriter(300); + SpiJsonWriter ctx = descriptor.createJsonWriter(writer); + help.jsonWrite(ctx, null, value, false); + ctx.flush(); + return writer.toString(); + } + + /** + * Read the collection as JSON. + */ + public Object jsonReadCollection(String json) throws IOException { + SpiJsonReader ctx = descriptor.createJsonReader(json); + JsonParser parser = ctx.getParser(); + JsonToken event = parser.nextToken(); + if (JsonToken.VALUE_NULL == event) { + return null; + } + return jsonReadCollection(ctx, null); + } + + /** + * Write the collection to JSON. + */ + public void jsonWriteCollection(SpiJsonWriter ctx, String name, Object value, boolean explicitInclude) throws IOException { + help.jsonWrite(ctx, name, value, explicitInclude); + } + /** * Read the collection (JSON Array) containing entity beans. */ - public Object jsonReadCollection(ReadJson readJson, EntityBean parentBean) throws IOException { + public Object jsonReadCollection(SpiJsonReader readJson, EntityBean parentBean) throws IOException { if (elementDescriptor != null && manyType.isMap()) { return elementDescriptor.jsonReadCollection(readJson, parentBean); diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonHelp.java b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonHelp.java index b4962a9e6..11b3f74bc 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonHelp.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonHelp.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import io.ebean.bean.EntityBean; -import io.ebeaninternal.server.text.json.ReadJson; +import io.ebeaninternal.api.json.SpiJsonReader; import java.io.IOException; @@ -35,7 +35,7 @@ class BeanPropertyAssocManyJsonHelp { /** * Read the JSON for this property. */ - public void jsonRead(ReadJson readJson, EntityBean parentBean) throws IOException { + public void jsonRead(SpiJsonReader readJson, EntityBean parentBean) throws IOException { if (!this.many.jsonDeserialize) { return; @@ -61,7 +61,7 @@ class BeanPropertyAssocManyJsonHelp { /** * Read a Transient property using Jackson ObjectMapper. */ - private void jsonReadTransientUsingObjectMapper(ReadJson readJson, EntityBean parentBean) throws IOException { + private void jsonReadTransientUsingObjectMapper(SpiJsonReader readJson, EntityBean parentBean) throws IOException { if (jsonTransient == null) { throw new IllegalStateException("Jackson ObjectMapper is required to read this Transient property " + many.getFullBeanName()); diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonTransient.java b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonTransient.java index fd1b9fe1d..5b05b3261 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonTransient.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonTransient.java @@ -6,7 +6,7 @@ import com.fasterxml.jackson.databind.type.CollectionType; import com.fasterxml.jackson.databind.type.MapType; import com.fasterxml.jackson.databind.type.TypeFactory; import io.ebean.bean.EntityBean; -import io.ebeaninternal.server.text.json.ReadJson; +import io.ebeaninternal.api.json.SpiJsonReader; import java.io.IOException; import java.util.LinkedHashMap; @@ -19,7 +19,7 @@ class BeanPropertyAssocManyJsonTransient { /** * Use Jackson ObjectMapper to read the transient 'many' property. */ - void jsonReadUsingObjectMapper(BeanPropertyAssocMany many, ReadJson readJson, EntityBean parentBean) throws IOException { + void jsonReadUsingObjectMapper(BeanPropertyAssocMany many, SpiJsonReader readJson, EntityBean parentBean) throws IOException { ObjectMapper mapper = readJson.getObjectMapper(); diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java index c4f28fd60..4cea3315a 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java @@ -9,6 +9,8 @@ import io.ebean.bean.EntityBean; import io.ebean.bean.PersistenceContext; import io.ebean.util.SplitName; import io.ebeaninternal.api.SpiQuery; +import io.ebeaninternal.api.json.SpiJsonReader; +import io.ebeaninternal.api.json.SpiJsonWriter; import io.ebeaninternal.server.cache.CacheChangeSet; import io.ebeaninternal.server.cache.CachedBeanData; import io.ebeaninternal.server.core.DefaultSqlUpdate; @@ -19,8 +21,6 @@ import io.ebeaninternal.server.el.ElPropertyValue; import io.ebeaninternal.server.query.STreePropertyAssocOne; import io.ebeaninternal.server.query.SqlBeanLoad; import io.ebeaninternal.server.query.SqlJoinType; -import io.ebeaninternal.server.text.json.ReadJson; -import io.ebeaninternal.server.text.json.SpiJsonWriter; import io.ebeaninternal.server.type.ScalarType; import javax.persistence.PersistenceException; @@ -740,7 +740,7 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc implements STr } @Override - public void jsonRead(ReadJson readJson, EntityBean bean) throws IOException { + public void jsonRead(SpiJsonReader readJson, EntityBean bean) throws IOException { if (jsonDeserialize && targetDescriptor != null) { T assocBean = targetDescriptor.jsonRead(readJson, name); setValue(bean, assocBean); diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertySimpleCollection.java b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertySimpleCollection.java index f00b401cd..a66d996b1 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertySimpleCollection.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertySimpleCollection.java @@ -2,8 +2,8 @@ package io.ebeaninternal.server.deploy; import io.ebean.SqlUpdate; import io.ebean.bean.EntityBean; +import io.ebeaninternal.api.json.SpiJsonReader; import io.ebeaninternal.server.deploy.meta.DeployBeanPropertySimpleCollection; -import io.ebeaninternal.server.text.json.ReadJson; import java.io.IOException; @@ -19,7 +19,7 @@ public class BeanPropertySimpleCollection extends BeanPropertyAssocMany { } @Override - public Object jsonReadCollection(ReadJson readJson, EntityBean parentBean) throws IOException { + public Object jsonReadCollection(SpiJsonReader readJson, EntityBean parentBean) throws IOException { return elementDescriptor.jsonReadCollection(readJson, parentBean); } } diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanSetHelp.java b/src/main/java/io/ebeaninternal/server/deploy/BeanSetHelp.java index 197cbae0a..482ccce62 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanSetHelp.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanSetHelp.java @@ -7,7 +7,7 @@ import io.ebean.bean.BeanCollection; import io.ebean.bean.BeanCollectionAdd; import io.ebean.bean.EntityBean; import io.ebean.common.BeanSet; -import io.ebeaninternal.server.text.json.SpiJsonWriter; +import io.ebeaninternal.api.json.SpiJsonWriter; import java.io.IOException; import java.util.LinkedHashSet; diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanSetHelpElement.java b/src/main/java/io/ebeaninternal/server/deploy/BeanSetHelpElement.java index c490c76e1..94ac9e5a5 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanSetHelpElement.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanSetHelpElement.java @@ -2,7 +2,7 @@ package io.ebeaninternal.server.deploy; import io.ebean.bean.BeanCollection; import io.ebean.bean.EntityBean; -import io.ebeaninternal.server.text.json.SpiJsonWriter; +import io.ebeaninternal.api.json.SpiJsonWriter; /** * Helper for element collection List. diff --git a/src/main/java/io/ebeaninternal/server/deploy/ElementHelpList.java b/src/main/java/io/ebeaninternal/server/deploy/ElementHelpList.java index 6c95550da..8d23b23de 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/ElementHelpList.java +++ b/src/main/java/io/ebeaninternal/server/deploy/ElementHelpList.java @@ -1,5 +1,8 @@ package io.ebeaninternal.server.deploy; +import io.ebean.bean.BeanCollection; +import io.ebean.common.BeanList; + import java.util.ArrayList; import java.util.List; @@ -26,7 +29,9 @@ class ElementHelpList implements ElementHelp { @Override public Object collection() { - return list; + BeanList beanList = new BeanList<>(list); + beanList.setModifyListening(BeanCollection.ModifyListenMode.ALL); + return beanList; } } } diff --git a/src/main/java/io/ebeaninternal/server/deploy/ElementHelpMap.java b/src/main/java/io/ebeaninternal/server/deploy/ElementHelpMap.java index 2d970a48f..6436f6d53 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/ElementHelpMap.java +++ b/src/main/java/io/ebeaninternal/server/deploy/ElementHelpMap.java @@ -1,5 +1,8 @@ package io.ebeaninternal.server.deploy; +import io.ebean.bean.BeanCollection; +import io.ebean.common.BeanMap; + import java.util.LinkedHashMap; import java.util.Map; @@ -26,7 +29,9 @@ class ElementHelpMap implements ElementHelp { @Override public Object collection() { - return map; + BeanMap beanMap = new BeanMap<>(map); + beanMap.setModifyListening(BeanCollection.ModifyListenMode.ALL); + return beanMap; } } } diff --git a/src/main/java/io/ebeaninternal/server/deploy/ElementHelpSet.java b/src/main/java/io/ebeaninternal/server/deploy/ElementHelpSet.java index 53a47de89..9f0e7602d 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/ElementHelpSet.java +++ b/src/main/java/io/ebeaninternal/server/deploy/ElementHelpSet.java @@ -1,5 +1,8 @@ package io.ebeaninternal.server.deploy; +import io.ebean.bean.BeanCollection; +import io.ebean.common.BeanSet; + import java.util.LinkedHashSet; import java.util.Set; @@ -26,7 +29,9 @@ class ElementHelpSet implements ElementHelp { @Override public Object collection() { - return set; + BeanSet beanSet = new BeanSet<>(set); + beanSet.setModifyListening(BeanCollection.ModifyListenMode.ALL); + return beanSet; } } } diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java index 24f3c96aa..68d3171d4 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java @@ -218,7 +218,7 @@ class AnnotationAssocManys extends AnnotationParser { elementDescriptor.setProperties(new String[]{"value"}); } else { elementDescriptor.setProperties(new String[]{"key", "value"}); - String dbKeyColumn = "key"; + String dbKeyColumn = "mkey"; MapKeyColumn mapKeyColumn = get(prop, MapKeyColumn.class); if (mapKeyColumn != null) { dbKeyColumn = mapKeyColumn.name(); diff --git a/src/main/java/io/ebeaninternal/server/persist/SaveManyBase.java b/src/main/java/io/ebeaninternal/server/persist/SaveManyBase.java index 9f892c5e8..2c7bde442 100644 --- a/src/main/java/io/ebeaninternal/server/persist/SaveManyBase.java +++ b/src/main/java/io/ebeaninternal/server/persist/SaveManyBase.java @@ -6,12 +6,18 @@ import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.api.SpiTransaction; import io.ebeaninternal.server.core.PersistRequestBean; import io.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; /** * Base for saving entity bean collections and element collections. */ abstract class SaveManyBase { + private static final Logger log = LoggerFactory.getLogger(SaveManyBase.class); + final PersistRequestBean request; final SpiEbeanServer server; final boolean insertedParent; @@ -48,4 +54,17 @@ abstract class SaveManyBase { } c.modifyReset(); } + + void postElementCollectionUpdate() { + if (!insertedParent) { + if (request.postElementCollectionUpdate()) { + try { + String asJson = many.jsonWriteCollection(value); + request.addCollectionChange(many.getName(), asJson); + } catch (IOException e) { + log.error("Error build element collection entry for L2 cache", e); + } + } + } + } } diff --git a/src/main/java/io/ebeaninternal/server/persist/SaveManyElementCollection.java b/src/main/java/io/ebeaninternal/server/persist/SaveManyElementCollection.java index 402a5e80a..94097fd81 100644 --- a/src/main/java/io/ebeaninternal/server/persist/SaveManyElementCollection.java +++ b/src/main/java/io/ebeaninternal/server/persist/SaveManyElementCollection.java @@ -49,8 +49,6 @@ class SaveManyElementCollection extends SaveManyBase { transaction.depth(-1); resetModifyState(); - if (!insertedParent) { - request.postElementCollectionUpdate(); - } + postElementCollectionUpdate(); } } diff --git a/src/main/java/io/ebeaninternal/server/persist/SaveManyElementCollectionMap.java b/src/main/java/io/ebeaninternal/server/persist/SaveManyElementCollectionMap.java index e19b7e314..f61ac4e0f 100644 --- a/src/main/java/io/ebeaninternal/server/persist/SaveManyElementCollectionMap.java +++ b/src/main/java/io/ebeaninternal/server/persist/SaveManyElementCollectionMap.java @@ -50,8 +50,6 @@ class SaveManyElementCollectionMap extends SaveManyBase { transaction.depth(-1); resetModifyState(); - if (!insertedParent) { - request.postElementCollectionUpdate(); - } + postElementCollectionUpdate(); } } diff --git a/src/main/java/io/ebeaninternal/server/text/json/DJsonBeanReader.java b/src/main/java/io/ebeaninternal/server/text/json/DJsonBeanReader.java index 23511f17d..ecfa3af8c 100644 --- a/src/main/java/io/ebeaninternal/server/text/json/DJsonBeanReader.java +++ b/src/main/java/io/ebeaninternal/server/text/json/DJsonBeanReader.java @@ -1,10 +1,11 @@ package io.ebeaninternal.server.text.json; +import com.fasterxml.jackson.core.JsonParser; import io.ebean.PersistenceIOException; import io.ebean.bean.PersistenceContext; import io.ebean.text.json.JsonBeanReader; +import io.ebeaninternal.api.json.SpiJsonReader; import io.ebeaninternal.server.deploy.BeanDescriptor; -import com.fasterxml.jackson.core.JsonParser; import java.io.IOException; @@ -18,9 +19,9 @@ public class DJsonBeanReader implements JsonBeanReader { private final BeanDescriptor desc; - private final ReadJson readJson; + private final SpiJsonReader readJson; - public DJsonBeanReader(BeanDescriptor desc, ReadJson readJson) { + public DJsonBeanReader(BeanDescriptor desc, SpiJsonReader readJson) { this.desc = desc; this.readJson = readJson; } diff --git a/src/main/java/io/ebeaninternal/server/text/json/DJsonContext.java b/src/main/java/io/ebeaninternal/server/text/json/DJsonContext.java index cc4302d20..dec96b5df 100644 --- a/src/main/java/io/ebeaninternal/server/text/json/DJsonContext.java +++ b/src/main/java/io/ebeaninternal/server/text/json/DJsonContext.java @@ -16,6 +16,8 @@ import io.ebean.text.json.JsonWriteBeanVisitor; import io.ebean.text.json.JsonWriteOptions; import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.api.SpiJsonContext; +import io.ebeaninternal.api.json.SpiJsonReader; +import io.ebeaninternal.api.json.SpiJsonWriter; import io.ebeaninternal.server.deploy.BeanDescriptor; import io.ebeaninternal.server.type.TypeManager; import io.ebeaninternal.util.ParamTypeHelper; @@ -119,7 +121,7 @@ public class DJsonContext implements SpiJsonContext { public T toBean(Class cls, JsonParser parser, JsonReadOptions options) throws JsonIOException { BeanDescriptor desc = getDescriptor(cls); - ReadJson readJson = new ReadJson(desc, parser, options, determineObjectMapper(options)); + SpiJsonReader readJson = new ReadJson(desc, parser, options, determineObjectMapper(options)); try { return desc.jsonRead(readJson, null); } catch (IOException e) { @@ -131,7 +133,7 @@ public class DJsonContext implements SpiJsonContext { public DJsonBeanReader createBeanReader(Class cls, JsonParser parser, JsonReadOptions options) throws JsonIOException { BeanDescriptor desc = getDescriptor(cls); - ReadJson readJson = new ReadJson(desc, parser, options, determineObjectMapper(options)); + SpiJsonReader readJson = new ReadJson(desc, parser, options, determineObjectMapper(options)); return new DJsonBeanReader<>(desc, readJson); } @@ -139,7 +141,7 @@ public class DJsonContext implements SpiJsonContext { public DJsonBeanReader createBeanReader(BeanType beanType, JsonParser parser, JsonReadOptions options) throws JsonIOException { BeanDescriptor desc = (BeanDescriptor) beanType; - ReadJson readJson = new ReadJson(desc, parser, options, determineObjectMapper(options)); + SpiJsonReader readJson = new ReadJson(desc, parser, options, determineObjectMapper(options)); return new DJsonBeanReader<>(desc, readJson); } @@ -172,7 +174,7 @@ public class DJsonContext implements SpiJsonContext { public List toList(Class cls, JsonParser src, JsonReadOptions options) throws JsonIOException { BeanDescriptor desc = getDescriptor(cls); - ReadJson readJson = new ReadJson(desc, src, options, determineObjectMapper(options)); + SpiJsonReader readJson = new ReadJson(desc, src, options, determineObjectMapper(options)); try { JsonToken currentToken = src.getCurrentToken(); @@ -304,7 +306,7 @@ public class DJsonContext implements SpiJsonContext { private String toJsonString(Object value, JsonWriteOptions options) throws JsonIOException { StringWriter writer = new StringWriter(500); - try (JsonGenerator gen = createGenerator(writer)){ + try (JsonGenerator gen = createGenerator(writer)) { toJsonInternal(value, gen, options); } catch (IOException e) { throw new JsonIOException(e); @@ -342,6 +344,14 @@ public class DJsonContext implements SpiJsonContext { } } + @Override + public SpiJsonReader createJsonRead(BeanType beanType, String json) { + + BeanDescriptor desc = (BeanDescriptor) beanType; + JsonParser parser = createParser(new StringReader(json)); + return new ReadJson(desc, parser, null, defaultObjectMapper); + } + @Override public SpiJsonWriter createJsonWriter(Writer writer) { JsonGenerator generator = createGenerator(writer); diff --git a/src/main/java/io/ebeaninternal/server/text/json/ReadJson.java b/src/main/java/io/ebeaninternal/server/text/json/ReadJson.java index a495ed3ba..8b91c86b5 100644 --- a/src/main/java/io/ebeaninternal/server/text/json/ReadJson.java +++ b/src/main/java/io/ebeaninternal/server/text/json/ReadJson.java @@ -1,17 +1,18 @@ package io.ebeaninternal.server.text.json; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.ObjectMapper; import io.ebean.bean.EntityBean; import io.ebean.bean.EntityBeanIntercept; import io.ebean.bean.PersistenceContext; import io.ebean.text.json.JsonReadBeanVisitor; import io.ebean.text.json.JsonReadOptions; import io.ebeaninternal.api.LoadContext; +import io.ebeaninternal.api.json.SpiJsonReader; import io.ebeaninternal.server.deploy.BeanDescriptor; import io.ebeaninternal.server.loadcontext.DLoadContext; import io.ebeaninternal.server.transaction.DefaultPersistenceContext; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.util.Map; @@ -19,7 +20,7 @@ import java.util.Map; /** * Context for JSON read processing. */ -public class ReadJson { +public class ReadJson implements SpiJsonReader { private final BeanDescriptor rootDesc; @@ -100,6 +101,7 @@ public class ReadJson { /** * Return the persistence context being used if any. */ + @Override public PersistenceContext getPersistenceContext() { return persistenceContext; } @@ -107,13 +109,15 @@ public class ReadJson { /** * Return a new instance of ReadJson using the existing context but with a new JsonParser. */ - public ReadJson forJson(JsonParser moreJson, boolean resetContext) { + @Override + public SpiJsonReader forJson(JsonParser moreJson, boolean resetContext) { return new ReadJson(moreJson, this, resetContext); } /** * Add the bean to the persistence context. */ + @Override public void persistenceContextPut(Object beanId, T currentBean) { persistenceContextPutIfAbsent(beanId, (EntityBean) currentBean, rootDesc); @@ -123,6 +127,7 @@ public class ReadJson { * Put the bean into the persistence context. If there is already a matching bean in the * persistence context then return that instance else return null. */ + @Override public Object persistenceContextPutIfAbsent(Object id, EntityBean bean, BeanDescriptor beanDesc) { if (persistenceContext == null) { @@ -153,6 +158,7 @@ public class ReadJson { /** * Return the objectMapper used for this request. */ + @Override public ObjectMapper getObjectMapper() { if (objectMapper == null) { throw new IllegalStateException( @@ -165,6 +171,7 @@ public class ReadJson { /** * Return the JsonParser. */ + @Override public JsonParser getParser() { return parser; } @@ -172,6 +179,7 @@ public class ReadJson { /** * Return the next JsonToken from the underlying parser. */ + @Override public JsonToken nextToken() throws IOException { return parser.nextToken(); } @@ -179,6 +187,7 @@ public class ReadJson { /** * Push the path onto the stack (traversing a 1-M or M-1 etc) */ + @Override public void pushPath(String path) { if (pathStack != null) { pathStack.pushPathKey(path); @@ -188,6 +197,7 @@ public class ReadJson { /** * Pop the path stack. */ + @Override public void popPath() { if (pathStack != null) { pathStack.pop(); @@ -198,6 +208,7 @@ public class ReadJson { * If there is a JsonReadBeanVisitor registered to the current path then * call it's visit method with the bean and unmappedProperties. */ + @Override @SuppressWarnings({ "unchecked", "rawtypes" }) public void beanVisitor(Object bean, Map unmappedProperties) { if (visitorMap != null) { @@ -213,6 +224,7 @@ public class ReadJson { *

* Typically this is used to read Transient properties where the type is unknown to Ebean. */ + @Override public Object readValueUsingObjectMapper(Class propertyType) throws IOException { return getObjectMapper().readValue(parser, propertyType); } diff --git a/src/main/java/io/ebeaninternal/server/text/json/WriteJson.java b/src/main/java/io/ebeaninternal/server/text/json/WriteJson.java index 20c7d3f28..2607974fa 100644 --- a/src/main/java/io/ebeaninternal/server/text/json/WriteJson.java +++ b/src/main/java/io/ebeaninternal/server/text/json/WriteJson.java @@ -9,6 +9,7 @@ import io.ebean.text.json.EJson; import io.ebean.text.json.JsonIOException; import io.ebean.text.json.JsonWriteBeanVisitor; import io.ebeaninternal.api.SpiEbeanServer; +import io.ebeaninternal.api.json.SpiJsonWriter; import io.ebeaninternal.server.deploy.BeanDescriptor; import io.ebeaninternal.server.deploy.BeanProperty; import io.ebeaninternal.server.util.ArrayStack; @@ -89,6 +90,11 @@ public class WriteJson implements SpiJsonWriter { return generator; } + @Override + public void flush() throws IOException { + generator.flush(); + } + @Override public void writeStartObject(String key) { try { @@ -373,7 +379,9 @@ public class WriteJson implements SpiJsonWriter { public void beginAssocMany(String key) { try { pathStack.pushPathKey(key); - generator.writeFieldName(key); + if (key != null) { + generator.writeFieldName(key); + } generator.writeStartArray(); } catch (IOException e) { throw new JsonIOException(e); diff --git a/src/test/java/io/ebeaninternal/server/deploy/TestDiffHelpInsertSimple.java b/src/test/java/io/ebeaninternal/server/deploy/TestDiffHelpInsertSimple.java index 92618f94f..cd48d4247 100644 --- a/src/test/java/io/ebeaninternal/server/deploy/TestDiffHelpInsertSimple.java +++ b/src/test/java/io/ebeaninternal/server/deploy/TestDiffHelpInsertSimple.java @@ -5,7 +5,7 @@ import io.ebean.Ebean; import io.ebean.EbeanServer; import io.ebean.bean.EntityBean; import io.ebeaninternal.api.SpiEbeanServer; -import io.ebeaninternal.server.text.json.SpiJsonWriter; +import io.ebeaninternal.api.json.SpiJsonWriter; import org.junit.Test; import org.tests.model.basic.Customer; import org.tests.model.basic.Order; @@ -57,7 +57,7 @@ public class TestDiffHelpInsertSimple extends BaseTestCase { SpiJsonWriter jsonWriter = spiEbeanServer().jsonExtended().createJsonWriter(buffer); orderDesc.jsonWriteForInsert(jsonWriter, (EntityBean) order1); - jsonWriter.gen().flush(); + jsonWriter.flush(); String asJson = buffer.toString(); diff --git a/src/test/java/io/ebeaninternal/server/deploy/TestDiffHelpInsertWithEmbedded.java b/src/test/java/io/ebeaninternal/server/deploy/TestDiffHelpInsertWithEmbedded.java index 7e04187dd..f2a03c63d 100644 --- a/src/test/java/io/ebeaninternal/server/deploy/TestDiffHelpInsertWithEmbedded.java +++ b/src/test/java/io/ebeaninternal/server/deploy/TestDiffHelpInsertWithEmbedded.java @@ -5,7 +5,7 @@ import io.ebean.Ebean; import io.ebean.EbeanServer; import io.ebean.bean.EntityBean; import io.ebeaninternal.api.SpiEbeanServer; -import io.ebeaninternal.server.text.json.SpiJsonWriter; +import io.ebeaninternal.api.json.SpiJsonWriter; import org.junit.Test; import org.tests.model.embedded.EMain; import org.tests.model.embedded.Eembeddable; @@ -44,7 +44,7 @@ public class TestDiffHelpInsertWithEmbedded extends BaseTestCase { SpiJsonWriter jsonWriter = spiEbeanServer().jsonExtended().createJsonWriter(buffer); emainDesc.jsonWriteForInsert(jsonWriter, emain1); - jsonWriter.gen().flush(); + jsonWriter.flush(); return buffer.toString(); } diff --git a/src/test/java/org/tests/model/elementcollection/EcPerson.java b/src/test/java/org/tests/model/elementcollection/EcPerson.java index 00550b5c6..763b137ab 100644 --- a/src/test/java/org/tests/model/elementcollection/EcPerson.java +++ b/src/test/java/org/tests/model/elementcollection/EcPerson.java @@ -1,5 +1,7 @@ package org.tests.model.elementcollection; +import io.ebean.annotation.Cache; + import javax.persistence.CollectionTable; import javax.persistence.Column; import javax.persistence.ElementCollection; @@ -10,6 +12,7 @@ import javax.persistence.Version; import java.util.ArrayList; import java.util.List; +@Cache @Entity public class EcPerson { diff --git a/src/test/java/org/tests/model/elementcollection/EcblPerson.java b/src/test/java/org/tests/model/elementcollection/EcblPerson.java index 982071e13..c4998d855 100644 --- a/src/test/java/org/tests/model/elementcollection/EcblPerson.java +++ b/src/test/java/org/tests/model/elementcollection/EcblPerson.java @@ -1,5 +1,7 @@ package org.tests.model.elementcollection; +import io.ebean.annotation.Cache; + import javax.persistence.CollectionTable; import javax.persistence.ElementCollection; import javax.persistence.Entity; @@ -9,6 +11,7 @@ import javax.persistence.Version; import java.util.ArrayList; import java.util.List; +@Cache @Entity public class EcblPerson { diff --git a/src/test/java/org/tests/model/elementcollection/EcbmPerson.java b/src/test/java/org/tests/model/elementcollection/EcbmPerson.java index 7a236bb85..992178c54 100644 --- a/src/test/java/org/tests/model/elementcollection/EcbmPerson.java +++ b/src/test/java/org/tests/model/elementcollection/EcbmPerson.java @@ -1,5 +1,7 @@ package org.tests.model.elementcollection; +import io.ebean.annotation.Cache; + import javax.persistence.CollectionTable; import javax.persistence.ElementCollection; import javax.persistence.Entity; @@ -9,7 +11,7 @@ import javax.persistence.Version; import java.util.LinkedHashMap; import java.util.Map; - +@Cache @Entity public class EcbmPerson { diff --git a/src/test/java/org/tests/model/elementcollection/EcmPerson.java b/src/test/java/org/tests/model/elementcollection/EcmPerson.java index 8be26fa50..fafb073e1 100644 --- a/src/test/java/org/tests/model/elementcollection/EcmPerson.java +++ b/src/test/java/org/tests/model/elementcollection/EcmPerson.java @@ -1,5 +1,7 @@ package org.tests.model.elementcollection; +import io.ebean.annotation.Cache; + import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.Entity; @@ -9,6 +11,7 @@ import javax.persistence.Version; import java.util.LinkedHashMap; import java.util.Map; +@Cache @Entity public class EcmPerson { diff --git a/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicCache.java b/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicCache.java new file mode 100644 index 000000000..a73220ce1 --- /dev/null +++ b/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicCache.java @@ -0,0 +1,80 @@ +package org.tests.model.elementcollection; + +import io.ebean.Ebean; +import org.ebeantest.LoggedSqlCollector; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TestElementCollectionBasicCache { + + @Test + public void test() { + + EcPerson person = new EcPerson("Cache1"); + person.getPhoneNumbers().add("021 1234"); + person.getPhoneNumbers().add("021 4321"); + Ebean.save(person); + + EcPerson one = Ebean.find(EcPerson.class) + .setId(person.getId()) + .findOne(); + + one.getPhoneNumbers().size(); + + LoggedSqlCollector.start(); + + EcPerson two = Ebean.find(EcPerson.class) + .setId(person.getId()) + .findOne(); + + two.setName("CacheMod"); + two.getPhoneNumbers().add("027 234234"); + + List sql = LoggedSqlCollector.current(); + assertThat(sql).isEmpty(); // cache hit containing phone numbers + + Ebean.save(two); + + sql = LoggedSqlCollector.current(); + assertThat(sql).hasSize(3); + + Ebean.save(two); + + sql = LoggedSqlCollector.current(); + assertThat(sql).isEmpty(); // no change + + EcPerson three = Ebean.find(EcPerson.class) + .setId(person.getId()) + .findOne(); + + assertThat(three.getName()).isEqualTo("CacheMod"); + assertThat(three.getPhoneNumbers()).contains("021 1234", "021 4321", "027 234234"); + + sql = LoggedSqlCollector.current(); + assertThat(sql).isEmpty(); // cache hit + + three.getPhoneNumbers().add("09 6534"); + Ebean.save(three); + + sql = LoggedSqlCollector.current(); + assertThat(sql).hasSize(2); // cache hit + + EcPerson four = Ebean.find(EcPerson.class) + .setId(person.getId()) + .fetch("phoneNumbers") + .findOne(); + + assertThat(four.getName()).isEqualTo("CacheMod"); + assertThat(four.getPhoneNumbers()).contains("021 1234", "021 4321", "027 234234", "09 6534"); + + sql = LoggedSqlCollector.current(); + assertThat(sql).isEmpty(); // cache hit + + Ebean.delete(four); + + LoggedSqlCollector.stop(); + } +} diff --git a/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicMapCache.java b/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicMapCache.java new file mode 100644 index 000000000..137e8e495 --- /dev/null +++ b/src/test/java/org/tests/model/elementcollection/TestElementCollectionBasicMapCache.java @@ -0,0 +1,85 @@ +package org.tests.model.elementcollection; + +import io.ebean.Ebean; +import org.ebeantest.LoggedSqlCollector; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TestElementCollectionBasicMapCache { + + @Test + public void test() { + + EcmPerson person = new EcmPerson("CacheMap"); + person.getPhoneNumbers().put("home", "021 1234"); + person.getPhoneNumbers().put("work", "021 4321"); + Ebean.save(person); + + + EcmPerson one = Ebean.find(EcmPerson.class) + .setId(person.getId()) + .fetch("phoneNumbers") + .findOne(); + + one.getPhoneNumbers().size(); + + LoggedSqlCollector.start(); + + EcmPerson two = Ebean.find(EcmPerson.class) + .setId(person.getId()) + .findOne(); + + two.setName("CacheMod"); + two.getPhoneNumbers().put("mob", "027 234234"); + + List sql = LoggedSqlCollector.current(); + assertThat(sql).isEmpty(); // cache hit containing phone numbers + + Ebean.save(two); + + sql = LoggedSqlCollector.current(); + assertThat(sql).hasSize(3); + + Ebean.save(two); + + sql = LoggedSqlCollector.current(); + assertThat(sql).isEmpty(); // no change + + EcmPerson three = Ebean.find(EcmPerson.class) + .setId(person.getId()) + .findOne(); + + assertThat(three.getName()).isEqualTo("CacheMod"); + assertThat(three.getPhoneNumbers().toString()).contains("021 1234", "021 4321", "027 234234"); + + sql = LoggedSqlCollector.current(); + assertThat(sql).isEmpty(); // cache hit + + three.getPhoneNumbers().put("oth", "09 6534"); + three.getPhoneNumbers().remove("home"); + three.getPhoneNumbers().remove("work"); + Ebean.save(three); + + sql = LoggedSqlCollector.current(); + assertThat(sql).hasSize(2); // cache hit + + EcmPerson four = Ebean.find(EcmPerson.class) + .setId(person.getId()) + .fetch("phoneNumbers") + .findOne(); + + assertThat(four.getName()).isEqualTo("CacheMod"); + assertThat(four.getPhoneNumbers().toString()).contains("027 234234", "09 6534"); + assertThat(four.getPhoneNumbers()).hasSize(2); + + sql = LoggedSqlCollector.current(); + assertThat(sql).isEmpty(); // cache hit + + Ebean.delete(four); + + LoggedSqlCollector.stop(); + } +} diff --git a/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedListCache.java b/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedListCache.java new file mode 100644 index 000000000..c9cbaf4d4 --- /dev/null +++ b/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedListCache.java @@ -0,0 +1,88 @@ +package org.tests.model.elementcollection; + +import io.ebean.BaseTestCase; +import io.ebean.Ebean; +import org.ebeantest.LoggedSqlCollector; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TestElementCollectionEmbeddedListCache extends BaseTestCase { + + @Test + public void test() { + + EcblPerson person = new EcblPerson("CacheL"); + person.getPhoneNumbers().add(new EcPhone("64", "021","1234")); + person.getPhoneNumbers().add(new EcPhone("64","021","4321")); + Ebean.save(person); + + EcblPerson one = Ebean.find(EcblPerson.class) + .setId(person.getId()) + .fetch("phoneNumbers") + .findOne(); + + LoggedSqlCollector.start(); + + one.getPhoneNumbers().size(); + + List sql = LoggedSqlCollector.current(); + assertThat(sql).isEmpty(); + + EcblPerson two = Ebean.find(EcblPerson.class) + .setId(person.getId()) + .findOne(); + + two.getPhoneNumbers().size(); + assertThat(two.getPhoneNumbers().toString()).contains("64-021-1234", "64-021-4321"); + + sql = LoggedSqlCollector.current(); + assertThat(sql).isEmpty(); // cache hit + + two.getPhoneNumbers().add(new EcPhone("61", "07", "11")); + two.getPhoneNumbers().remove(1); + + Ebean.save(two); + + sql = LoggedSqlCollector.current(); + assertThat(sql).hasSize(2); // update of collection only + assertThat(sql.get(0)).contains("delete from ecbl_person_phone_numbers where person_id=?"); + assertThat(sql.get(1)).contains("insert into ecbl_person_phone_numbers (person_id,country_code,area,number) values (?,?,?,?)"); + + EcblPerson three = Ebean.find(EcblPerson.class) + .setId(person.getId()) + .findOne(); + + assertThat(three.getPhoneNumbers().toString()).contains("61-07-11", "64-021-1234"); + assertThat(three.getPhoneNumbers()).hasSize(2); + + sql = LoggedSqlCollector.current(); + assertThat(sql).isEmpty(); // cache hit + + + three.setName("mod-3"); + three.getPhoneNumbers().remove(0); + + Ebean.save(three); + + sql = LoggedSqlCollector.current(); + assertThat(sql).hasSize(3); + + EcblPerson four = Ebean.find(EcblPerson.class) + .setId(person.getId()) + .findOne(); + + assertThat(four.getPhoneNumbers().toString()).contains("61-07-11"); + assertThat(four.getPhoneNumbers()).hasSize(1); + + + Ebean.delete(four); + sql = LoggedSqlCollector.current(); + assertThat(sql).hasSize(2); + + + LoggedSqlCollector.stop(); + } +} diff --git a/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedMap.java b/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedMap.java index ca342342c..50b37480c 100644 --- a/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedMap.java +++ b/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedMap.java @@ -90,7 +90,7 @@ public class TestElementCollectionEmbeddedMap extends BaseTestCase { assertThat(sql).hasSize(3); assertThat(sql.get(0)).contains("update ecbm_person set name=?, version=? where id=? and version=?"); assertThat(sql.get(1)).contains("delete from ecbm_person_phone_numbers where person_id=?"); - assertThat(sql.get(2)).contains("insert into ecbm_person_phone_numbers (person_id,key,country_code,area,number)"); + assertThat(sql.get(2)).contains("insert into ecbm_person_phone_numbers (person_id,mkey,country_code,area,number)"); updateNothing(bean); } @@ -113,7 +113,7 @@ public class TestElementCollectionEmbeddedMap extends BaseTestCase { List sql = LoggedSqlCollector.current(); assertThat(sql).hasSize(2); assertThat(sql.get(0)).contains("delete from ecbm_person_phone_numbers where person_id=?"); - assertThat(sql.get(1)).contains("insert into ecbm_person_phone_numbers (person_id,key,country_code,area,number) values (?,?,?,?,?)"); + assertThat(sql.get(1)).contains("insert into ecbm_person_phone_numbers (person_id,mkey,country_code,area,number) values (?,?,?,?,?)"); delete(bean); } diff --git a/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedMapCache.java b/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedMapCache.java new file mode 100644 index 000000000..1c29bfc55 --- /dev/null +++ b/src/test/java/org/tests/model/elementcollection/TestElementCollectionEmbeddedMapCache.java @@ -0,0 +1,89 @@ +package org.tests.model.elementcollection; + +import io.ebean.BaseTestCase; +import io.ebean.Ebean; +import org.ebeantest.LoggedSqlCollector; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TestElementCollectionEmbeddedMapCache extends BaseTestCase { + + @Test + public void test() { + + EcbmPerson person = new EcbmPerson("Cache1"); + person.getPhoneNumbers().put("home", new EcPhone("64", "021", "1234")); + person.getPhoneNumbers().put("work", new EcPhone("64", "021", "4321")); + Ebean.save(person); + + + EcbmPerson one = Ebean.find(EcbmPerson.class) + .setId(person.getId()) + .fetch("phoneNumbers") + .findOne(); + + LoggedSqlCollector.start(); + + one.getPhoneNumbers().size(); + + List sql = LoggedSqlCollector.current(); + assertThat(sql).isEmpty(); + + EcbmPerson two = Ebean.find(EcbmPerson.class) + .setId(person.getId()) + .findOne(); + + two.getPhoneNumbers().size(); + assertThat(two.getPhoneNumbers().toString()).contains("64-021-1234", "64-021-4321"); + + sql = LoggedSqlCollector.current(); + assertThat(sql).isEmpty(); // cache hit + + two.getPhoneNumbers().put("mob", new EcPhone("61", "07", "11")); + two.getPhoneNumbers().remove("home"); + + Ebean.save(two); + + sql = LoggedSqlCollector.current(); + assertThat(sql).hasSize(2); // update of collection only + assertThat(sql.get(0)).contains("delete from ecbm_person_phone_numbers where person_id=?"); + assertThat(sql.get(1)).contains("insert into ecbm_person_phone_numbers (person_id,mkey,country_code,area,number) values (?,?,?,?,?)"); + + EcbmPerson three = Ebean.find(EcbmPerson.class) + .setId(person.getId()) + .findOne(); + + assertThat(three.getPhoneNumbers().toString()).contains("61-07-11", "64-021-4321"); + assertThat(three.getPhoneNumbers()).hasSize(2); + + sql = LoggedSqlCollector.current(); + assertThat(sql).isEmpty(); // cache hit + + + three.setName("mod-3"); + three.getPhoneNumbers().remove("work"); + + Ebean.save(three); + + sql = LoggedSqlCollector.current(); + assertThat(sql).hasSize(3); + + EcbmPerson four = Ebean.find(EcbmPerson.class) + .setId(person.getId()) + .findOne(); + + assertThat(four.getPhoneNumbers().toString()).contains("61-07-11"); + assertThat(four.getPhoneNumbers()).hasSize(1); + + + Ebean.delete(four); + sql = LoggedSqlCollector.current(); + assertThat(sql).hasSize(2); + + + LoggedSqlCollector.stop(); + } +} diff --git a/src/test/java/org/tests/text/json/TestJsonBeanDescriptorParse.java b/src/test/java/org/tests/text/json/TestJsonBeanDescriptorParse.java index ebce7d3a2..eaf1825fc 100644 --- a/src/test/java/org/tests/text/json/TestJsonBeanDescriptorParse.java +++ b/src/test/java/org/tests/text/json/TestJsonBeanDescriptorParse.java @@ -1,15 +1,16 @@ package org.tests.text.json; +import com.fasterxml.jackson.core.JsonParser; import io.ebean.BaseTestCase; import io.ebean.BeanState; import io.ebean.Ebean; import io.ebeaninternal.api.SpiEbeanServer; +import io.ebeaninternal.api.json.SpiJsonReader; import io.ebeaninternal.server.deploy.BeanDescriptor; import io.ebeaninternal.server.text.json.ReadJson; -import org.tests.model.basic.Customer; -import com.fasterxml.jackson.core.JsonParser; import org.junit.Assert; import org.junit.Test; +import org.tests.model.basic.Customer; import java.io.IOException; import java.io.StringReader; @@ -28,7 +29,7 @@ public class TestJsonBeanDescriptorParse extends BaseTestCase { StringReader reader = new StringReader("{\"id\":123,\"name\":\"Hello rob\"}"); JsonParser parser = server.json().createParser(reader); - ReadJson readJson = new ReadJson(descriptor, parser, null, null); + SpiJsonReader readJson = new ReadJson(descriptor, parser, null, null); Customer customer = descriptor.jsonRead(readJson, null);