From 6a6cff22363c309017caaee91cdf255466cfa450 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Tue, 25 Nov 2014 23:40:21 +1300 Subject: [PATCH] Fix for #209 - refresh() ... does not refresh OneToMany or ManyToMany properties --- .../com/avaje/ebean/bean/BeanCollection.java | 20 +- .../ebean/common/AbstractBeanCollection.java | 10 +- .../java/com/avaje/ebean/common/BeanList.java | 15 +- .../java/com/avaje/ebean/common/BeanMap.java | 19 +- .../java/com/avaje/ebean/common/BeanSet.java | 8 + .../server/core/DefaultBeanLoader.java | 74 +++-- .../server/deploy/BeanCollectionHelp.java | 19 +- .../deploy/BeanCollectionHelpFactory.java | 8 +- .../server/deploy/BeanDescriptor.java | 11 + .../server/deploy/BeanListHelp.java | 47 ++- .../server/deploy/BeanMapHelp.java | 291 +++++++++--------- .../server/deploy/BeanPropertyAssocMany.java | 24 +- .../deploy/BeanPropertyAssocManyJsonHelp.java | 3 +- .../server/deploy/BeanPropertyAssocOne.java | 9 +- .../server/deploy/BeanSetHelp.java | 250 ++++++++------- .../server/deploy/DRawSqlSelect.java | 7 +- .../ebeaninternal/server/query/CQuery.java | 71 ++--- .../ebeaninternal/server/query/SqlTree.java | 102 +++--- .../server/query/SqlTreeBuilder.java | 40 +-- .../text/json/DefaultJsonValueAdapter.java | 57 ---- .../batchload/TestSecondQueryNoRows.java | 17 +- .../com/avaje/tests/model/basic/DMLTest.java | 60 ---- .../tests/query/TestQueryFilterMany.java | 37 ++- .../tests/refresh/TestRefreshWithMany.java | 88 ++++++ .../avaje/tests/text/csv/TestCsvReader.java | 4 +- .../text/csv/TestCsvReaderWithCallback.java | 4 +- .../TestTextJsonBeanReadVisitor.java.orig | 50 --- 27 files changed, 618 insertions(+), 727 deletions(-) delete mode 100644 src/main/java/com/avaje/ebeaninternal/server/text/json/DefaultJsonValueAdapter.java delete mode 100644 src/test/java/com/avaje/tests/model/basic/DMLTest.java create mode 100644 src/test/java/com/avaje/tests/refresh/TestRefreshWithMany.java delete mode 100644 src/test/java/com/avaje/tests/text/json/TestTextJsonBeanReadVisitor.java.orig diff --git a/src/main/java/com/avaje/ebean/bean/BeanCollection.java b/src/main/java/com/avaje/ebean/bean/BeanCollection.java index 1f3980396..83eed3fe9 100644 --- a/src/main/java/com/avaje/ebean/bean/BeanCollection.java +++ b/src/main/java/com/avaje/ebean/bean/BeanCollection.java @@ -31,6 +31,13 @@ public interface BeanCollection extends Serializable { ALL } + /** + * Reset the collection back to an empty state ready for reloading. + *

