From b99f0029681d3d65af109f50e1bfa5e81b1863cc Mon Sep 17 00:00:00 2001
From: Robin Bygrave
Date: Mon, 14 Mar 2016 17:40:26 +1300
Subject: [PATCH] #599 - ElasticSearch - add query join support for find,
findList, findPagedList, findEach, findEachWhile
---
.../java/com/avaje/ebean/DocumentStore.java | 13 +++--
.../com/avaje/ebean/bean/BeanCollection.java | 5 ++
.../java/com/avaje/ebean/common/BeanList.java | 10 ++++
.../java/com/avaje/ebean/common/BeanMap.java | 9 +++
.../java/com/avaje/ebean/common/BeanSet.java | 10 ++++
.../avaje/ebean/text/json/JsonBeanReader.java | 2 +-
.../ebean/text/json/JsonReadOptions.java | 17 ++++++
.../avaje/ebeaninternal/api/LoadContext.java | 56 ++++++++++---------
.../ebeaninternal/api/LoadSecondaryQuery.java | 2 +-
.../server/core/DefaultServer.java | 10 ++--
.../server/core/OrmQueryRequest.java | 41 +++++++++++---
.../server/core/SpiOrmQueryRequest.java | 3 +-
.../server/deploy/BeanDescriptor.java | 22 ++++++++
.../server/deploy/BeanProperty.java | 3 +
.../server/deploy/BeanPropertyAssocMany.java | 21 +++++++
.../server/loadcontext/DLoadBeanContext.java | 11 ++--
.../server/loadcontext/DLoadContext.java | 4 +-
.../server/loadcontext/DLoadManyContext.java | 17 ++++--
.../server/query/CQueryEngine.java | 4 +-
.../query/CQueryIteratorWithBuffer.java | 3 +-
.../server/querydefn/DefaultOrmQuery.java | 7 +--
.../server/text/json/DJsonBeanReader.java | 4 +-
.../server/text/json/ReadJson.java | 33 +++++++----
.../docstore/api/DocQueryRequest.java | 26 +++++++++
.../docstore/none/NoneDocStore.java | 13 ++---
.../ebean/text/json/JsonBeanReaderTest.java | 2 +-
.../server/deploy/BeanDescriptorTest.java | 15 +++++
27 files changed, 276 insertions(+), 87 deletions(-)
create mode 100644 src/main/java/com/avaje/ebeanservice/docstore/api/DocQueryRequest.java
diff --git a/src/main/java/com/avaje/ebean/DocumentStore.java b/src/main/java/com/avaje/ebean/DocumentStore.java
index d8e267d4a..f92cd518a 100644
--- a/src/main/java/com/avaje/ebean/DocumentStore.java
+++ b/src/main/java/com/avaje/ebean/DocumentStore.java
@@ -1,5 +1,6 @@
package com.avaje.ebean;
+import com.avaje.ebeanservice.docstore.api.DocQueryRequest;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
@@ -48,12 +49,12 @@ public interface DocumentStore {
* If the document is not found null is returned.
*/
@Nullable
- T find(Class beanType, Object id);
+ T find(DocQueryRequest request);
/**
- * Execute the query against the document store returning the list.
+ * Execute the find list query. This request is prepared to execute secondary queries.
*/
- List findList(Query query);
+ List findList(DocQueryRequest request);
/**
* Execute the query against the document store returning the paged list.
@@ -61,7 +62,7 @@ public interface DocumentStore {
* The query should have firstRow or maxRows set prior to calling this method.
*
*/
- PagedList findPagedList(Query query);
+ PagedList findPagedList(DocQueryRequest request);
/**
* Execute the query against the document store with the expectation of a large set of results
@@ -70,7 +71,7 @@ public interface DocumentStore {
* For example, with the ElasticSearch doc store this uses SCROLL.
*
*/
- void findEach(Query query, QueryEachConsumer consumer);
+ void findEach(DocQueryRequest query, QueryEachConsumer consumer);
/**
* Execute the query against the document store with the expectation of a large set of results
@@ -82,7 +83,7 @@ public interface DocumentStore {
* For example, with the ElasticSearch doc store this uses SCROLL.
*
*/
- void findEachWhile(Query query, QueryEachWhileConsumer consumer);
+ void findEachWhile(DocQueryRequest query, QueryEachWhileConsumer consumer);
/**
* Process the queue entries sending updates to the document store or queuing them for later processing.
diff --git a/src/main/java/com/avaje/ebean/bean/BeanCollection.java b/src/main/java/com/avaje/ebean/bean/BeanCollection.java
index f9b2737d7..c24ac482b 100644
--- a/src/main/java/com/avaje/ebean/bean/BeanCollection.java
+++ b/src/main/java/com/avaje/ebean/bean/BeanCollection.java
@@ -31,6 +31,11 @@ public interface BeanCollection extends Serializable {
ALL
}
+ /**
+ * Load bean from another collection.
+ */
+ void loadFrom(BeanCollection> other);
+
/**
* Add a bean to the list/set with modifyListen notification.
*/
diff --git a/src/main/java/com/avaje/ebean/common/BeanList.java b/src/main/java/com/avaje/ebean/common/BeanList.java
index 1ce9aa2d3..2ac4c523d 100644
--- a/src/main/java/com/avaje/ebean/common/BeanList.java
+++ b/src/main/java/com/avaje/ebean/common/BeanList.java
@@ -1,5 +1,6 @@
package com.avaje.ebean.common;
+import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.EntityBean;
@@ -64,6 +65,15 @@ public final class BeanList extends AbstractBeanCollection implements List
list.add((E) bean);
}
+ @Override
+ @SuppressWarnings("unchecked")
+ public void loadFrom(BeanCollection> other) {
+ if (list == null) {
+ list = new ArrayList();
+ }
+ list.addAll((Collection extends E>) other.getActualDetails());
+ }
+
@SuppressWarnings("unchecked")
public void internalAdd(Object bean) {
if (list == null) {
diff --git a/src/main/java/com/avaje/ebean/common/BeanMap.java b/src/main/java/com/avaje/ebean/common/BeanMap.java
index 60190e386..d31e01968 100644
--- a/src/main/java/com/avaje/ebean/common/BeanMap.java
+++ b/src/main/java/com/avaje/ebean/common/BeanMap.java
@@ -1,5 +1,6 @@
package com.avaje.ebean.common;
+import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.EntityBean;
@@ -51,6 +52,14 @@ public final class BeanMap extends AbstractBeanCollection implements Ma
return !touched && (map == null || map.isEmpty());
}
+ @Override
+ @SuppressWarnings("unchecked")
+ public void loadFrom(BeanCollection> other) {
+ BeanMap otherMap = (BeanMap)other;
+ internalPutNull();
+ map.putAll(otherMap.getActualMap());
+ }
+
public void internalPutNull() {
if (map == null) {
map = new LinkedHashMap();
diff --git a/src/main/java/com/avaje/ebean/common/BeanSet.java b/src/main/java/com/avaje/ebean/common/BeanSet.java
index feda082d0..8e1ed5b2f 100644
--- a/src/main/java/com/avaje/ebean/common/BeanSet.java
+++ b/src/main/java/com/avaje/ebean/common/BeanSet.java
@@ -1,5 +1,6 @@
package com.avaje.ebean.common;
+import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.EntityBean;
@@ -57,6 +58,15 @@ public final class BeanSet extends AbstractBeanCollection implements Set other) {
+ if (set == null) {
+ set = new LinkedHashSet();
+ }
+ set.addAll((Collection extends E>) other.getActualDetails());
+ }
+
@Override
public void internalAddWithCheck(Object bean) {
if (set == null || !set.contains(bean)) {
diff --git a/src/main/java/com/avaje/ebean/text/json/JsonBeanReader.java b/src/main/java/com/avaje/ebean/text/json/JsonBeanReader.java
index 6c78f089b..17a4eb027 100644
--- a/src/main/java/com/avaje/ebean/text/json/JsonBeanReader.java
+++ b/src/main/java/com/avaje/ebean/text/json/JsonBeanReader.java
@@ -20,7 +20,7 @@ public interface JsonBeanReader {
/**
* Create a new reader taking the context from the existing one but using a new JsonParser.
*/
- JsonBeanReader forJson(JsonParser moreJson);
+ JsonBeanReader forJson(JsonParser moreJson, boolean resetContext);
/**
* Add a bean explicitly to the persistence context.
diff --git a/src/main/java/com/avaje/ebean/text/json/JsonReadOptions.java b/src/main/java/com/avaje/ebean/text/json/JsonReadOptions.java
index aee1d9407..a8569d078 100644
--- a/src/main/java/com/avaje/ebean/text/json/JsonReadOptions.java
+++ b/src/main/java/com/avaje/ebean/text/json/JsonReadOptions.java
@@ -23,6 +23,8 @@ public class JsonReadOptions {
protected PersistenceContext persistenceContext;
+ protected Object loadContext;
+
/**
* Default constructor.
*/
@@ -100,4 +102,19 @@ public class JsonReadOptions {
public PersistenceContext getPersistenceContext() {
return persistenceContext;
}
+
+ /**
+ * Return the load context to use.
+ */
+ public Object getLoadContext() {
+ return loadContext;
+ }
+
+ /**
+ * Set the load context to use.
+ */
+ public void setLoadContext(Object loadContext) {
+ this.loadContext = loadContext;
+ }
+
}
\ No newline at end of file
diff --git a/src/main/java/com/avaje/ebeaninternal/api/LoadContext.java b/src/main/java/com/avaje/ebeaninternal/api/LoadContext.java
index 05df996b7..beac37864 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/LoadContext.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/LoadContext.java
@@ -16,38 +16,40 @@ public interface LoadContext {
*/
int getSecondaryQueriesMinBatchSize(int defaultQueryBatch);
- /**
- * Execute any secondary (+query) queries if there are any defined.
- * @param parentRequest the originating query request
- */
- void executeSecondaryQueries(OrmQueryRequest> parentRequest);
+ /**
+ * Execute any secondary (+query) queries if there are any defined.
+ *
+ * @param parentRequest the originating query request
+ * @param forEach set true when using findEach iteration
+ */
+ void executeSecondaryQueries(OrmQueryRequest> parentRequest, boolean forEach);
/**
- * Return the node for a given path which is used by AutoTune profiling.
- */
- ObjectGraphNode getObjectGraphNode(String path);
+ * Return the node for a given path which is used by AutoTune profiling.
+ */
+ ObjectGraphNode getObjectGraphNode(String path);
- /**
- * Return the persistence context used by this query and future lazy loading.
- */
- PersistenceContext getPersistenceContext();
+ /**
+ * Return the persistence context used by this query and future lazy loading.
+ */
+ PersistenceContext getPersistenceContext();
- /**
- * Set the persistence context used by this query and future lazy loading.
- *
- * Used by query iterator when processing large result sets.
- *
- */
- void resetPersistenceContext(PersistenceContext persistenceContext);
+ /**
+ * Set the persistence context used by this query and future lazy loading.
+ *
+ * Used by query iterator when processing large result sets.
+ *
+ */
+ void resetPersistenceContext(PersistenceContext persistenceContext);
- /**
- * Register a Bean for lazy loading.
- */
- void register(String path, EntityBeanIntercept ebi);
+ /**
+ * Register a Bean for lazy loading.
+ */
+ void register(String path, EntityBeanIntercept ebi);
- /**
- * Register a collection for lazy loading.
- */
- void register(String path, BeanCollection> bc);
+ /**
+ * Register a collection for lazy loading.
+ */
+ void register(String path, BeanCollection> bc);
}
diff --git a/src/main/java/com/avaje/ebeaninternal/api/LoadSecondaryQuery.java b/src/main/java/com/avaje/ebeaninternal/api/LoadSecondaryQuery.java
index f109d20a9..bb97cccca 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/LoadSecondaryQuery.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/LoadSecondaryQuery.java
@@ -14,5 +14,5 @@ public interface LoadSecondaryQuery {
/**
* Execute the secondary query with a given batch size.
*/
- void loadSecondaryQuery(OrmQueryRequest> parentRequest);
+ void loadSecondaryQuery(OrmQueryRequest> parentRequest, boolean forEach);
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java
index 707fc4859..92b81664a 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java
@@ -1124,7 +1124,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
SpiOrmQueryRequest request = createQueryRequest(spiQuery, t);
if (request.isUseDocStore()) {
- return docStore().find(query.getBeanType(), query.getId());
+ return docStore().find(request);
}
try {
request.initTransIfRequired();
@@ -1329,7 +1329,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
if (spiQuery.isUseDocStore()) {
- return docStore().findPagedList(query);
+ return docStore().findPagedList(createQueryRequest(Type.LIST, query, transaction));
}
return new LimitOffsetPagedList(this, spiQuery);
@@ -1340,7 +1340,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
SpiOrmQueryRequest request = createQueryRequest(Type.ITERATE, query, t);
if (request.isUseDocStore()) {
- docStore().findEach(query, consumer);
+ docStore().findEach(request, consumer);
return;
}
@@ -1353,7 +1353,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
SpiOrmQueryRequest request = createQueryRequest(Type.ITERATE, query, t);
if (request.isUseDocStore()) {
- docStore().findEachWhile(query, consumer);
+ docStore().findEachWhile(request, consumer);
return;
}
@@ -1398,7 +1398,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return (List) result;
}
if (request.isUseDocStore()) {
- return docStore().findList(query);
+ return docStore().findList(request);
}
try {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java
index b70c635c5..fe93f16b0 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java
@@ -12,6 +12,7 @@ import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebean.event.BeanFindController;
import com.avaje.ebean.event.BeanQueryAdapter;
import com.avaje.ebean.event.BeanQueryRequest;
+import com.avaje.ebean.text.json.JsonReadOptions;
import com.avaje.ebeaninternal.api.BeanIdList;
import com.avaje.ebeaninternal.api.CQueryPlanKey;
import com.avaje.ebeaninternal.api.HashQuery;
@@ -59,6 +60,8 @@ public final class OrmQueryRequest extends BeanRequest implements BeanQueryRe
private PersistenceContext persistenceContext;
+ private JsonReadOptions jsonRead;
+
private HashQuery cacheKey;
private CQueryPlanKey queryPlanKey;
@@ -94,8 +97,11 @@ public final class OrmQueryRequest extends BeanRequest implements BeanQueryRe
return ebeanServer.getDatabasePlatform().getLikeClause();
}
- public void executeSecondaryQueries() {
- loadContext.executeSecondaryQueries(this);
+ public void executeSecondaryQueries(boolean forEach) {
+ // disable lazy loading leaves loadContext null
+ if (loadContext != null) {
+ loadContext.executeSecondaryQueries(this, forEach);
+ }
}
/**
@@ -152,7 +158,6 @@ public final class OrmQueryRequest extends BeanRequest implements BeanQueryRe
public void prepareQuery() {
adapterPreQuery();
-
this.secondaryQueries = query.convertJoins();
this.queryPlanKey = query.prepare(this);
}
@@ -213,9 +218,27 @@ public final class OrmQueryRequest extends BeanRequest implements BeanQueryRe
createdTransaction = true;
}
}
- // initialise the persistenceContext and loadContext
- this.persistenceContext = getPersistenceContext(query, transaction);
- this.loadContext = new DLoadContext(this, secondaryQueries);
+ persistenceContext = getPersistenceContext(query, transaction);
+ loadContext = new DLoadContext(this, secondaryQueries);
+ }
+
+ /**
+ * Return the JsonReadOptions taking into account lazy loading and persistence context.
+ */
+ public JsonReadOptions createJsonReadOptions() {
+
+ persistenceContext = getPersistenceContext(query, transaction);
+ if (query.getPersistenceContext() == null) {
+ query.setPersistenceContext(persistenceContext);
+ }
+ jsonRead = new JsonReadOptions();
+ jsonRead.setPersistenceContext(persistenceContext);
+ if (!query.isDisableLazyLoading()) {
+ loadContext = new DLoadContext(this, secondaryQueries);
+ jsonRead.setLoadContext(loadContext);
+ }
+
+ return jsonRead;
}
/**
@@ -224,6 +247,10 @@ public final class OrmQueryRequest extends BeanRequest implements BeanQueryRe
public void flushPersistenceContextOnIterate() {
persistenceContext = new DefaultPersistenceContext();
loadContext.resetPersistenceContext(persistenceContext);
+ if (jsonRead != null) {
+ jsonRead.setPersistenceContext(persistenceContext);
+ jsonRead.setLoadContext(loadContext);
+ }
}
/**
@@ -239,7 +266,7 @@ public final class OrmQueryRequest extends BeanRequest implements BeanQueryRe
// determine the scope (from the query and then server)
PersistenceContextScope scope = ebeanServer.getPersistenceContextScope(query);
- return (scope == PersistenceContextScope.QUERY) ? new DefaultPersistenceContext() : t.getPersistenceContext();
+ return (scope == PersistenceContextScope.QUERY || t == null) ? new DefaultPersistenceContext() : t.getPersistenceContext();
}
/**
diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/SpiOrmQueryRequest.java b/src/main/java/com/avaje/ebeaninternal/server/core/SpiOrmQueryRequest.java
index 555dea8a1..9c4a21fef 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/core/SpiOrmQueryRequest.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/core/SpiOrmQueryRequest.java
@@ -7,6 +7,7 @@ import com.avaje.ebean.Version;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import com.avaje.ebeanservice.docstore.api.DocQueryRequest;
import java.util.List;
import java.util.Map;
@@ -15,7 +16,7 @@ import java.util.Set;
/**
* Defines the ORM query request api.
*/
-public interface SpiOrmQueryRequest {
+public interface SpiOrmQueryRequest extends DocQueryRequest {
/**
* Return the query.
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java
index 8fbee99a0..485b6b234 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java
@@ -107,6 +107,28 @@ public class BeanDescriptor implements MetaBeanInfo, BeanType {
private final ConcurrentHashMap> comparatorCache = new ConcurrentHashMap>();
+ public void merge(EntityBean bean, EntityBean existing) {
+
+ EntityBeanIntercept fromEbi = bean._ebean_getIntercept();
+ EntityBeanIntercept toEbi = existing._ebean_getIntercept();
+
+ int propertyLength = toEbi.getPropertyLength();
+ String[] names = getProperties();
+
+ for (int i = 0; i < propertyLength; i++) {
+
+ if (fromEbi.isLoadedProperty(i) ) {
+ BeanProperty property = getBeanProperty(names[i]);
+ if (!toEbi.isLoadedProperty(i)) {
+ Object val = property.getValue(bean);
+ property.setValue(existing, val);
+ } else if (property.isMany()) {
+ property.merge(bean, existing);
+ }
+ }
+ }
+ }
+
public enum EntityType {
ORM, EMBEDDED, SQL
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java
index 4b322a551..cad1459ff 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java
@@ -1329,4 +1329,7 @@ public class BeanProperty implements ElPropertyValue, Property {
return DocPropertyType.STRING == type && (id || discriminator);
}
+ public void merge(EntityBean bean, EntityBean existing) {
+ // do nothing unless Many property
+ }
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java
index a7514a363..02e302d18 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java
@@ -201,6 +201,27 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc {
// by default not including "Many" properties in document store
}
+ /**
+ * Copy collection value if existing is empty.
+ */
+ @Override
+ public void merge(EntityBean bean, EntityBean existing) {
+
+ Object existingCollection = getVal(existing);
+ if (existingCollection instanceof BeanCollection>) {
+ BeanCollection> toBC = (BeanCollection>)existingCollection;
+ if (!toBC.isPopulated()) {
+ Object fromCollection = getVal(bean);
+ if (fromCollection instanceof BeanCollection>) {
+ BeanCollection> fromBC = (BeanCollection>)fromCollection;
+ if (fromBC.isPopulated()) {
+ toBC.loadFrom(fromBC);
+ }
+ }
+ }
+ }
+ }
+
/**
* Add the bean to the appropriate collection on the parent bean.
*/
diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java
index 59d7f39cb..575a1fc02 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java
@@ -70,13 +70,12 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
return buffer;
}
- public void loadSecondaryQuery(OrmQueryRequest> parentRequest) {
+ public void loadSecondaryQuery(OrmQueryRequest> parentRequest, boolean forEach) {
if (!queryFetch) {
throw new IllegalStateException("Not expecting loadSecondaryQuery() to be called?");
}
synchronized (this) {
-
if (bufferList != null) {
for (LoadBuffer loadBuffer : bufferList) {
if (!loadBuffer.list.isEmpty()) {
@@ -87,8 +86,12 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
break;
}
}
- // this is only run once - secondary query is a one shot deal
- this.bufferList = null;
+ if (forEach) {
+ clear();
+ } else {
+ // this is only run once - secondary query is a one shot deal
+ this.bufferList = null;
+ }
}
}
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java
index 7abcb9dfe..4eeb0decd 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java
@@ -182,12 +182,12 @@ public class DLoadContext implements LoadContext {
/**
* Execute all the secondary queries.
*/
- public void executeSecondaryQueries(OrmQueryRequest> parentRequest) {
+ public void executeSecondaryQueries(OrmQueryRequest> parentRequest, boolean forEach) {
if (secQuery != null) {
for (int i = 0; i < secQuery.size(); i++) {
LoadSecondaryQuery load = getLoadSecondaryQuery(secQuery.get(i).getPath());
- load.loadSecondaryQuery(parentRequest);
+ load.loadSecondaryQuery(parentRequest, forEach);
}
}
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java
index c484651a2..979244593 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java
@@ -21,6 +21,8 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
protected final BeanPropertyAssocMany> property;
+ private final boolean docStoreMapped;
+
private List bufferList;
private LoadBuffer currentBuffer;
@@ -31,6 +33,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
super(parent, property.getBeanDescriptor(), path, defaultBatchSize, queryProps);
this.property = property;
+ this.docStoreMapped = property.getTargetDescriptor().isDocStoreMapped();
// bufferList only required when using query joins (queryFetch)
this.bufferList = (!queryFetch) ? null : new ArrayList();
this.currentBuffer = createBuffer(firstBatchSize);
@@ -56,7 +59,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
public void configureQuery(SpiQuery> query) {
- parent.propagateQueryState(query, desc.isDocStoreMapped());
+ parent.propagateQueryState(query, docStoreMapped);
query.setParentNode(objectGraphNode);
if (queryProps != null) {
queryProps.configureBeanQuery(query);
@@ -85,7 +88,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
bc.setLoader(currentBuffer);
}
- public void loadSecondaryQuery(OrmQueryRequest> parentRequest) {
+ public void loadSecondaryQuery(OrmQueryRequest> parentRequest, boolean forEach) {
if (!queryFetch) {
throw new IllegalStateException("Not expecting loadSecondaryQuery() to be called?");
@@ -103,8 +106,12 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
}
}
- // this is only run once - secondary query is a one shot deal
- this.bufferList = null;
+ if (forEach) {
+ clear();
+ } else {
+ // this is only run once - secondary query is a one shot deal
+ this.bufferList = null;
+ }
}
}
}
@@ -131,7 +138,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
@Override
public boolean isUseDocStore() {
- return context.parent.useDocStore;
+ return context.parent.useDocStore && context.docStoreMapped;
}
public int getBatchSize() {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java
index d11d8fb1e..f2c50e3bc 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java
@@ -316,7 +316,7 @@ public class CQueryEngine {
cquery.auditFindMany();
}
- request.executeSecondaryQueries();
+ request.executeSecondaryQueries(false);
return beanCollection;
@@ -365,7 +365,7 @@ public class CQueryEngine {
cquery.auditFind(bean);
}
- request.executeSecondaryQueries();
+ request.executeSecondaryQueries(false);
return (T) bean;
diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorWithBuffer.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorWithBuffer.java
index ff1907b53..163fdfc2f 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorWithBuffer.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorWithBuffer.java
@@ -42,8 +42,7 @@ class CQueryIteratorWithBuffer implements QueryIterator {
moreToLoad = false;
}
}
- // execute secondary queries
- request.executeSecondaryQueries();
+ request.executeSecondaryQueries(true);
}
return !buffer.isEmpty();
diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
index 2c10120c6..2967d7cae 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
@@ -462,10 +462,10 @@ public class DefaultOrmQuery implements SpiQuery {
@Override
public SpiQuerySecondary convertJoins() {
-
- createExtraJoinsToSupportManyWhereClause();
+ if (!useDocStore) {
+ createExtraJoinsToSupportManyWhereClause();
+ }
markQueryJoins();
-
return new OrmQuerySecondary(removeQueryJoins(), removeLazyJoins());
}
@@ -845,7 +845,6 @@ public class DefaultOrmQuery implements SpiQuery {
public CQueryPlanKey prepare(BeanQueryRequest> request) {
prepareExpressions(request);
-
queryPlanKey = createQueryPlanKey();
return queryPlanKey;
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonBeanReader.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonBeanReader.java
index 8099e9357..0497146e9 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonBeanReader.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonBeanReader.java
@@ -45,7 +45,7 @@ public class DJsonBeanReader implements JsonBeanReader {
}
@Override
- public JsonBeanReader forJson(JsonParser moreJson) {
- return new DJsonBeanReader(desc, readJson.forJson(moreJson));
+ public JsonBeanReader forJson(JsonParser moreJson, boolean resetContext) {
+ return new DJsonBeanReader(desc, readJson.forJson(moreJson, resetContext));
}
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJson.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJson.java
index e2ad41484..e815de642 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJson.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJson.java
@@ -63,21 +63,31 @@ public class ReadJson {
/**
* Construct when transferring load context, persistence context, object mapper etc to a new ReadJson instance.
*/
- private ReadJson(JsonParser moreJson, ReadJson source) {
+ private ReadJson(JsonParser moreJson, ReadJson source, boolean resetContext) {
this.parser = moreJson;
this.rootDesc = source.rootDesc;
this.pathStack = source.pathStack;
this.visitorMap = source.visitorMap;
this.objectMapper = source.objectMapper;
- this.persistenceContext = source.persistenceContext;
- this.loadContext = source.loadContext;
+ if (resetContext) {
+ this.persistenceContext = new DefaultPersistenceContext();
+ this.loadContext = source.loadContext;
+ if (loadContext != null) {
+ loadContext.resetPersistenceContext(persistenceContext);
+ }
+ } else {
+ this.persistenceContext = source.persistenceContext;
+ this.loadContext = source.loadContext;
+ }
}
private LoadContext initLoadContext(BeanDescriptor> desc, JsonReadOptions readOptions) {
- if (readOptions != null && readOptions.isEnableLazyLoading()) {
+ if (readOptions == null) return null;
+ if (readOptions.isEnableLazyLoading() && readOptions.getLoadContext() == null) {
return new DLoadContext(desc, persistenceContext);
+ } else {
+ return (LoadContext) readOptions.getLoadContext();
}
- return null;
}
private PersistenceContext initPersistenceContext(JsonReadOptions readOptions) {
@@ -97,8 +107,8 @@ public class ReadJson {
/**
* Return a new instance of ReadJson using the existing context but with a new JsonParser.
*/
- public ReadJson forJson(JsonParser moreJson) {
- return new ReadJson(moreJson, this);
+ public ReadJson forJson(JsonParser moreJson, boolean resetContext) {
+ return new ReadJson(moreJson, this, resetContext);
}
/**
@@ -120,8 +130,11 @@ public class ReadJson {
return null;
}
- Object contextBean = beanDesc.contextPutIfAbsent(persistenceContext, id, bean);
- if (contextBean == null) {
+ Object existing = beanDesc.contextPutIfAbsent(persistenceContext, id, bean);
+ if (existing != null) {
+ beanDesc.merge(bean, (EntityBean)existing);
+
+ } else {
if (loadContext != null) {
EntityBeanIntercept ebi = bean._ebean_getIntercept();
if (ebi.isPartial()) {
@@ -134,7 +147,7 @@ public class ReadJson {
}
return null;
}
- return contextBean;
+ return existing;
}
/**
diff --git a/src/main/java/com/avaje/ebeanservice/docstore/api/DocQueryRequest.java b/src/main/java/com/avaje/ebeanservice/docstore/api/DocQueryRequest.java
new file mode 100644
index 000000000..42fa4ddd2
--- /dev/null
+++ b/src/main/java/com/avaje/ebeanservice/docstore/api/DocQueryRequest.java
@@ -0,0 +1,26 @@
+package com.avaje.ebeanservice.docstore.api;
+
+import com.avaje.ebean.text.json.JsonReadOptions;
+import com.avaje.ebeaninternal.api.SpiQuery;
+
+/**
+ * A Query request for the document store.
+ */
+public interface DocQueryRequest {
+
+ /**
+ * Return the query for this request.
+ */
+ SpiQuery getQuery();
+
+ /**
+ * Create JsonReadOptions taking into account persistence context and lazy loading support.
+ */
+ JsonReadOptions createJsonReadOptions();
+
+ /**
+ * Execute secondary queries.
+ */
+ void executeSecondaryQueries(boolean forEach);
+
+}
diff --git a/src/main/java/com/avaje/ebeanservice/docstore/none/NoneDocStore.java b/src/main/java/com/avaje/ebeanservice/docstore/none/NoneDocStore.java
index 0b4662d10..fc09931c0 100644
--- a/src/main/java/com/avaje/ebeanservice/docstore/none/NoneDocStore.java
+++ b/src/main/java/com/avaje/ebeanservice/docstore/none/NoneDocStore.java
@@ -6,7 +6,7 @@ import com.avaje.ebean.PagedList;
import com.avaje.ebean.Query;
import com.avaje.ebean.QueryEachConsumer;
import com.avaje.ebean.QueryEachWhileConsumer;
-import org.jetbrains.annotations.Nullable;
+import com.avaje.ebeanservice.docstore.api.DocQueryRequest;
import java.io.IOException;
import java.util.List;
@@ -60,29 +60,28 @@ public class NoneDocStore implements DocumentStore {
throw implementationNotInClassPath();
}
- @Nullable
@Override
- public T find(Class beanType, Object id) {
+ public T find(DocQueryRequest request) {
throw implementationNotInClassPath();
}
@Override
- public PagedList findPagedList(Query query) {
+ public PagedList findPagedList(DocQueryRequest request) {
throw implementationNotInClassPath();
}
@Override
- public List findList(Query query) {
+ public List findList(DocQueryRequest request) {
throw implementationNotInClassPath();
}
@Override
- public void findEach(Query query, QueryEachConsumer consumer) {
+ public void findEach(DocQueryRequest query, QueryEachConsumer consumer) {
throw implementationNotInClassPath();
}
@Override
- public void findEachWhile(Query query, QueryEachWhileConsumer consumer) {
+ public void findEachWhile(DocQueryRequest query, QueryEachWhileConsumer consumer) {
throw implementationNotInClassPath();
}
diff --git a/src/test/java/com/avaje/ebean/text/json/JsonBeanReaderTest.java b/src/test/java/com/avaje/ebean/text/json/JsonBeanReaderTest.java
index ff85cd96c..b7dad8a27 100644
--- a/src/test/java/com/avaje/ebean/text/json/JsonBeanReaderTest.java
+++ b/src/test/java/com/avaje/ebean/text/json/JsonBeanReaderTest.java
@@ -47,7 +47,7 @@ public class JsonBeanReaderTest extends BaseTestCase {
beanReader.read();
JsonParser more = getParser();
- JsonBeanReader moreReader = beanReader.forJson(more);
+ JsonBeanReader moreReader = beanReader.forJson(more, true);
Customer customer = moreReader.read();
assertThat(customer.getId()).isEqualTo(42);
diff --git a/src/test/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorTest.java b/src/test/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorTest.java
index e58eaa8b1..1be4b9100 100644
--- a/src/test/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorTest.java
+++ b/src/test/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorTest.java
@@ -1,6 +1,7 @@
package com.avaje.ebeaninternal.server.deploy;
import com.avaje.ebean.BaseTestCase;
+import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.plugin.Property;
import com.avaje.tests.model.basic.Customer;
import com.avaje.tests.model.basic.Order;
@@ -45,4 +46,18 @@ public class BeanDescriptorTest extends BaseTestCase {
assertThat(props).extracting("name").contains("id", "status", "orderDate", "shipDate");
}
+ @Test
+ public void merge_when_empty() {
+
+ Customer from = new Customer();
+ from.setId(42);
+ from.setName("rob");
+
+ Customer to = new Customer();
+ customerDesc.merge((EntityBean)from, (EntityBean)to);
+
+ assertThat(to.getId()).isEqualTo(42);
+ assertThat(to.getName()).isEqualTo("rob");
+ }
+
}
\ No newline at end of file