+ * This is done as part of bean refresh. + */ + public void reset(EntityBean ownerBean, String propertyName); + /** * Return true if the collection is empty and untouched. Used to detect if a * collection was 'cleared' deliberately or just un-initialised. @@ -47,14 +54,6 @@ public interface BeanCollection extends Serializable { */ public String getPropertyName(); - /** - * Return the index position of this collection in the lazy/query loader. - *

- * Used for batch loading of collections. - *

- */ - public int getLoaderIndex(); - /** * Check after the lazy load that the underlying collection is not null * (handle case where join to many not outer). @@ -108,11 +107,6 @@ public interface BeanCollection extends Serializable { */ public void internalAdd(Object bean); - /** - * Returns the underlying List Set or Map object. - */ - public Object getActualCollection(); - /** * Return the number of elements in the List Set or Map. */ diff --git a/src/main/java/com/avaje/ebean/common/AbstractBeanCollection.java b/src/main/java/com/avaje/ebean/common/AbstractBeanCollection.java index a5b110402..d497298fb 100644 --- a/src/main/java/com/avaje/ebean/common/AbstractBeanCollection.java +++ b/src/main/java/com/avaje/ebean/common/AbstractBeanCollection.java @@ -38,12 +38,12 @@ public abstract class AbstractBeanCollection implements BeanCollection { /** * The owning bean (used for lazy fetch). */ - protected final EntityBean ownerBean; + protected EntityBean ownerBean; /** * The name of this property in the owning bean (used for lazy fetch). */ - protected final String propertyName; + protected String propertyName; protected ModifyHolder modifyHolder; @@ -62,8 +62,6 @@ public abstract class AbstractBeanCollection implements BeanCollection { * Constructor not non-lazy loading collection. */ public AbstractBeanCollection() { - this.ownerBean = null; - this.propertyName = null; } /** @@ -85,10 +83,6 @@ public abstract class AbstractBeanCollection implements BeanCollection { return propertyName; } - public int getLoaderIndex() { - return loaderIndex; - } - public ExpressionList getFilterMany() { return filterMany; } diff --git a/src/main/java/com/avaje/ebean/common/BeanList.java b/src/main/java/com/avaje/ebean/common/BeanList.java index 4ebb821e7..9ed264223 100644 --- a/src/main/java/com/avaje/ebean/common/BeanList.java +++ b/src/main/java/com/avaje/ebean/common/BeanList.java @@ -46,6 +46,14 @@ public final class BeanList extends AbstractBeanCollection implements List super(loader, ownerBean, propertyName); } + @Override + public void reset(EntityBean ownerBean, String propertyName) { + this.ownerBean = ownerBean; + this.propertyName = propertyName; + this.list = null; + this.touched = false; + } + @Override public boolean isEmptyAndUntouched() { return !touched && (list == null || list.isEmpty()); @@ -130,13 +138,6 @@ public final class BeanList extends AbstractBeanCollection implements List return list; } - /** - * Returns the underlying list. - */ - public Object getActualCollection() { - return list; - } - /** * Return true if the underlying list is populated. */ diff --git a/src/main/java/com/avaje/ebean/common/BeanMap.java b/src/main/java/com/avaje/ebean/common/BeanMap.java index 754be731a..f777ccb41 100644 --- a/src/main/java/com/avaje/ebean/common/BeanMap.java +++ b/src/main/java/com/avaje/ebean/common/BeanMap.java @@ -39,7 +39,15 @@ public final class BeanMap extends AbstractBeanCollection implements Ma public BeanMap(BeanCollectionLoader ebeanServer, EntityBean ownerBean, String propertyName) { super(ebeanServer, ownerBean, propertyName); } - + + @Override + public void reset(EntityBean ownerBean, String propertyName) { + this.ownerBean = ownerBean; + this.propertyName = propertyName; + this.map = null; + this.touched = false; + } + public boolean isEmptyAndUntouched() { return !touched && (map == null || map.isEmpty()); } @@ -146,15 +154,8 @@ public final class BeanMap extends AbstractBeanCollection implements Ma } - /** - * Returns the underlying map. - */ - public Object getActualCollection() { - return map; - } - public String toString() { - StringBuffer sb = new StringBuffer(50); + StringBuilder sb = new StringBuilder(50); sb.append("BeanMap "); if (isReadOnly()) { sb.append("readOnly "); diff --git a/src/main/java/com/avaje/ebean/common/BeanSet.java b/src/main/java/com/avaje/ebean/common/BeanSet.java index f4cddde29..f14cfae96 100644 --- a/src/main/java/com/avaje/ebean/common/BeanSet.java +++ b/src/main/java/com/avaje/ebean/common/BeanSet.java @@ -40,6 +40,14 @@ public final class BeanSet extends AbstractBeanCollection implements Set * This means we can have large and variable requestedBatchSizes. @@ -51,7 +51,7 @@ public class DefaultBeanLoader { *

*/ private int getBatchSize(int batchSize) { - + if (batchSize == 1) { // there is only one bean/collection to load return 1; @@ -74,10 +74,10 @@ public class DefaultBeanLoader { } return batchSize; } - - public void refreshMany(EntityBean parentBean, String propertyName) { - refreshMany(parentBean, propertyName, null); - } + + public void refreshMany(EntityBean parentBean, String propertyName) { + refreshMany(parentBean, propertyName, null); + } public void loadMany(LoadManyRequest loadRequest) { @@ -113,7 +113,7 @@ public class DefaultBeanLoader { if (orderBy != null) { query.orderBy(orderBy); } - + String extraWhere = many.getExtraWhere(); if (extraWhere != null) { // replace special ${ta} placeholder with the base table alias @@ -121,7 +121,7 @@ public class DefaultBeanLoader { String ew = StringHelper.replaceString(extraWhere, "${ta}", "t0"); query.where().raw(ew); } - + query.setLazyLoadForParents(idList, many); many.addWhereParentIdIn(query, idList); @@ -153,7 +153,7 @@ public class DefaultBeanLoader { desc.cacheManyPropPut(many, bc, parentId); } } - + // log the query (for testing secondary queries) loadRequest.logSecondaryQuery(query); } @@ -163,18 +163,17 @@ public class DefaultBeanLoader { EntityBean parentBean = bc.getOwnerBean(); String propertyName = bc.getPropertyName(); - //ObjectGraphNode node = ctx == null ? null : ctx.getObjectGraphNode(); - loadManyInternal(parentBean, propertyName, null, false, null, onlyIds); } - public void refreshMany(EntityBean parentBean, String propertyName, Transaction t) { - loadManyInternal(parentBean, propertyName, t, true, null, false); - } + public void refreshMany(EntityBean parentBean, String propertyName, Transaction t) { + loadManyInternal(parentBean, propertyName, t, true, null, false); + } - private void loadManyInternal(EntityBean parentBean, String propertyName, Transaction t, boolean refresh, ObjectGraphNode node, boolean onlyIds) { + private void loadManyInternal(EntityBean parentBean, String propertyName, Transaction t, boolean refresh, + ObjectGraphNode node, boolean onlyIds) { - EntityBeanIntercept ebi = ((EntityBean) parentBean)._ebean_getIntercept(); + EntityBeanIntercept ebi = parentBean._ebean_getIntercept(); PersistenceContext pc = ebi.getPersistenceContext(); BeanDescriptor parentDesc = server.getBeanDescriptor(parentBean.getClass()); @@ -182,7 +181,7 @@ public class DefaultBeanLoader { BeanCollection beanCollection = null; ExpressionList filterMany = null; - + Object currentValue = many.getValue(parentBean); if (currentValue instanceof BeanCollection) { beanCollection = (BeanCollection) currentValue; @@ -211,7 +210,7 @@ public class DefaultBeanLoader { if (refresh) { // populate a new collection - Object emptyCollection = many.createEmpty(false); + BeanCollection emptyCollection = many.createEmpty(parentBean); many.setValue(parentBean, emptyCollection); query.setLoadDescription("+refresh", null); } else { @@ -329,7 +328,7 @@ public class DefaultBeanLoader { if (loadRequest.isLoadCache()) { for (int i = 0; i < list.size(); i++) { - desc.cacheBeanPutData((EntityBean)list.get(i)); + desc.cacheBeanPutData((EntityBean) list.get(i)); } } @@ -338,33 +337,38 @@ public class DefaultBeanLoader { // necessary but allow processing to continue until it is accessed by client code ebis[i].checkLazyLoadFailure(); } - + // log the query (for testing secondary queries) loadRequest.logSecondaryQuery(query); } - public void refresh(EntityBean bean) { - refreshBeanInternal(bean, SpiQuery.Mode.REFRESH_BEAN, -1); - } - - public void loadBean(EntityBeanIntercept ebi) { - refreshBeanInternal(ebi.getOwner(), SpiQuery.Mode.LAZYLOAD_BEAN, -1); - } + public void refresh(EntityBean bean) { + refreshBeanInternal(bean, SpiQuery.Mode.REFRESH_BEAN, -1); + } + + public void loadBean(EntityBeanIntercept ebi) { + refreshBeanInternal(ebi.getOwner(), SpiQuery.Mode.LAZYLOAD_BEAN, -1); + } private void refreshBeanInternal(EntityBean bean, SpiQuery.Mode mode, int embeddedOwnerIndex) { - EntityBeanIntercept ebi = ((EntityBean) bean)._ebean_getIntercept();; + EntityBeanIntercept ebi = bean._ebean_getIntercept(); + ; PersistenceContext pc = ebi.getPersistenceContext(); + if (Mode.REFRESH_BEAN == mode) { + // need a new PersistenceContext for REFRESH + pc = null; + } BeanDescriptor desc = server.getBeanDescriptor(bean.getClass()); if (EntityType.EMBEDDED == desc.getEntityType()) { // lazy loading on an embedded bean property - EntityBean embeddedOwner = (EntityBean)ebi.getEmbeddedOwner(); + EntityBean embeddedOwner = (EntityBean) ebi.getEmbeddedOwner(); int ownerIndex = ebi.getEmbeddedOwnerIndex(); refreshBeanInternal(embeddedOwner, mode, ownerIndex); } - + Object id = desc.getId(bean); if (pc == null) { @@ -379,7 +383,7 @@ public class DefaultBeanLoader { if (embeddedOwnerIndex == -1) { if (SpiQuery.Mode.LAZYLOAD_BEAN.equals(mode) && desc.isBeanCaching()) { // lazy loading and the bean cache is active - if (desc.cacheBeanLoad((EntityBean)bean, ebi, id)) { + if (desc.cacheBeanLoad(bean, ebi, id)) { return; } } @@ -395,9 +399,9 @@ public class DefaultBeanLoader { if (embeddedOwnerIndex > -1) { String embeddedBeanPropertyName = ebi.getProperty(embeddedOwnerIndex); - query.select("id,"+embeddedBeanPropertyName); + query.select("id," + embeddedBeanPropertyName); } - + // don't collect autoFetch usage profiling information // as we just copy the data out of these fetched beans // and put the data into the original bean @@ -415,7 +419,7 @@ public class DefaultBeanLoader { if (ebi.isReadOnly()) { query.setReadOnly(true); } - + if (SpiQuery.Mode.REFRESH_BEAN.equals(mode)) { // explicitly state to load all properties on REFRESH. // Lobs default to fetch lazy so this forces lobs to be @@ -429,5 +433,7 @@ public class DefaultBeanLoader { throw new EntityNotFoundException(msg); } + desc.resetManyProperties(dbBean); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCollectionHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCollectionHelp.java index 08eb0d9ff..f6c8d2d7b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCollectionHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCollectionHelp.java @@ -1,8 +1,5 @@ package com.avaje.ebeaninternal.server.deploy; -import java.io.IOException; -import java.util.Iterator; - import com.avaje.ebean.EbeanServer; import com.avaje.ebean.Query; import com.avaje.ebean.Transaction; @@ -12,6 +9,8 @@ import com.avaje.ebean.bean.BeanCollectionLoader; import com.avaje.ebean.bean.EntityBean; import com.avaje.ebeaninternal.server.text.json.WriteJson; +import java.io.IOException; + /** * Helper functions for performing tasks on Lists Sets or Maps. */ @@ -30,15 +29,15 @@ public interface BeanCollectionHelp { */ public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey); + /** + * Create an empty collection of the correct type without a parent bean. + */ + public BeanCollection createEmptyNoParent(); + /** * Create an empty collection of the correct type. */ - public Object createEmpty(boolean vanilla); - - /** - * Create an iterator for reading the entries. - */ - public Iterator getIterator(Object collection); + public BeanCollection createEmpty(EntityBean bean); /** * Add a bean to the List Set or Map. @@ -48,7 +47,7 @@ public interface BeanCollectionHelp { /** * Create a lazy loading proxy for a List Set or Map. */ - public BeanCollection createReference(EntityBean parentBean, String propertyName); + public BeanCollection createReference(EntityBean parentBean); /** * Refresh the List Set or Map. diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCollectionHelpFactory.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCollectionHelpFactory.java index caee7c364..a7badac13 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCollectionHelpFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCollectionHelpFactory.java @@ -9,6 +9,10 @@ import com.avaje.ebeaninternal.server.core.OrmQueryRequest; */ public class BeanCollectionHelpFactory { + static final BeanListHelp LIST_HELP = new BeanListHelp(); + + static final BeanSetHelp SET_HELP = new BeanSetHelp(); + /** * Create the helper based on the many property. */ @@ -33,10 +37,10 @@ public class BeanCollectionHelpFactory { SpiQuery.Type manyType = request.getQuery().getType(); if (manyType.equals(SpiQuery.Type.LIST)){ - return new BeanListHelp(); + return LIST_HELP; } else if (manyType.equals(SpiQuery.Type.SET)) { - return new BeanSetHelp(); + return SET_HELP; } else { BeanDescriptor target = request.getBeanDescriptor(); 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 133a1d0f9..cc4ef6d7d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java @@ -1487,6 +1487,17 @@ public class BeanDescriptor implements MetaBeanInfo { return prop; } + /** + * Reset the many properties to empty state ready for reloading. + */ + public void resetManyProperties(Object dbBean) { + + EntityBean bean = (EntityBean)dbBean; + for (int i = 0; i < propertiesMany.length; i++) { + propertiesMany[i].resetMany(bean); + } + } + /** * Return the name of the server this BeanDescriptor belongs to. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanListHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanListHelp.java index 4e7568b4d..286f5326a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanListHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanListHelp.java @@ -22,29 +22,36 @@ public final class BeanListHelp implements BeanCollectionHelp { private final BeanPropertyAssocMany many; private final BeanDescriptor targetDescriptor; + private final String propertyName; + private BeanCollectionLoader loader; public BeanListHelp(BeanPropertyAssocMany many) { this.many = many; this.targetDescriptor = many.getTargetDescriptor(); + this.propertyName = many.getName(); } public BeanListHelp() { this.many = null; this.targetDescriptor = null; + this.propertyName = null; } + @Override public void setLoader(BeanCollectionLoader loader) { this.loader = loader; } - + /** * Internal add bypassing any modify listening. */ + @Override public void add(BeanCollection collection, EntityBean bean) { collection.internalAdd(bean); } + @Override public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) { if (bc instanceof BeanList) { @@ -54,57 +61,42 @@ public final class BeanListHelp implements BeanCollectionHelp { bl.setActualList(new ArrayList()); } return bl; - } else if (bc instanceof List) { - return new VanillaAdd((List) bc); } else { throw new RuntimeException("Unhandled type " + bc); } } - @SuppressWarnings("unchecked") - static class VanillaAdd implements BeanCollectionAdd { - - @SuppressWarnings("rawtypes") - private final List list; - - private VanillaAdd(List list) { - this.list = list; - } - - public void addBean(EntityBean bean) { - list.add(bean); - } + @Override + public BeanCollection createEmptyNoParent() { + return new BeanList(); } - public Iterator getIterator(Object collection) { - return ((List) collection).iterator(); - } - - public Object createEmpty(boolean vanilla) { - if (vanilla) { - return new ArrayList(); - } - BeanList beanList = new BeanList(); + @Override + public BeanCollection createEmpty(EntityBean parentBean) { + BeanList beanList = new BeanList(loader, parentBean, propertyName); if (many != null) { beanList.setModifyListening(many.getModifyListenMode()); } return beanList; } - public BeanCollection createReference(EntityBean parentBean, String propertyName) { + @Override + public BeanCollection createReference(EntityBean parentBean) { BeanList beanList = new BeanList(loader, parentBean, propertyName); beanList.setModifyListening(many.getModifyListenMode()); return beanList; } + @Override public void refresh(EbeanServer server, Query query, Transaction t, EntityBean parentBean) { BeanList newBeanList = (BeanList) server.findList(query, t); refresh(newBeanList, parentBean); } + @Override public void refresh(BeanCollection bc, EntityBean parentBean) { BeanList newBeanList = (BeanList) bc; @@ -129,6 +121,7 @@ public final class BeanListHelp implements BeanCollectionHelp { } } + @Override public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException { List list; @@ -150,7 +143,7 @@ public final class BeanListHelp implements BeanCollectionHelp { ctx.writeStartArray(name); for (int j = 0; j < list.size(); j++) { - targetDescriptor.jsonWrite(ctx, (EntityBean)list.get(j)); + targetDescriptor.jsonWrite(ctx, (EntityBean) list.get(j)); } ctx.writeEndArray(); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanMapHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanMapHelp.java index 79a8b972c..466d6916e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanMapHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanMapHelp.java @@ -1,11 +1,5 @@ package com.avaje.ebeaninternal.server.deploy; -import java.io.IOException; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Map.Entry; - import com.avaje.ebean.EbeanServer; import com.avaje.ebean.Query; import com.avaje.ebean.Transaction; @@ -16,174 +10,175 @@ import com.avaje.ebean.bean.EntityBean; import com.avaje.ebean.common.BeanMap; import com.avaje.ebeaninternal.server.text.json.WriteJson; +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; + /** * Helper specifically for dealing with Maps. */ public final class BeanMapHelp implements BeanCollectionHelp { - private final BeanPropertyAssocMany many; - private final BeanDescriptor targetDescriptor; - private final BeanProperty beanProperty; - private BeanCollectionLoader loader; - //private final String mapKey; - - /** - * When created for a given query that will return a map. - */ - public BeanMapHelp(BeanDescriptor targetDescriptor, String mapKey) { - this(null, targetDescriptor, mapKey); - } + private final BeanPropertyAssocMany many; + private final BeanDescriptor targetDescriptor; + private final String propertyName; + private final BeanProperty beanProperty; + private BeanCollectionLoader loader; - public BeanMapHelp(BeanPropertyAssocMany many){ - this(many, many.getTargetDescriptor(), many.getMapKey()); - } - - /** - * When help is attached to a specific many property. - */ - private BeanMapHelp(BeanPropertyAssocMany many, BeanDescriptor targetDescriptor, String mapKey){ - this.many = many; - this.targetDescriptor = targetDescriptor; - //this.mapKey = mapKey; - this.beanProperty = targetDescriptor.getBeanProperty(mapKey); - } - - /** - * Return an iterator of the values. - */ - public Iterator getIterator(Object collection) { - return ((Map) collection).values().iterator(); + /** + * When created for a given query that will return a map. + */ + public BeanMapHelp(BeanDescriptor targetDescriptor, String mapKey) { + this.targetDescriptor = targetDescriptor; + this.beanProperty = targetDescriptor.getBeanProperty(mapKey); + this.many = null; + this.propertyName = null; + } + + /** + * When help is attached to a specific many property. + */ public BeanMapHelp(BeanPropertyAssocMany many) { + this.many = many; + this.targetDescriptor = many.getTargetDescriptor(); + this.propertyName = many.getName(); + this.beanProperty = targetDescriptor.getBeanProperty(many.getMapKey()); + } + + @Override + public void setLoader(BeanCollectionLoader loader) { + this.loader = loader; + } + + @Override + @SuppressWarnings("unchecked") + public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) { + + if (mapKey == null) { + mapKey = many.getMapKey(); } - - public void setLoader(BeanCollectionLoader loader){ - this.loader = loader; - } + BeanProperty beanProp = targetDescriptor.getBeanProperty(mapKey); - @SuppressWarnings("unchecked") - public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) { - - if(mapKey == null){ - mapKey = many.getMapKey(); - } - BeanProperty beanProp = targetDescriptor.getBeanProperty(mapKey); - - if (bc instanceof BeanMap){ - BeanMap bm = (BeanMap)bc; - Map actualMap = bm.getActualMap(); - if (actualMap == null){ - actualMap = new LinkedHashMap(); - bm.setActualMap(actualMap); - } - return new Adder(beanProp, actualMap); - - } else if (bc instanceof Map) { - return new Adder(beanProp, (Map)bc); - - } else { - throw new RuntimeException("Unhandled type "+bc); - } - } + if (bc instanceof BeanMap) { + BeanMap bm = (BeanMap) bc; + Map actualMap = bm.getActualMap(); + if (actualMap == null) { + actualMap = new LinkedHashMap(); + bm.setActualMap(actualMap); + } + return new Adder(beanProp, actualMap); - static class Adder implements BeanCollectionAdd { - - private final BeanProperty beanProperty; - - private final Map map; - - Adder(BeanProperty beanProperty, Map map) { - this.beanProperty = beanProperty; - this.map = map; - } - - public void addBean(EntityBean bean) { - Object keyValue = beanProperty.getValue(bean); - map.put(keyValue, bean); - } - } + } else { + throw new RuntimeException("Unhandled type " + bc); + } + } - @SuppressWarnings("rawtypes") - public Object createEmpty(boolean vanilla) { - if (vanilla) { - return new LinkedHashMap(); - } - BeanMap beanMap = new BeanMap(); - if (many != null) { - beanMap.setModifyListening(many.getModifyListenMode()); - } - return beanMap; - } - - public void add(BeanCollection collection, EntityBean bean) { + static class Adder implements BeanCollectionAdd { - Object keyValue = beanProperty.getValueIntercept(bean); + private final BeanProperty beanProperty; - ((BeanMap) collection).internalPut(keyValue, bean); - } + private final Map map; - @SuppressWarnings({ "unchecked", "rawtypes" }) - public BeanCollection createReference(EntityBean parentBean, String propertyName) { + Adder(BeanProperty beanProperty, Map map) { + this.beanProperty = beanProperty; + this.map = map; + } - BeanMap beanMap = new BeanMap(loader, parentBean, propertyName); + public void addBean(EntityBean bean) { + Object keyValue = beanProperty.getValue(bean); + map.put(keyValue, bean); + } + } + + @Override + public BeanCollection createEmptyNoParent() { + return new BeanMap(); + } + + @Override + @SuppressWarnings("rawtypes") + public BeanCollection createEmpty(EntityBean ownerBean) { + + BeanMap beanMap = new BeanMap(loader, ownerBean, propertyName); if (many != null) { beanMap.setModifyListening(many.getModifyListenMode()); } return beanMap; - } + } - public void refresh(EbeanServer server, Query query, Transaction t, EntityBean parentBean) { - BeanMap newBeanMap = (BeanMap) server.findMap(query, t); - refresh(newBeanMap, parentBean); - } - - public void refresh(BeanCollection bc, EntityBean parentBean) { + @Override + public void add(BeanCollection collection, EntityBean bean) { - BeanMap newBeanMap = (BeanMap) bc; - Map current = (Map) many.getValue(parentBean); + Object keyValue = beanProperty.getValueIntercept(bean); + ((BeanMap) collection).internalPut(keyValue, bean); + } - newBeanMap.setModifyListening(many.getModifyListenMode()); - if (current == null) { - // the currentMap is null? Not really expecting this... - many.setValue(parentBean, newBeanMap); + @Override + @SuppressWarnings({"unchecked", "rawtypes"}) + public BeanCollection createReference(EntityBean parentBean) { - } else if (current instanceof BeanMap) { - // normally this case, replace just the underlying list - BeanMap currentBeanMap = (BeanMap) current; - currentBeanMap.setActualMap(newBeanMap.getActualMap()); - currentBeanMap.setModifyListening(many.getModifyListenMode()); + BeanMap beanMap = new BeanMap(loader, parentBean, propertyName); + if (many != null) { + beanMap.setModifyListening(many.getModifyListenMode()); + } + return beanMap; + } - } else { - // replace the entire set - many.setValue(parentBean, newBeanMap); - } - } + @Override + public void refresh(EbeanServer server, Query query, Transaction t, EntityBean parentBean) { + BeanMap newBeanMap = (BeanMap) server.findMap(query, t); + refresh(newBeanMap, parentBean); + } - public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException { - - Map map; - if (collection instanceof BeanCollection){ - BeanMap bc = (BeanMap)collection; - if (!bc.isPopulated()){ - if (explicitInclude){ - // invoke lazy loading as collection - // is explicitly included in the output - bc.size(); - } else { - return; - } - } - map = bc.getActualMap(); + @Override + public void refresh(BeanCollection bc, EntityBean parentBean) { + + BeanMap newBeanMap = (BeanMap) bc; + Map current = (Map) many.getValue(parentBean); + + newBeanMap.setModifyListening(many.getModifyListenMode()); + if (current == null) { + // the currentMap is null? Not really expecting this... + many.setValue(parentBean, newBeanMap); + + } else if (current instanceof BeanMap) { + // normally this case, replace just the underlying list + BeanMap currentBeanMap = (BeanMap) current; + currentBeanMap.setActualMap(newBeanMap.getActualMap()); + currentBeanMap.setModifyListening(many.getModifyListenMode()); + + } else { + // replace the entire set + many.setValue(parentBean, newBeanMap); + } + } + + @Override + public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException { + + Map map; + if (collection instanceof BeanCollection) { + BeanMap bc = (BeanMap) collection; + if (!bc.isPopulated()) { + if (explicitInclude) { + // invoke lazy loading as collection + // is explicitly included in the output + bc.size(); } else { - map = (Map)collection; + return; } - - ctx.writeStartArray(name); - Iterator it = map.entrySet().iterator(); - while (it.hasNext()) { - Entry entry = (Entry)it.next(); - //FIXME: json write map key ... - targetDescriptor.jsonWrite(ctx, (EntityBean) entry.getValue()); - } - ctx.writeEndArray(); + } + map = bc.getActualMap(); + } else { + map = (Map) collection; } + ctx.writeStartArray(name); + for (Entry entry : map.entrySet()) { + //FIXME: json write map key ... + targetDescriptor.jsonWrite(ctx, (EntityBean) entry.getValue()); + } + ctx.writeEndArray(); + } + } 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 2d8d42c55..25b45de1c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java @@ -174,7 +174,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { public void addBeanToCollectionWithCreate(EntityBean parentBean, EntityBean detailBean) { BeanCollection bc = (BeanCollection) super.getValue(parentBean); if (bc == null) { - bc = (BeanCollection) help.createEmpty(false); + bc = help.createEmpty(parentBean); setValue(parentBean, bc); } help.add(bc, detailBean); @@ -185,6 +185,22 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { return val == null || (val instanceof BeanCollection) && ((BeanCollection) val).isEmptyAndUntouched(); } + /** + * Reset the many properties to be empty and ready for reloading. + *

+ * Used in bean refresh. + */ + public void resetMany(EntityBean bean) { + Object value = getValue(bean); + if (value == null) { + // not expecting this - set an empty reference + createReference(bean); + } else { + // reset the collection back to empty + ((BeanCollection)value).reset(bean, name); + } + } + @Override public Object getValue(EntityBean bean) { return super.getValue(bean); @@ -526,13 +542,13 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { public BeanCollection createReference(EntityBean parentBean) { - BeanCollection ref = help.createReference(parentBean, name); + BeanCollection ref = help.createReference(parentBean); setValue(parentBean, ref); return ref; } - public Object createEmpty(boolean vanilla) { - return help.createEmpty(vanilla); + public BeanCollection createEmpty(EntityBean parentBean) { + return help.createEmpty(parentBean); } public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonHelp.java index b97f579dc..f5f2e7ae5 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocManyJsonHelp.java @@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.deploy; import java.io.IOException; +import com.avaje.ebean.bean.BeanCollection; import com.avaje.ebean.bean.BeanCollectionAdd; import com.avaje.ebean.bean.EntityBean; import com.fasterxml.jackson.core.JsonParseException; @@ -30,7 +31,7 @@ public class BeanPropertyAssocManyJsonHelp { throw new JsonParseException("Unexpected token " + event + " - expecting start_array ", parser.getCurrentLocation()); } - Object collection = many.createEmpty(false); + BeanCollection collection = many.createEmpty(parentBean); BeanCollectionAdd add = many.getBeanCollectionAdd(collection, null); do { EntityBean detailBean = (EntityBean) many.targetDescriptor.jsonRead(parser, many.name); diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java index 2a463971b..6cc18b3ff 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java @@ -395,13 +395,6 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc { return getTargetDescriptor().createEntityBean(); } - /** - * Return an empty reference object. - */ - public Object createEmptyReference() { - return targetDescriptor.createEntityBean(); - } - @Override public Object elGetReference(EntityBean bean) { Object value = getValueIntercept(bean); @@ -465,7 +458,7 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc { } } - return (ExportedProperty[]) list.toArray(new ExportedProperty[list.size()]); + return list.toArray(new ExportedProperty[list.size()]); } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanSetHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanSetHelp.java index a73c4054f..b7c9ee7be 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanSetHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanSetHelp.java @@ -19,141 +19,131 @@ import com.avaje.ebeaninternal.server.text.json.WriteJson; * Helper specifically for dealing with Sets. */ public final class BeanSetHelp implements BeanCollectionHelp { - - private final BeanPropertyAssocMany many; - private final BeanDescriptor targetDescriptor; - private BeanCollectionLoader loader; - - /** - * When attached to a specific many property. - */ - public BeanSetHelp(BeanPropertyAssocMany many){ - this.many = many; - this.targetDescriptor = many.getTargetDescriptor(); - } - - /** - * For a query that returns a set. - */ - public BeanSetHelp(){ - this.many = null; - this.targetDescriptor = null; - } - - public void setLoader(BeanCollectionLoader loader){ - this.loader = loader; - } - - public Iterator getIterator(Object collection) { - return ((Set) collection).iterator(); - } - - public BeanCollectionAdd getBeanCollectionAdd(Object bc,String mapKey) { - if (bc instanceof BeanSet){ - BeanSet beanSet = (BeanSet)bc; - if (beanSet.getActualSet() == null){ - beanSet.setActualSet(new LinkedHashSet()); - } - return beanSet; - } else if (bc instanceof Set) { - return new VanillaAdd((Set)bc); - - } else { - throw new RuntimeException("Unhandled type "+bc); - } - } - - @SuppressWarnings("unchecked") - static class VanillaAdd implements BeanCollectionAdd { + private final BeanPropertyAssocMany many; + private final BeanDescriptor targetDescriptor; + private final String propertyName; + private BeanCollectionLoader loader; - @SuppressWarnings("rawtypes") - private final Set set; + /** + * When attached to a specific many property. + */ + public BeanSetHelp(BeanPropertyAssocMany many) { + this.many = many; + this.targetDescriptor = many.getTargetDescriptor(); + this.propertyName = many.getName(); + } - private VanillaAdd(Set set) { - this.set = set; - } + /** + * For a query that returns a set. + */ + public BeanSetHelp() { + this.many = null; + this.targetDescriptor = null; + this.propertyName = null; + } - public void addBean(EntityBean bean) { - set.add(bean); - } - } - - public void add(BeanCollection collection, EntityBean bean) { - collection.internalAdd(bean); - } - - public Object createEmpty(boolean vanilla) { - if (vanilla) { - return new LinkedHashSet(); + @Override + public void setLoader(BeanCollectionLoader loader) { + this.loader = loader; + } + + @Override + public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) { + if (bc instanceof BeanSet) { + BeanSet beanSet = (BeanSet) bc; + if (beanSet.getActualSet() == null) { + beanSet.setActualSet(new LinkedHashSet()); } - BeanSet beanSet = new BeanSet(); - if (many != null) { - beanSet.setModifyListening(many.getModifyListenMode()); - } - return beanSet; - } + return beanSet; - public BeanCollection createReference(EntityBean parentBean, String propertyName) { - - BeanSet beanSet = new BeanSet(loader, parentBean, propertyName); - beanSet.setModifyListening(many.getModifyListenMode()); - return beanSet; - } - - public void refresh(EbeanServer server, Query query, Transaction t, EntityBean parentBean) { - - BeanSet newBeanSet = (BeanSet)server.findSet(query, t); - refresh(newBeanSet, parentBean); - } - - public void refresh(BeanCollection bc, EntityBean parentBean) { - - BeanSet newBeanSet = (BeanSet)bc; - - Set current = (Set)many.getValue(parentBean); - - newBeanSet.setModifyListening(many.getModifyListenMode()); - if (current == null){ - // the currentList is null? Not really expecting this... - many.setValue(parentBean,newBeanSet); - - } else if (current instanceof BeanSet) { - // normally this case, replace just the underlying list - BeanSet currentBeanSet = (BeanSet)current; - currentBeanSet.setActualSet(newBeanSet.getActualSet()); - currentBeanSet.setModifyListening(many.getModifyListenMode()); - - } else { - // replace the entire set - many.setValue(parentBean, newBeanSet); - } - } - - public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException { - - Set set; - if (collection instanceof BeanCollection){ - BeanSet bc = (BeanSet)collection; - if (!bc.isPopulated()){ - if (explicitInclude){ - // invoke lazy loading as collection - // is explicitly included in the output - bc.size(); - } else { - return; - } - } - set = bc.getActualSet(); - } else { - set = (Set)collection; - } - - ctx.writeStartArray(name); - Iterator it = set.iterator(); - while (it.hasNext()) { - targetDescriptor.jsonWrite(ctx, (EntityBean)it.next()); - } - ctx.writeEndArray(); + } else { + throw new RuntimeException("Unhandled type " + bc); } + } + + public void add(BeanCollection collection, EntityBean bean) { + collection.internalAdd(bean); + } + + @Override + public BeanCollection createEmptyNoParent() { + return new BeanSet(); + } + + @Override + public BeanCollection createEmpty(EntityBean ownerBean) { + BeanSet beanSet = new BeanSet(loader, ownerBean, propertyName); + if (many != null) { + beanSet.setModifyListening(many.getModifyListenMode()); + } + return beanSet; + } + + @Override + public BeanCollection createReference(EntityBean parentBean) { + + BeanSet beanSet = new BeanSet(loader, parentBean, propertyName); + beanSet.setModifyListening(many.getModifyListenMode()); + return beanSet; + } + + @Override + public void refresh(EbeanServer server, Query query, Transaction t, EntityBean parentBean) { + + BeanSet newBeanSet = (BeanSet) server.findSet(query, t); + refresh(newBeanSet, parentBean); + } + + @Override + public void refresh(BeanCollection bc, EntityBean parentBean) { + + BeanSet newBeanSet = (BeanSet) bc; + + Set current = (Set) many.getValue(parentBean); + + newBeanSet.setModifyListening(many.getModifyListenMode()); + if (current == null) { + // the currentList is null? Not really expecting this... + many.setValue(parentBean, newBeanSet); + + } else if (current instanceof BeanSet) { + // normally this case, replace just the underlying list + BeanSet currentBeanSet = (BeanSet) current; + currentBeanSet.setActualSet(newBeanSet.getActualSet()); + currentBeanSet.setModifyListening(many.getModifyListenMode()); + + } else { + // replace the entire set + many.setValue(parentBean, newBeanSet); + } + } + + @Override + public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException { + + Set set; + if (collection instanceof BeanCollection) { + BeanSet bc = (BeanSet) collection; + if (!bc.isPopulated()) { + if (explicitInclude) { + // invoke lazy loading as collection + // is explicitly included in the output + bc.size(); + } else { + return; + } + } + set = bc.getActualSet(); + } else { + set = (Set) collection; + } + + ctx.writeStartArray(name); + Iterator it = set.iterator(); + while (it.hasNext()) { + targetDescriptor.jsonWrite(ctx, (EntityBean) it.next()); + } + ctx.writeEndArray(); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/DRawSqlSelect.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/DRawSqlSelect.java index 8d81e7680..6465edade 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/DRawSqlSelect.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/DRawSqlSelect.java @@ -122,10 +122,6 @@ public class DRawSqlSelect { */ private SqlTree buildSqlTree(BeanDescriptor desc){ - - SqlTree sqlTree = new SqlTree(); - sqlTree.setSummary(desc.getName()); - LinkedHashSet includedProps = new LinkedHashSet(); SqlTreeProperties selectProps = new SqlTreeProperties(); @@ -157,9 +153,8 @@ public class DRawSqlSelect { } SqlTreeNode sqlRoot = new SqlTreeNodeRoot(desc, selectProps, null, withId); - sqlTree.setRootNode(sqlRoot); - return sqlTree; + return new SqlTree(desc.getName(), sqlRoot); } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java index 68444a167..e1341b3a8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java @@ -1,29 +1,8 @@ package com.avaje.ebeaninternal.server.query; -import java.lang.ref.WeakReference; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import javax.persistence.PersistenceException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.avaje.ebean.QueryIterator; -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebean.bean.BeanCollectionAdd; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.bean.NodeUsageCollector; -import com.avaje.ebean.bean.NodeUsageListener; -import com.avaje.ebean.bean.ObjectGraphNode; -import com.avaje.ebean.bean.PersistenceContext; +import com.avaje.ebean.bean.*; import com.avaje.ebean.config.GlobalProperties; -import com.avaje.ebeaninternal.api.LoadContext; import com.avaje.ebeaninternal.api.SpiExpressionList; import com.avaje.ebeaninternal.api.SpiQuery; import com.avaje.ebeaninternal.api.SpiQuery.Mode; @@ -32,16 +11,23 @@ import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; import com.avaje.ebeaninternal.server.core.Message; import com.avaje.ebeaninternal.server.core.OrmQueryRequest; import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest; -import com.avaje.ebeaninternal.server.deploy.BeanCollectionHelp; -import com.avaje.ebeaninternal.server.deploy.BeanCollectionHelpFactory; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.deploy.DbReadContext; +import com.avaje.ebeaninternal.server.deploy.*; import com.avaje.ebeaninternal.server.el.ElPropertyValue; import com.avaje.ebeaninternal.server.lib.util.StringHelper; import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties; import com.avaje.ebeaninternal.server.type.DataBind; import com.avaje.ebeaninternal.server.type.DataReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.persistence.PersistenceException; +import java.lang.ref.WeakReference; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Map; +import java.util.concurrent.TimeUnit; /** * An object that represents a SqlSelect statement. @@ -82,7 +68,7 @@ public class CQuery implements DbReadContext, CancelableQuery { /** * Flag set when 'master' bean changed. */ - private boolean loadedBeanChanged; + private boolean loadedBeanChanged; /** * The 'master' bean just loaded. @@ -220,6 +206,9 @@ public class CQuery implements DbReadContext, CancelableQuery { private long startNano; private long executionTimeMicros; + + private BeanCollectionAdd currentDetailAdd; + /** * Create the Sql select based on the request. */ @@ -253,7 +242,7 @@ public class CQuery implements DbReadContext, CancelableQuery { // get filter to put on the collection for reuse with refresh String manyPropertyName = sqlTree.getManyPropertyName(); OrmQueryProperties chunk = query.getDetail().getChunk(manyPropertyName, false); - this.filterMany = chunk.getFilterMany(); + this.filterMany = (chunk == null) ? null : chunk.getFilterMany(); } else { this.filterMany = null; } @@ -266,7 +255,7 @@ public class CQuery implements DbReadContext, CancelableQuery { this.predicates = predicates; this.maxRowsLimit = query.getMaxRows() > 0 ? query.getMaxRows() : GLOBAL_ROW_LIMIT; this.help = createHelp(request); - this.collection = (BeanCollection) (help != null ? help.createEmpty(false) : null); + this.collection = (help != null ? help.createEmptyNoParent() : null); } private BeanCollectionHelp createHelp(OrmQueryRequest request) { @@ -306,10 +295,6 @@ public class CQuery implements DbReadContext, CancelableQuery { return predicates; } - public LoadContext getGraphContext() { - return request.getGraphContext(); - } - public SpiOrmQueryRequest getQueryRequest() { return request; } @@ -470,12 +455,10 @@ public class CQuery implements DbReadContext, CancelableQuery { public EntityBean getLoadedBean() { if (manyIncluded) { if (prevDetailCollection instanceof BeanCollection) { - ((BeanCollection) prevDetailCollection).setModifyListening(manyProperty - .getModifyListenMode()); + ((BeanCollection) prevDetailCollection).setModifyListening(manyProperty.getModifyListenMode()); } else if (currentDetailCollection instanceof BeanCollection) { - ((BeanCollection) currentDetailCollection).setModifyListening(manyProperty - .getModifyListenMode()); + ((BeanCollection) currentDetailCollection).setModifyListening(manyProperty.getModifyListenMode()); } } @@ -581,8 +564,6 @@ public class CQuery implements DbReadContext, CancelableQuery { return false; } - private BeanCollectionAdd currentDetailAdd; - private void createNewDetailCollection() { prevDetailCollection = currentDetailCollection; if (queryMode.equals(Mode.LAZYLOAD_MANY)) { @@ -590,7 +571,7 @@ public class CQuery implements DbReadContext, CancelableQuery { currentDetailCollection = manyPropertyEl.elGetValue(loadedBean); } else { // create a new collection to populate and assign to the bean - currentDetailCollection = manyProperty.createEmpty(false); + currentDetailCollection = manyProperty.createEmpty(loadedBean); manyPropertyEl.elSetValue(loadedBean, currentDetailCollection, false); } @@ -719,14 +700,6 @@ public class CQuery implements DbReadContext, CancelableQuery { return sqlTree.getSummary(); } - /** - * Return the SqlSelectChain. This is the flattened structure that represents - * this query. - */ - public SqlTree getSqlTree() { - return sqlTree; - } - public String getBindLog() { return bindLog; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTree.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTree.java index 0065904a5..4cff655b6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTree.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTree.java @@ -13,40 +13,71 @@ import com.avaje.ebeaninternal.server.el.ElPropertyValue; */ public class SqlTree { - private SqlTreeNode rootNode; + private final SqlTreeNode rootNode; /** * Property if resultSet contains master and detail rows. */ - private BeanPropertyAssocMany manyProperty; - private String manyPropertyName; - private ElPropertyValue manyPropEl; + private final BeanPropertyAssocMany manyProperty; - private Set includes; + private final String manyPropertyName; + + private final ElPropertyValue manyPropEl; + + private final Set includes; /** * Summary of the select being generated. */ - private String summary; + private final String summary; - private String selectSql; + private final String selectSql; - private String fromSql; + private final String fromSql; /** * Encrypted Properties require additional binding. */ - private BeanProperty[] encryptedProps; + private final BeanProperty[] encryptedProps; /** * Where clause for inheritance. */ - private String inheritanceWhereSql; + private final String inheritanceWhereSql; /** * Create the SqlSelectClause. */ - public SqlTree() { + public SqlTree(String summary, SqlTreeNode rootNode, String selectSql, String fromSql, String inheritanceWhereSql, + BeanProperty[] encryptedProps, BeanPropertyAssocMany manyProperty, String manyPropertyName, + ElPropertyValue manyPropEl, Set includes) { + + this.summary = summary; + this.rootNode = rootNode; + this.selectSql = selectSql; + this.fromSql = fromSql; + this.inheritanceWhereSql = inheritanceWhereSql; + this.encryptedProps = encryptedProps; + this.manyProperty = manyProperty; + this.manyPropertyName = manyPropertyName; + this.manyPropEl = manyPropEl; + this.includes = includes; + } + + /** + * Construct for RawSql. + */ + public SqlTree(String summary, SqlTreeNode rootNode) { + this.summary = summary; + this.rootNode = rootNode; + this.selectSql = null; + this.fromSql = null; + this.inheritanceWhereSql = null; + this.encryptedProps = null; + this.manyProperty = null; + this.manyPropertyName = null; + this.manyPropEl = null; + this.includes = null; } public List buildSelectExpressionChain() { @@ -62,23 +93,6 @@ public class SqlTree { return includes; } - /** - * Set the association includes (Ones and Many's). - */ - public void setIncludes(Set includes) { - this.includes = includes; - } - - /** - * Set the manyProperty used for this query. - */ - public void setManyProperty(BeanPropertyAssocMany manyProperty, String manyPropertyName, - ElPropertyValue manyPropEl) { - this.manyProperty = manyProperty; - this.manyPropertyName = manyPropertyName; - this.manyPropEl = manyPropEl; - } - /** * Return the String for the actual SQL. */ @@ -86,21 +100,10 @@ public class SqlTree { return selectSql; } - /** - * Set the select sql clause. - */ - public void setSelectSql(String selectSql) { - this.selectSql = selectSql; - } - public String getFromSql() { return fromSql; } - public void setFromSql(String fromSql) { - this.fromSql = fromSql; - } - /** * Return the where clause for inheritance. */ @@ -108,20 +111,6 @@ public class SqlTree { return inheritanceWhereSql; } - /** - * Set where clause(s) for inheritance. - */ - public void setInheritanceWhereSql(String whereSql) { - this.inheritanceWhereSql = whereSql; - } - - /** - * Set the summary description of the query. - */ - public void setSummary(String summary) { - this.summary = summary; - } - /** * Return a summary of the select clause. */ @@ -133,10 +122,6 @@ public class SqlTree { return rootNode; } - public void setRootNode(SqlTreeNode rootNode) { - this.rootNode = rootNode; - } - /** * Return the property that is associated with the many. There can only be one * per SqlSelect. This can be null. @@ -164,7 +149,4 @@ public class SqlTree { return encryptedProps; } - public void setEncryptedProps(BeanProperty[] encryptedProps) { - this.encryptedProps = encryptedProps; - } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java index 92415e2a2..c70070d49 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java @@ -66,6 +66,8 @@ public class SqlTreeBuilder { private final boolean rawSql; + private SqlTreeNode rootNode; + /** * Construct for RawSql query. */ @@ -111,34 +113,33 @@ public class SqlTreeBuilder { */ public SqlTree build() { - SqlTree sqlTree = new SqlTree(); - summary.append(desc.getName()); // build the appropriate chain of SelectAdapter's - buildRoot(desc, sqlTree); + buildRoot(desc); // build the actual String - SqlTreeNode rootNode = sqlTree.getRootNode(); - + String selectSql = null; + String fromSql = null; + String inheritanceWhereSql = null; + BeanProperty[] encryptedProps = null; if (!rawSql) { - sqlTree.setSelectSql(buildSelectClause(rootNode)); - sqlTree.setFromSql(buildFromClause(rootNode)); - sqlTree.setInheritanceWhereSql(buildWhereClause(rootNode)); - sqlTree.setEncryptedProps(ctx.getEncryptedProps()); + selectSql = buildSelectClause(); + fromSql = buildFromClause(); + inheritanceWhereSql = buildWhereClause(); + encryptedProps = ctx.getEncryptedProps(); } - sqlTree.setIncludes(queryDetail.getIncludes()); - sqlTree.setSummary(summary.toString()); + ElPropertyValue manyPropEl = null; if (manyPropertyName != null) { - ElPropertyValue manyPropEl = desc.getElGetValue(manyPropertyName); - sqlTree.setManyProperty(manyProperty, manyPropertyName, manyPropEl); + manyPropEl = desc.getElGetValue(manyPropertyName); } - return sqlTree; + return new SqlTree(summary.toString(), rootNode, selectSql, fromSql, inheritanceWhereSql, encryptedProps, + manyProperty, manyPropertyName, manyPropEl, queryDetail.getIncludes()); } - private String buildSelectClause(SqlTreeNode rootNode) { + private String buildSelectClause() { if (rawSql) { return "Not Used"; @@ -155,7 +156,7 @@ public class SqlTreeBuilder { return selectSql; } - private String buildWhereClause(SqlTreeNode rootNode) { + private String buildWhereClause() { if (rawSql) { return "Not Used"; @@ -164,7 +165,7 @@ public class SqlTreeBuilder { return ctx.getContent(); } - private String buildFromClause(SqlTreeNode rootNode) { + private String buildFromClause() { if (rawSql) { return "Not Used"; @@ -173,10 +174,9 @@ public class SqlTreeBuilder { return ctx.getContent(); } - private void buildRoot(BeanDescriptor desc, SqlTree sqlTree) { + private void buildRoot(BeanDescriptor desc) { - SqlTreeNode selectRoot = buildSelectChain(null, null, desc, null); - sqlTree.setRootNode(selectRoot); + rootNode = buildSelectChain(null, null, desc, null); if (!rawSql) { alias.addJoin(queryDetail.getIncludes(), desc); diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/DefaultJsonValueAdapter.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/DefaultJsonValueAdapter.java deleted file mode 100644 index 85fc55c78..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/DefaultJsonValueAdapter.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.avaje.ebeaninternal.server.text.json; - -import java.sql.Date; -import java.sql.Timestamp; -import java.text.SimpleDateFormat; -import java.util.TimeZone; - -public class DefaultJsonValueAdapter {//implements JsonValueAdapter { - - private final SimpleDateFormat dateTimeProto; - - public DefaultJsonValueAdapter(String dateTimeFormat) { - this.dateTimeProto = new SimpleDateFormat(dateTimeFormat); - this.dateTimeProto.setTimeZone(TimeZone.getTimeZone("UTC")); - } - - public DefaultJsonValueAdapter() { - this("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); - } - - private SimpleDateFormat dtFormat() { - return (SimpleDateFormat) dateTimeProto.clone(); - } - - public String jsonFromDate(Date date) { - return "\"" + date.toString() + "\""; - } - - public String jsonFromTimestamp(Timestamp date) { - return "\"" + dtFormat().format(date) + "\""; - } - - public Date jsonToDate(String jsonDate) { - try { - long utc = Long.parseLong(jsonDate); - return new java.sql.Date(utc); - } catch (NumberFormatException ex) { - return Date.valueOf(jsonDate); - } - } - - public Timestamp jsonToTimestamp(String jsonDateTime) { - try { - long utc = Long.parseLong(jsonDateTime); - return new Timestamp(utc); - } catch (NumberFormatException ex) { - try { - java.util.Date d = dtFormat().parse(jsonDateTime); - return new Timestamp(d.getTime()); - } catch (Exception e) { - String m = "Error parsing Datetime[" + jsonDateTime + "]"; - throw new RuntimeException(m, e); - } - } - } - -} diff --git a/src/test/java/com/avaje/tests/batchload/TestSecondQueryNoRows.java b/src/test/java/com/avaje/tests/batchload/TestSecondQueryNoRows.java index c9873bd22..4f2177615 100644 --- a/src/test/java/com/avaje/tests/batchload/TestSecondQueryNoRows.java +++ b/src/test/java/com/avaje/tests/batchload/TestSecondQueryNoRows.java @@ -1,12 +1,13 @@ package com.avaje.tests.batchload; -import org.junit.Assert; -import org.junit.Test; - import com.avaje.ebean.BaseTestCase; import com.avaje.ebean.Ebean; import com.avaje.ebean.FetchConfig; import com.avaje.tests.model.basic.Customer; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; public class TestSecondQueryNoRows extends BaseTestCase { @@ -18,9 +19,13 @@ public class TestSecondQueryNoRows extends BaseTestCase { Ebean.save(cnew); - Customer c = Ebean.find(Customer.class).setAutofetch(false).setId(cnew.getId()) - .fetch("contacts", new FetchConfig().query()).findUnique(); + Customer c = Ebean.find(Customer.class) + .setAutofetch(false) + .setId(cnew.getId()) + .fetch("contacts", new FetchConfig().query()) + .findUnique(); - Assert.assertNotNull(c); + assertNotNull(c); + assertEquals(0, c.getContacts().size()); } } diff --git a/src/test/java/com/avaje/tests/model/basic/DMLTest.java b/src/test/java/com/avaje/tests/model/basic/DMLTest.java deleted file mode 100644 index 54823d234..000000000 --- a/src/test/java/com/avaje/tests/model/basic/DMLTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.avaje.tests.model.basic; - -import java.util.ArrayList; -import java.util.List; - -import junit.framework.Assert; -import junit.framework.TestCase; - -import com.avaje.ebean.Ebean; - -public class DMLTest extends TestCase { - - public DMLTest() { - } - - public static void main(String[] args) { - DMLTest dmlTest = new DMLTest(); - dmlTest.deleteAll(); - dmlTest.insertData(); - } - - public void test() { - Assert.assertTrue(true); - } - - public void deleteAll() { - Ebean.beginTransaction(); - try { - Ebean.createUpdate(Phone.class, "delete from phone").execute(); - - Ebean.createUpdate(Person.class, "delete from person").execute(); - - Ebean.commitTransaction(); - } finally { - Ebean.endTransaction(); - } - } - - private void insertData() { - Person person = new Person(); - person.setSurname("Kosinov"); - person.setName("Yaroslav"); - Phone phone = new Phone(); - phone.setPhoneNumber("5244011"); - List phones = new ArrayList(); - phones.add(phone); - person.setPhones(phones); - Ebean.save(person); - - person = new Person(); - person.setSurname("Kosinov"); - person.setName("Gennady"); - phone = new Phone(); - phone.setPhoneNumber("5712658"); - phone.setPerson(person); - Ebean.save(phone); - - } - -} diff --git a/src/test/java/com/avaje/tests/query/TestQueryFilterMany.java b/src/test/java/com/avaje/tests/query/TestQueryFilterMany.java index a6c445586..be4593553 100644 --- a/src/test/java/com/avaje/tests/query/TestQueryFilterMany.java +++ b/src/test/java/com/avaje/tests/query/TestQueryFilterMany.java @@ -1,16 +1,18 @@ package com.avaje.tests.query; -import java.util.List; - -import org.junit.Test; - import com.avaje.ebean.BaseTestCase; import com.avaje.ebean.Ebean; import com.avaje.ebean.FetchConfig; -import com.avaje.ebean.Query; import com.avaje.tests.model.basic.Customer; import com.avaje.tests.model.basic.Order; import com.avaje.tests.model.basic.ResetBasicData; +import org.avaje.ebeantest.LoggedSqlCollector; +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; public class TestQueryFilterMany extends BaseTestCase { @@ -19,20 +21,23 @@ public class TestQueryFilterMany extends BaseTestCase { ResetBasicData.reset(); - Query query = Ebean.find(Customer.class).fetch("orders", new FetchConfig().lazy()) - .where().ilike("name", "Rob%").filterMany("orders").eq("status", Order.Status.NEW).where() - .gt("id", 0).query(); + LoggedSqlCollector.start(); - // query.filterMany("orders").eq("status", Order.Status.NEW); + Customer customer = Ebean.find(Customer.class) + .fetch("orders", new FetchConfig().lazy()) + .filterMany("orders").eq("status", Order.Status.NEW) + .where().ieq("name", "Rob") + .order().asc("id").setMaxRows(1) + .findList().get(0); - List list = query.findList(); - for (Customer customer : list) { - customer.getOrders().size(); - } + customer.getOrders().size(); - Customer c0 = list.get(0); - System.out.println("......... refreshMany ..."); - Ebean.refreshMany(c0, "orders"); + List sqlList = LoggedSqlCollector.stop(); + assertEquals(2, sqlList.size()); + assertTrue(sqlList.get(1).contains("status = ?")); + + // Currently this does not include the query filter + Ebean.refreshMany(customer, "orders"); } } diff --git a/src/test/java/com/avaje/tests/refresh/TestRefreshWithMany.java b/src/test/java/com/avaje/tests/refresh/TestRefreshWithMany.java new file mode 100644 index 000000000..7df30826b --- /dev/null +++ b/src/test/java/com/avaje/tests/refresh/TestRefreshWithMany.java @@ -0,0 +1,88 @@ +package com.avaje.tests.refresh; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.SqlRow; +import com.avaje.tests.model.basic.Contact; +import com.avaje.tests.model.basic.Customer; +import com.avaje.tests.model.basic.ResetBasicData; +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class TestRefreshWithMany extends BaseTestCase { + + @Test + public void test() { + + ResetBasicData.reset(); + + Customer customer = ResetBasicData.createCustomer("refresher", "22 refresh set", "23 fresh", 9); + + Ebean.save(customer); + + assertEquals(3, customer.getContacts().size()); + + int rc = Ebean.createSqlUpdate("update o_customer set name = :name where id = :id") + .setParameter("name", "ref-modified") + .setParameter("id", customer.getId()) + .execute(); + + int rc2 = Ebean.createSqlUpdate("update contact set first_name = first_name || '-mod' where customer_id = :id") + .setParameter("id", customer.getId()) + .execute(); + + assertEquals(1, rc); + assertEquals(3, rc2); + + System.out.println("-------------------------- prior"); + Ebean.refresh(customer); + System.out.println("-------------------------- after"); + + assertEquals("ref-modified", customer.getName()); + assertEquals(3, customer.getContacts().size()); + assertEquals(true, customer.getContacts().get(0).getFirstName().endsWith("-mod")); + + + // now try again when the bean is fully loaded + Customer customer1 = Ebean.find(Customer.class) + .fetch("contacts") + .setId(customer.getId()) + .findUnique(); + + + int rcb1 = Ebean.createSqlUpdate("update o_customer set name = :name where id = :id") + .setParameter("name", "ref-modified-again") + .setParameter("id", customer.getId()) + .execute(); + + int rcb2 = Ebean.createSqlUpdate("update contact set first_name = :first, last_name='foo' where customer_id = :id") + .setParameter("id", customer.getId()) + .setParameter("first","-alt") + .execute(); + + List sqlRows = Ebean.createSqlQuery("select id, first_name, last_name from contact").findList(); + for (SqlRow sqlRow : sqlRows) { + System.out.println("row: "+sqlRow.get("id")+" "+sqlRow.get("first_name")+" "+sqlRow.get("last_name")); + } + + assertEquals(1, rcb1); + assertEquals(3, rcb2); + + System.out.println("-------------------------- prior2"); + Ebean.refresh(customer1); + System.out.println("-------------------------- after2"); + + assertEquals("ref-modified-again", customer1.getName()); + System.out.println("-------------------------- after2a"); + assertEquals(3, customer1.getContacts().size()); + System.out.println("-------------------------- after2b"); + for (Contact contact : customer1.getContacts()) { + System.out.println("hello "+contact.getId()+" "+contact.getFirstName()+" "+contact.getLastName()); + } + assertEquals(true, customer1.getContacts().get(0).getFirstName().endsWith("-alt")); + + } +} diff --git a/src/test/java/com/avaje/tests/text/csv/TestCsvReader.java b/src/test/java/com/avaje/tests/text/csv/TestCsvReader.java index 923cd4c1b..954f54671 100644 --- a/src/test/java/com/avaje/tests/text/csv/TestCsvReader.java +++ b/src/test/java/com/avaje/tests/text/csv/TestCsvReader.java @@ -2,6 +2,7 @@ package com.avaje.tests.text.csv; import java.io.File; import java.io.FileReader; +import java.net.URL; import java.util.Locale; import org.junit.Test; @@ -20,7 +21,8 @@ public class TestCsvReader extends BaseTestCase { ResetBasicData.reset(); try { - File f = new File("src/test/resources/test1.csv"); + URL resource = TestCsvReaderWithCallback.class.getResource("/test1.csv"); + File f = new File(resource.getFile()); FileReader reader = new FileReader(f); diff --git a/src/test/java/com/avaje/tests/text/csv/TestCsvReaderWithCallback.java b/src/test/java/com/avaje/tests/text/csv/TestCsvReaderWithCallback.java index c14c6cf97..32e797c6c 100644 --- a/src/test/java/com/avaje/tests/text/csv/TestCsvReaderWithCallback.java +++ b/src/test/java/com/avaje/tests/text/csv/TestCsvReaderWithCallback.java @@ -2,6 +2,7 @@ package com.avaje.tests.text.csv; import java.io.File; import java.io.FileReader; +import java.net.URL; import java.util.Locale; import org.junit.Test; @@ -21,7 +22,8 @@ public class TestCsvReaderWithCallback extends BaseTestCase { ResetBasicData.reset(); - File f = new File("src/test/resources/test1.csv"); + URL resource = TestCsvReaderWithCallback.class.getResource("/test1.csv"); + File f = new File(resource.getFile()); FileReader reader = new FileReader(f); diff --git a/src/test/java/com/avaje/tests/text/json/TestTextJsonBeanReadVisitor.java.orig b/src/test/java/com/avaje/tests/text/json/TestTextJsonBeanReadVisitor.java.orig deleted file mode 100644 index 2a68b02b9..000000000 --- a/src/test/java/com/avaje/tests/text/json/TestTextJsonBeanReadVisitor.java.orig +++ /dev/null @@ -1,50 +0,0 @@ -package com.avaje.tests.text.json; - -import java.io.StringReader; -import java.util.List; -<<<<<<< HEAD -import java.util.Map; -======= ->>>>>>> json-refactor - -import org.junit.Assert; -import org.junit.Test; - -import com.avaje.ebean.BaseTestCase; -import com.avaje.ebean.Ebean; -import com.avaje.ebean.text.json.JsonContext; -import com.avaje.tests.model.basic.Customer; -import com.avaje.tests.model.basic.ResetBasicData; - -public class TestTextJsonBeanReadVisitor extends BaseTestCase { - - @Test - public void test() { - - ResetBasicData.reset(); - - List list = Ebean.find(Customer.class) - .select("id, name, status, shippingAddress") - .fetch("billingAddress", "line1, city") - .fetch("billingAddress.country", "*") - .fetch("contacts", "firstName,email") - .order().desc("id").findList(); - - JsonContext json = Ebean.createJsonContext(); - - - String s = json.toJsonString(list); - System.out.println(s); - - List mList = json.toList(Customer.class, s); - System.out.println("VIA STRING: " + mList); - - StringReader reader = new StringReader(s); - List mList2 = json.toList(Customer.class, reader); - System.out.println("VIA READER: " + mList2); - - Assert.assertEquals(mList.size(), mList2.size()); - } - - -}