From eaee3a9c0224f27443071c602b458a8e0bbea345 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Wed, 12 Nov 2014 22:10:28 +1300 Subject: [PATCH] JSON refactor - tidy up --- src/main/java/com/avaje/ebean/Ebean.java | 1 + .../java/com/avaje/ebean/EbeanServer.java | 1 + .../com/avaje/ebean/text/TextException.java | 2 +- .../avaje/ebean/text/json/JsonContext.java | 18 +- .../ebean/text/json/JsonWriteOptions.java | 141 +- .../avaje/ebean/text/json/package-info.java | 29 +- .../server/deploy/BeanDescriptorJsonHelp.java | 12 +- .../server/deploy/BeanPropertyAssocMany.java | 1347 ++++++++--------- .../server/text/json/DJsonContext.java | 103 +- .../server/text/json/WriteJson.java | 38 +- .../server/type/ScalarTypeByte.java | 8 +- .../text/json/JsonWriteOptionsTests.java | 39 +- .../server/text/json/WriteJsonTest.java | 57 + .../avaje/tests/ddd/iud/TestDPersonEl.java | 4 +- .../model/selfref/TestTextJsonSelfRef.java | 2 +- .../text/json/TestJsonBeanWithTimeZone.java | 4 +- .../TestJsonInheritanceDiscriminator.java | 12 +- .../avaje/tests/text/json/TestJsonMap.java | 60 +- .../avaje/tests/text/json/TestJsonSimple.java | 4 +- .../json/TestJsonSomeEnumWithToString.java | 4 +- .../json/TestTextJsonBeanReadVisitor.java | 4 +- .../text/json/TestTextJsonCompoundType.java | 4 +- .../text/json/TestTextJsonInheritance.java | 4 +- .../text/json/TestTextJsonInsertUpdate.java | 4 +- .../text/json/TestTextJsonInvokeLazy.java | 7 +- .../json/TestTextJsonReadManyLazyLoad.java | 12 +- .../text/json/TestTextJsonReferenceBean.java | 9 +- .../tests/text/json/TestTextJsonSimple.java | 24 +- .../text/json/TestTextJsonSuperSimple.java | 4 +- .../text/json/TestTextJsonUpdateCascade.java | 6 +- .../tests/update/TestJsonStatelessUpdate.java | 4 +- 31 files changed, 935 insertions(+), 1033 deletions(-) create mode 100644 src/test/java/com/avaje/ebeaninternal/server/text/json/WriteJsonTest.java diff --git a/src/main/java/com/avaje/ebean/Ebean.java b/src/main/java/com/avaje/ebean/Ebean.java index 2d124557b..8acf3f719 100644 --- a/src/main/java/com/avaje/ebean/Ebean.java +++ b/src/main/java/com/avaje/ebean/Ebean.java @@ -1391,6 +1391,7 @@ public final class Ebean { /** * Return the JsonContext for reading/writing JSON. + * @deprecated Please use #json instead. */ public static JsonContext createJsonContext() { return json(); diff --git a/src/main/java/com/avaje/ebean/EbeanServer.java b/src/main/java/com/avaje/ebean/EbeanServer.java index 3d7e38002..64bdcbed2 100644 --- a/src/main/java/com/avaje/ebean/EbeanServer.java +++ b/src/main/java/com/avaje/ebean/EbeanServer.java @@ -1120,6 +1120,7 @@ public interface EbeanServer { /** * Return the JsonContext for reading/writing JSON. + * @deprecated Please use #json instead. */ public JsonContext createJsonContext(); diff --git a/src/main/java/com/avaje/ebean/text/TextException.java b/src/main/java/com/avaje/ebean/text/TextException.java index dbaf83cb4..68a65649e 100644 --- a/src/main/java/com/avaje/ebean/text/TextException.java +++ b/src/main/java/com/avaje/ebean/text/TextException.java @@ -1,7 +1,7 @@ package com.avaje.ebean.text; /** - * An exception occured typically in processing CSV, JSON or XML. + * An exception occurred typically in processing CSV, JSON or XML. * * @author rbygrave */ diff --git a/src/main/java/com/avaje/ebean/text/json/JsonContext.java b/src/main/java/com/avaje/ebean/text/json/JsonContext.java index ab5b182f0..38b095ae5 100644 --- a/src/main/java/com/avaje/ebean/text/json/JsonContext.java +++ b/src/main/java/com/avaje/ebean/text/json/JsonContext.java @@ -11,8 +11,6 @@ import com.fasterxml.jackson.core.JsonParser; /** * Converts objects to and from JSON format. - * - * @author rbygrave */ public interface JsonContext { @@ -53,39 +51,39 @@ public interface JsonContext { * Write the bean or collection in JSON format to the writer with default * options. * - * @param o + * @param value * the bean or collection of beans to write * @param writer * used to write the json output to */ - public void toJsonWriter(Object o, Writer writer) throws IOException; + public void toJson(Object value, Writer writer) throws IOException; /** * With additional options to specify JsonValueAdapter and * JsonWriteBeanVisitor's. * - * @param o + * @param value * the bean or collection of beans to write * @param writer * used to write the json output to * @param options * additional options to control the JSON output */ - public void toJsonWriter(Object o, Writer writer, JsonWriteOptions options) throws IOException; + public void toJson(Object value, Writer writer, JsonWriteOptions options) throws IOException; /** * Convert a bean or collection to json string using default options. */ - public String toJsonString(Object o) throws IOException; + public String toJson(Object value) throws IOException; /** * Convert a bean or collection to json string. */ - public String toJsonString(Object o, JsonWriteOptions options) throws IOException; + public String toJson(Object value, JsonWriteOptions options) throws IOException; /** - * Return true if the type is known as an Entity or Xml type or a List Set or - * Map of known bean types. + * Return true if the type is known as an Entity bean or a List Set or + * Map of entity beans. */ public boolean isSupportedType(Type genericType); diff --git a/src/main/java/com/avaje/ebean/text/json/JsonWriteOptions.java b/src/main/java/com/avaje/ebean/text/json/JsonWriteOptions.java index d245725e0..f1c69023a 100644 --- a/src/main/java/com/avaje/ebean/text/json/JsonWriteOptions.java +++ b/src/main/java/com/avaje/ebean/text/json/JsonWriteOptions.java @@ -1,72 +1,23 @@ package com.avaje.ebean.text.json; -import java.util.LinkedHashSet; -import java.util.Set; - import com.avaje.ebean.text.PathProperties; /** * Provides options for customising the JSON write process. *

- * You can optionally provide a custom JsonValueAdapter to handle specific - * formatting for Date and DateTime types. - *

- *

- * You can optionally register JsonWriteBeanVisitors to customise the processing - * of the beans as they are processed and add raw JSON - * elements. - *

- *

* You can explicitly state which properties to include in the JSON output for * the root level and each path. *

* *
- * // find some customers ...
- * 
- * List<Customer> 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();
- * 
- * JsonWriteOptions writeOptions = new JsonWriteOptions();
- * writeOptions.setRootPathVisitor(new JsonWriteBeanVisitor<Customer>() {
- * 
- *   public void visit(Customer bean, JsonWriter ctx) {
- *     System.out.println("write visit customer: " + bean);
- *     ctx.appendKeyValue("dummyCust", "34");
- *     ctx.appendKeyValue("smallCustObject", "{\"a\":34,\"b\":\"asdasdasd\"}");
- *   }
- * });
- * 
- * writeOptions.setPathProperties("contacts", "firstName,id");
- * writeOptions.setPathVisitor("contacts", new JsonWriteBeanVisitor<Contact>() {
- * 
- *   public void visit(Contact bean, JsonWriter ctx) {
- *     System.out.println("write additional custom json on customer: " + bean);
- *     ctx.appendKeyValue("dummy", "  3400" + bean.getId() + "");
- *     ctx.appendKeyValue("smallObject", "{\"contactA\":34,\"contactB\":\"banana\"}");
- *   }
- * 
- * });
- * 
+
  * // output as a JSON string with pretty formatting
- * String s = json.toJsonString(list, true, writeOptions);
+ * String s = json.toJson(list, true, writeOptions);
  * 
  * 
- * - * @see JsonContext#toList(Class, String, JsonReadOptions) - * - * @author rbygrave - * */ public class JsonWriteOptions { - protected String callback; - protected PathProperties pathProperties; /** @@ -82,94 +33,6 @@ public class JsonWriteOptions { return o; } - /** - * This creates and returns a copy of these options. - *

- * Note that it assumes that the JsonWriteBeanVisitor (if defined) are - * immutable and any JsonWriteBeanVisitor instances are shared between the - * original and the copy. - *

- */ - public JsonWriteOptions copy() { - JsonWriteOptions copy = new JsonWriteOptions(); - copy.callback = callback; - copy.pathProperties = pathProperties; - return copy; - } - - /** - * Return a JSONP callback function. - */ - public String getCallback() { - return callback; - } - - /** - * Set a JSONP callback function. - */ - public JsonWriteOptions setCallback(String callback) { - this.callback = callback; - return this; - } - - /** - * Set the properties to include in the JSON output for the given path. - * - * @param propertiesToInclude - * The set of properties to output - */ - public JsonWriteOptions setPathProperties(String path, Set propertiesToInclude) { - if (pathProperties == null) { - pathProperties = new PathProperties(); - } - pathProperties.put(path, propertiesToInclude); - return this; - } - - /** - * Set the properties to include in the JSON output for the given path. - * - * @param propertiesToInclude - * Comma delimited list of properties to output - */ - public JsonWriteOptions setPathProperties(String path, String propertiesToInclude) { - return setPathProperties(path, parseProps(propertiesToInclude)); - } - - /** - * Set the properties to include in the JSON output for the root level. - * - * @param propertiesToInclude - * Comma delimited list of properties to output - */ - public JsonWriteOptions setRootPathProperties(String propertiesToInclude) { - return setPathProperties(null, parseProps(propertiesToInclude)); - } - - /** - * Set the properties to include in the JSON output for the root level. - * - * @param propertiesToInclude - * The set of properties to output - */ - public JsonWriteOptions setRootPathProperties(Set propertiesToInclude) { - return setPathProperties(null, propertiesToInclude); - } - - private Set parseProps(String propertiesToInclude) { - - LinkedHashSet props = new LinkedHashSet(); - - String[] split = propertiesToInclude.split(","); - for (int i = 0; i < split.length; i++) { - String s = split[i].trim(); - if (s.length() > 0) { - props.add(s); - } - } - return props; - } - /** * Set the Map of properties to include by path. */ diff --git a/src/main/java/com/avaje/ebean/text/json/package-info.java b/src/main/java/com/avaje/ebean/text/json/package-info.java index cb51e51c0..cdb33b989 100644 --- a/src/main/java/com/avaje/ebean/text/json/package-info.java +++ b/src/main/java/com/avaje/ebean/text/json/package-info.java @@ -19,31 +19,10 @@ * .order().desc("id") * .findList(); * - * JsonContext json = Ebean.createJsonContext(); - * - * JsonWriteOptions writeOptions = new JsonWriteOptions(); - * writeOptions.setRootPathVisitor(new JsonWriteBeanVisitor<Customer>() { - * - * public void visit(Customer bean, JsonWriter ctx) { - * System.out.println("write visit customer: " + bean); - * ctx.appendKeyValue("dummyCust", "34"); - * ctx.appendKeyValue("smallCustObject", "{\"a\":34,\"b\":\"asdasdasd\"}"); - * } - * }); - * - * writeOptions.setPathProperties("contacts", "firstName,id"); - * writeOptions.setPathVisitor("contacts", new JsonWriteBeanVisitor<Contact>() { - * - * public void visit(Contact bean, JsonWriter ctx) { - * System.out.println("write additional custom json on customer: " + bean); - * ctx.appendKeyValue("dummy", " 3400" + bean.getId() + ""); - * ctx.appendKeyValue("smallObject", "{\"contactA\":34,\"contactB\":\"banana\"}"); - * } - * - * }); - * - * // output as a JSON string with pretty formatting - * String s = json.toJsonString(list, true, writeOptions); + * JsonContext json = Ebean.json(); + * + * // output as a JSON string + * String jsonOutput = json.toJson(list); * * */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java index 4e33c009b..aa3994170 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java @@ -1,14 +1,14 @@ package com.avaje.ebeaninternal.server.deploy; -import java.io.IOException; - import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.text.TextException; import com.avaje.ebeaninternal.server.text.json.WriteJson; import com.avaje.ebeaninternal.server.text.json.WriteJson.WriteBean; +import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; +import java.io.IOException; + public class BeanDescriptorJsonHelp { private final BeanDescriptor desc; @@ -56,7 +56,7 @@ public class BeanDescriptorJsonHelp { return null; } if (JsonToken.START_OBJECT != token) { - throw new IOException("Unexpected token "+token+" - expecting start_object at: "+parser.getCurrentLocation()); + throw new JsonParseException("Unexpected token "+token+" - expecting start_object", parser.getCurrentLocation()); } if (desc.inheritInfo == null) { @@ -69,7 +69,7 @@ public class BeanDescriptorJsonHelp { token = parser.nextToken(); if (token != JsonToken.FIELD_NAME) { String msg = "Error reading inheritance discriminator - expected [" + discColumn + "] but no json key?"; - throw new TextException(msg); + throw new JsonParseException(msg, parser.getCurrentLocation()); } String propName = parser.getCurrentName(); @@ -82,7 +82,7 @@ public class BeanDescriptorJsonHelp { return jsonReadProperties(parser, bean); } String msg = "Error reading inheritance discriminator, expected property ["+discColumn+"] but got [" + propName + "] ?"; - throw new TextException(msg); + throw new JsonParseException(msg, parser.getCurrentLocation()); } String discValue = parser.nextTextValue(); 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 0ee972c80..0745915b7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java @@ -1,21 +1,6 @@ package com.avaje.ebeaninternal.server.deploy; -import java.io.IOException; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import javax.persistence.PersistenceException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.avaje.ebean.EbeanServer; -import com.avaje.ebean.Expression; -import com.avaje.ebean.Query; -import com.avaje.ebean.SqlUpdate; -import com.avaje.ebean.Transaction; +import com.avaje.ebean.*; import com.avaje.ebean.bean.BeanCollection; import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; import com.avaje.ebean.bean.BeanCollectionAdd; @@ -27,10 +12,18 @@ import com.avaje.ebeaninternal.server.deploy.id.ImportedId; import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany; import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; import com.avaje.ebeaninternal.server.el.ElPropertyValue; -import com.avaje.ebeaninternal.server.lib.util.StringHelper; import com.avaje.ebeaninternal.server.query.SqlBeanLoad; import com.avaje.ebeaninternal.server.text.json.WriteJson; import com.fasterxml.jackson.core.JsonParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.persistence.PersistenceException; +import java.io.IOException; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; /** * Property mapped to a List Set or Map. @@ -38,9 +31,9 @@ import com.fasterxml.jackson.core.JsonParser; public class BeanPropertyAssocMany extends BeanPropertyAssoc { private static final Logger logger = LoggerFactory.getLogger(BeanPropertyAssocMany.class); - + private final BeanPropertyAssocManyJsonHelp jsonHelp; - + /** * Join for manyToMany intersection table. */ @@ -81,12 +74,12 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { private final ModifyListenMode modifyListenMode; private BeanProperty mapKeyProperty; - + /** * Derived list of exported property and matching foreignKey */ private ExportedProperty[] exportedProperties; - + private String exportedPropertyBindProto = "?"; /** @@ -101,198 +94,189 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { private ImportedId importedId; private String deleteByParentIdSql; - + private String deleteByParentIdInSql; - /** - * Create this property. - */ - public BeanPropertyAssocMany(BeanDescriptorMap owner, BeanDescriptor descriptor, DeployBeanPropertyAssocMany deploy) { - super(owner, descriptor, deploy); - this.unidirectional = deploy.isUnidirectional(); - this.manyToMany = deploy.isManyToMany(); - this.manyType = deploy.getManyType(); - this.mapKey = deploy.getMapKey(); - this.fetchOrderBy = deploy.getFetchOrderBy(); - this.intersectionJoin = deploy.createIntersectionTableJoin(); - this.inverseJoin = deploy.createInverseTableJoin(); - this.modifyListenMode = deploy.getModifyListenMode(); - this.jsonHelp = new BeanPropertyAssocManyJsonHelp(this); - } + /** + * Create this property. + */ + public BeanPropertyAssocMany(BeanDescriptorMap owner, BeanDescriptor descriptor, DeployBeanPropertyAssocMany deploy) { + super(owner, descriptor, deploy); + this.unidirectional = deploy.isUnidirectional(); + this.manyToMany = deploy.isManyToMany(); + this.manyType = deploy.getManyType(); + this.mapKey = deploy.getMapKey(); + this.fetchOrderBy = deploy.getFetchOrderBy(); + this.intersectionJoin = deploy.createIntersectionTableJoin(); + this.inverseJoin = deploy.createInverseTableJoin(); + this.modifyListenMode = deploy.getModifyListenMode(); + this.jsonHelp = new BeanPropertyAssocManyJsonHelp(this); + } - public void initialise() { - super.initialise(); + public void initialise() { + super.initialise(); - if (!isTransient){ - this.help = BeanCollectionHelpFactory.create(this); + if (!isTransient) { + this.help = BeanCollectionHelpFactory.create(this); - if (manyToMany){ - // only manyToMany's have imported properties - importedId = createImportedId(this, targetDescriptor, tableJoin); - - } else { - // find the property in the many that matches - // back to the master (Order in the OrderDetail bean) - childMasterProperty = initChildMasterProperty(); - if (childMasterProperty != null){ - childMasterProperty.setRelationshipProperty(this); - } - } - - if (mapKey != null){ - mapKeyProperty = initMapKeyProperty(); - } - - exportedProperties = createExported(); - if (exportedProperties.length > 0){ - embeddedExportedProperties = exportedProperties[0].isEmbedded(); - exportedPropertyBindProto = deriveExportedPropertyBindProto(); - - if (fetchOrderBy != null) { - // derive lazyFetchOrderBy - StringBuilder sb = new StringBuilder(50); - for (int i = 0; i < exportedProperties.length; i++) { - if (i > 0) { - sb.append(", "); - } - // these fk columns are either on the intersection (int_) or base table (t0) - String fkTableAlias = isManyToMany() ? "int_" : "t0"; - sb.append(fkTableAlias).append(".").append(exportedProperties[i].getForeignDbColumn()); - } - if (fetchOrderBy != null) { - sb.append(", ").append(fetchOrderBy); - } - lazyFetchOrderBy = sb.toString().trim(); - } - } - - String delStmt; - if (manyToMany){ - delStmt = "delete from "+inverseJoin.getTable()+" where "; - } else { - delStmt = "delete from "+targetDescriptor.getBaseTable()+" where "; - } - deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false,""); - deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true,""); - } - } - - /** - * Add the bean to the appropriate collection on the parent bean. - */ - public void addBeanToCollectionWithCreate(EntityBean parentBean, EntityBean detailBean) { - BeanCollection bc = (BeanCollection)super.getValue(parentBean); - if (bc == null) { - bc = (BeanCollection)help.createEmpty(false); - setValue(parentBean, bc); - } - help.add(bc, detailBean); - } - - public boolean isEmptyBeanCollection(EntityBean bean) { - Object val = getValue(bean); - if (val == null) { - return true; - } - if (val instanceof BeanCollection) { - // if empty and not been cleared or elements removed - return ((BeanCollection)val).isEmptyAndUntouched(); - } - return false; - } - - @Override - public Object getValue(EntityBean bean) { - return super.getValue(bean); - } - - @Override - public Object getValueIntercept(EntityBean bean) { - return super.getValueIntercept(bean); - } - - @Override - public void setValue(EntityBean bean, Object value) { - super.setValue(bean, value); - } - - @Override - public void setValueIntercept(EntityBean bean, Object value) { - super.setValueIntercept(bean, value); - } - - public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) { - return createElPropertyValue(propName, remainder, chain, propertyDeploy); - } - - public SqlUpdate deleteByParentId(Object parentId, List parentIdist) { - if (parentId != null){ - return deleteByParentId(parentId); - } else { - return deleteByParentIdList(parentIdist); - } - } - - private SqlUpdate deleteByParentId(Object parentId) { - DefaultSqlUpdate sqlDelete = new DefaultSqlUpdate(deleteByParentIdSql); - bindWhereParendId(sqlDelete, parentId); - return sqlDelete; - } - - /** - * Find the Id's of detail beans given a parent Id or list of parent Id's. - */ - public List findIdsByParentId(Object parentId, List parentIdist, Transaction t, ArrayList excludeDetailIds) { - if (parentId != null){ - return findIdsByParentId(parentId, t, excludeDetailIds); - } else { - return findIdsByParentIdList(parentIdist, t, excludeDetailIds); - } - } - - private List findIdsByParentId(Object parentId, Transaction t, ArrayList excludeDetailIds) { - - String rawWhere = deriveWhereParentIdSql(false,""); - - EbeanServer server = getBeanDescriptor().getEbeanServer(); - Query q = server.find(getPropertyType()) - .where().raw(rawWhere).query(); - - bindWhereParendId(1, q, parentId); - - if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) { - Expression idIn = q.getExpressionFactory().idIn(excludeDetailIds); - q.where().not(idIn); - } - - return server.findIds(q, t); - } - - /** - * Add a where clause to the query for a given list of parent Id's. - */ - public void addWhereParentIdIn(SpiQuery query, List parentIds) { - - String tableAlias = manyToMany ? "int_." : "t0."; if (manyToMany) { - query.setIncludeTableJoin(inverseJoin); + // only manyToMany's have imported properties + importedId = createImportedId(this, targetDescriptor, tableJoin); + + } else { + // find the property in the many that matches + // back to the master (Order in the OrderDetail bean) + childMasterProperty = initChildMasterProperty(); + if (childMasterProperty != null) { + childMasterProperty.setRelationshipProperty(this); + } } - String rawWhere = deriveWhereParentIdSql(true, tableAlias); - String inClause = descriptor.getIdBinder().getIdInValueExpr(parentIds.size()); - String expr = rawWhere+inClause; + if (mapKey != null) { + mapKeyProperty = initMapKeyProperty(); + } - // Flatten the bind values if needed (embeddedId) - List bindValues = getBindParentIds(parentIds); - - query.where().raw(expr, bindValues.toArray()); + exportedProperties = createExported(); + if (exportedProperties.length > 0) { + embeddedExportedProperties = exportedProperties[0].isEmbedded(); + exportedPropertyBindProto = deriveExportedPropertyBindProto(); + + if (fetchOrderBy != null) { + // derive lazyFetchOrderBy + StringBuilder sb = new StringBuilder(50); + for (int i = 0; i < exportedProperties.length; i++) { + if (i > 0) { + sb.append(", "); + } + // these fk columns are either on the intersection (int_) or base table (t0) + String fkTableAlias = isManyToMany() ? "int_" : "t0"; + sb.append(fkTableAlias).append(".").append(exportedProperties[i].getForeignDbColumn()); + } + sb.append(", ").append(fetchOrderBy); + lazyFetchOrderBy = sb.toString().trim(); + } + } + + String delStmt; + if (manyToMany) { + delStmt = "delete from " + inverseJoin.getTable() + " where "; + } else { + delStmt = "delete from " + targetDescriptor.getBaseTable() + " where "; + } + deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false, ""); + deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true, ""); } - + } + + /** + * Add the bean to the appropriate collection on the parent bean. + */ + public void addBeanToCollectionWithCreate(EntityBean parentBean, EntityBean detailBean) { + BeanCollection bc = (BeanCollection) super.getValue(parentBean); + if (bc == null) { + bc = (BeanCollection) help.createEmpty(false); + setValue(parentBean, bc); + } + help.add(bc, detailBean); + } + + public boolean isEmptyBeanCollection(EntityBean bean) { + Object val = getValue(bean); + return val == null || (val instanceof BeanCollection) && ((BeanCollection) val).isEmptyAndUntouched(); + } + + @Override + public Object getValue(EntityBean bean) { + return super.getValue(bean); + } + + @Override + public Object getValueIntercept(EntityBean bean) { + return super.getValueIntercept(bean); + } + + @Override + public void setValue(EntityBean bean, Object value) { + super.setValue(bean, value); + } + + @Override + public void setValueIntercept(EntityBean bean, Object value) { + super.setValueIntercept(bean, value); + } + + public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) { + return createElPropertyValue(propName, remainder, chain, propertyDeploy); + } + + public SqlUpdate deleteByParentId(Object parentId, List parentIdist) { + if (parentId != null) { + return deleteByParentId(parentId); + } else { + return deleteByParentIdList(parentIdist); + } + } + + private SqlUpdate deleteByParentId(Object parentId) { + DefaultSqlUpdate sqlDelete = new DefaultSqlUpdate(deleteByParentIdSql); + bindWhereParendId(sqlDelete, parentId); + return sqlDelete; + } + + /** + * Find the Id's of detail beans given a parent Id or list of parent Id's. + */ + public List findIdsByParentId(Object parentId, List parentIdist, Transaction t, ArrayList excludeDetailIds) { + if (parentId != null) { + return findIdsByParentId(parentId, t, excludeDetailIds); + } else { + return findIdsByParentIdList(parentIdist, t, excludeDetailIds); + } + } + + private List findIdsByParentId(Object parentId, Transaction t, ArrayList excludeDetailIds) { + + String rawWhere = deriveWhereParentIdSql(false, ""); + + EbeanServer server = getBeanDescriptor().getEbeanServer(); + Query q = server.find(getPropertyType()) + .where().raw(rawWhere).query(); + + bindWhereParendId(1, q, parentId); + + if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) { + Expression idIn = q.getExpressionFactory().idIn(excludeDetailIds); + q.where().not(idIn); + } + + return server.findIds(q, t); + } + + /** + * Add a where clause to the query for a given list of parent Id's. + */ + public void addWhereParentIdIn(SpiQuery query, List parentIds) { + + String tableAlias = manyToMany ? "int_." : "t0."; + if (manyToMany) { + query.setIncludeTableJoin(inverseJoin); + } + String rawWhere = deriveWhereParentIdSql(true, tableAlias); + String inClause = descriptor.getIdBinder().getIdInValueExpr(parentIds.size()); + + String expr = rawWhere + inClause; + + // Flatten the bind values if needed (embeddedId) + List bindValues = getBindParentIds(parentIds); + + query.where().raw(expr, bindValues.toArray()); + } + private List findIdsByParentIdList(List parentIdist, Transaction t, ArrayList excludeDetailIds) { String rawWhere = deriveWhereParentIdSql(true, ""); String inClause = buildInClauseBinding(parentIdist.size(), exportedPropertyBindProto); - + String expr = rawWhere + inClause; EbeanServer server = getBeanDescriptor().getEbeanServer(); @@ -310,7 +294,7 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { return server.findIds(q, t); } - + private SqlUpdate deleteByParentIdList(List parentIdist) { StringBuilder sb = new StringBuilder(100); @@ -325,8 +309,8 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { } return delete; - } - + } + private String deriveExportedPropertyBindProto() { if (exportedProperties.length == 1) { return "?"; @@ -357,549 +341,532 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { } sb.append(") "); return sb.toString(); - } - - /** - * Set the lazy load server to help create reference collections (that lazy - * load on demand). - */ - public void setLoader(BeanCollectionLoader loader){ - if (help != null){ - help.setLoader(loader); - } - } + } - /** - * Return the mode for listening to modifications to collections for this - * association. - */ - public ModifyListenMode getModifyListenMode() { - return modifyListenMode; - } - - /** - * Ignore changes for Many properties. - */ - public boolean hasChanged(Object bean, Object oldValues) { - return false; - } - - @Override - public void appendSelect(DbSqlContext ctx, boolean subQuery) { - } - - @Override - public void loadIgnore(DbReadContext ctx) { - // nothing to ignore for Many + /** + * Set the lazy load server to help create reference collections (that lazy + * load on demand). + */ + public void setLoader(BeanCollectionLoader loader) { + if (help != null) { + help.setLoader(loader); } - - @Override - public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { - sqlBeanLoad.loadAssocMany(this); - } - - @Override - public Object readSet(DbReadContext ctx, EntityBean bean, Class type) throws SQLException { - return null; - } + } - @Override - public Object read(DbReadContext ctx) throws SQLException { - return null; - } + /** + * Return the mode for listening to modifications to collections for this + * association. + */ + public ModifyListenMode getModifyListenMode() { + return modifyListenMode; + } + + @Override + public void appendSelect(DbSqlContext ctx, boolean subQuery) { + } + + @Override + public void loadIgnore(DbReadContext ctx) { + // nothing to ignore for Many + } + + @Override + public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { + sqlBeanLoad.loadAssocMany(this); + } + + @Override + public Object readSet(DbReadContext ctx, EntityBean bean, Class type) throws SQLException { + return null; + } + + @Override + public Object read(DbReadContext ctx) throws SQLException { + return null; + } - @Override - public boolean isValueLoaded(Object value) { - if (value instanceof BeanCollection){ - return ((BeanCollection)value).isPopulated(); - } - return true; - } + @Override + public boolean isValueLoaded(Object value) { + return !(value instanceof BeanCollection) || ((BeanCollection) value).isPopulated(); + } - public void add(BeanCollection collection, EntityBean bean) { - help.add(collection, bean); - } + public void add(BeanCollection collection, EntityBean bean) { + help.add(collection, bean); + } - /** - * Refresh the appropriate list set or map. - */ - public void refresh(EbeanServer server, Query query, Transaction t, EntityBean parentBean) { - help.refresh(server, query, t, parentBean); - } + /** + * Refresh the appropriate list set or map. + */ + public void refresh(EbeanServer server, Query query, Transaction t, EntityBean parentBean) { + help.refresh(server, query, t, parentBean); + } - /** - * Apply the refreshed BeanCollection to the property of the parentBean. - */ - public void refresh(BeanCollection bc, EntityBean parentBean) { - help.refresh(bc, parentBean); - } + /** + * Apply the refreshed BeanCollection to the property of the parentBean. + */ + public void refresh(BeanCollection bc, EntityBean parentBean) { + help.refresh(bc, parentBean); + } - /** - * Return the Id values from the given bean. - */ - @Override - public Object[] getAssocOneIdValues(EntityBean bean) { - return targetDescriptor.getIdBinder().getIdValues(bean); + /** + * Return the Id values from the given bean. + */ + @Override + public Object[] getAssocOneIdValues(EntityBean bean) { + return targetDescriptor.getIdBinder().getIdValues(bean); + } + + /** + * Return the Id expression to add to where clause etc. + */ + public String getAssocOneIdExpr(String prefix, String operator) { + return targetDescriptor.getIdBinder().getAssocOneIdExpr(prefix, operator); + } + + /** + * Return the logical id value expression taking into account embedded id's. + */ + @Override + public String getAssocIdInValueExpr(int size) { + return targetDescriptor.getIdBinder().getIdInValueExpr(size); + } + + /** + * Return the logical id in expression taking into account embedded id's. + */ + @Override + public String getAssocIdInExpr(String prefix) { + return targetDescriptor.getIdBinder().getAssocIdInExpr(prefix); + } + + + @Override + public boolean isAssocId() { + return true; + } + + @Override + public boolean isAssocProperty() { + return true; + } + + /** + * Returns true. + */ + @Override + public boolean containsMany() { + return true; + } + + /** + * Return the many type. + */ + public ManyType getManyType() { + return manyType; + } + + /** + * Return true if this is many to many. + */ + public boolean isManyToMany() { + return manyToMany; + } + + /** + * ManyToMany only, join from local table to intersection table. + */ + public TableJoin getIntersectionTableJoin() { + return intersectionJoin; + } + + /** + * Set the join properties from the parent bean to the child bean. + * This is only valid for OneToMany and NOT valid for ManyToMany. + */ + public void setJoinValuesToChild(EntityBean parent, EntityBean child, Object mapKeyValue) { + + if (mapKeyProperty != null) { + mapKeyProperty.setValue(child, mapKeyValue); } - /** - * Return the Id expression to add to where clause etc. - */ - public String getAssocOneIdExpr(String prefix, String operator) { - return targetDescriptor.getIdBinder().getAssocOneIdExpr(prefix, operator); - } - - /** - * Return the logical id value expression taking into account embedded id's. - */ - @Override - public String getAssocIdInValueExpr(int size){ - return targetDescriptor.getIdBinder().getIdInValueExpr(size); - } - - /** - * Return the logical id in expression taking into account embedded id's. - */ - @Override - public String getAssocIdInExpr(String prefix){ - return targetDescriptor.getIdBinder().getAssocIdInExpr(prefix); + if (!manyToMany && childMasterProperty != null) { + // bidirectional in the sense that the 'master' property + // exists on the 'detail' bean + childMasterProperty.setValue(child, parent); } + } + /** + * Return the order by clause used to order the fetching of the data for + * this list, set or map. + */ + public String getFetchOrderBy() { + return fetchOrderBy; + } - @Override - public boolean isAssocId() { - return true; - } - - @Override - public boolean isAssocProperty() { - return true; - } - - /** - * Returns true. - */ - @Override - public boolean containsMany(){ - return true; - } - - /** - * Return the many type. - */ - public ManyType getManyType() { - return manyType; - } - - /** - * Return true if this is many to many. - */ - public boolean isManyToMany() { - return manyToMany; - } - - /** - * ManyToMany only, join from local table to intersection table. - */ - public TableJoin getIntersectionTableJoin() { - return intersectionJoin; - } - - /** - * Set the join properties from the parent bean to the child bean. - * This is only valid for OneToMany and NOT valid for ManyToMany. - */ - public void setJoinValuesToChild(EntityBean parent, EntityBean child, Object mapKeyValue) { - - if (mapKeyProperty != null){ - mapKeyProperty.setValue(child, mapKeyValue); - } - - if (!manyToMany){ - if (childMasterProperty != null){ - // bidirectional in the sense that the 'master' property - // exists on the 'detail' bean - childMasterProperty.setValue(child, parent); - } else { - // unidirectional in the sense that the 'master' property - // does NOT exist on the 'detail' bean - } - } - } - - /** - * Return the order by clause used to order the fetching of the data for - * this list, set or map. - */ - public String getFetchOrderBy() { - return fetchOrderBy; - } - - /** - * Return the order by for use when lazy loading the associated collection. - */ - public String getLazyFetchOrderBy() { + /** + * Return the order by for use when lazy loading the associated collection. + */ + public String getLazyFetchOrderBy() { return lazyFetchOrderBy; } /** - * Return the default mapKey when returning a Map. - */ - public String getMapKey() { - return mapKey; - } + * Return the default mapKey when returning a Map. + */ + public String getMapKey() { + return mapKey; + } - public BeanCollection createReferenceIfNull(EntityBean parentBean) { + public BeanCollection createReferenceIfNull(EntityBean parentBean) { - Object v = getValue(parentBean); - if (v instanceof BeanCollection){ - BeanCollection bc = (BeanCollection)v; - return bc.isReference() ? bc : null; - } else { - return createReference(parentBean); - } + Object v = getValue(parentBean); + if (v instanceof BeanCollection) { + BeanCollection bc = (BeanCollection) v; + return bc.isReference() ? bc : null; + } else { + return createReference(parentBean); } - - public BeanCollection createReference(EntityBean parentBean) { + } - BeanCollection ref = help.createReference(parentBean, name); - setValue(parentBean, ref); - return ref; - } + public BeanCollection createReference(EntityBean parentBean) { - public Object createEmpty(boolean vanilla) { - return help.createEmpty(vanilla); - } - - public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) { - return help.getBeanCollectionAdd(bc, mapKey); - } - - public Object getParentId(EntityBean parentBean) { - return descriptor.getId(parentBean); - } + BeanCollection ref = help.createReference(parentBean, name); + setValue(parentBean, ref); + return ref; + } - public List getBindParentIds(List parentIds) { - if (exportedProperties.length == 1){ + public Object createEmpty(boolean vanilla) { + return help.createEmpty(vanilla); + } + + public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) { + return help.getBeanCollectionAdd(bc, mapKey); + } + + public Object getParentId(EntityBean parentBean) { + return descriptor.getId(parentBean); + } + + public List getBindParentIds(List parentIds) { + if (exportedProperties.length == 1) { return parentIds; } - List expandedList = new ArrayList(parentIds.size()*exportedProperties.length); - for (int i=0; i < parentIds.size(); i++) { + List expandedList = new ArrayList(parentIds.size() * exportedProperties.length); + for (int i = 0; i < parentIds.size(); i++) { for (int y = 0; y < exportedProperties.length; y++) { Object compId = parentIds.get(i); - expandedList.add(exportedProperties[y].getValue((EntityBean)compId)); - } + expandedList.add(exportedProperties[y].getValue((EntityBean) compId)); + } } return expandedList; - } - - private void bindWhereParendId(DefaultSqlUpdate sqlUpd, Object parentId){ - - if (exportedProperties.length == 1){ - sqlUpd.addParameter(parentId); - return; - } - EntityBean parent = (EntityBean)parentId; - for (int i = 0; i < exportedProperties.length; i++) { - Object embVal = exportedProperties[i].getValue(parent); - sqlUpd.addParameter(embVal); - } - } - - private int bindWhereParendId(int pos, Query q, Object parentId) { + } - if (exportedProperties.length == 1) { - q.setParameter(pos++, parentId); - - } else { - - EntityBean parent = (EntityBean)parentId; - for (int i = 0; i < exportedProperties.length; i++) { - Object embVal = exportedProperties[i].getValue(parent); - q.setParameter(pos++, embVal); - } - } - return pos; + private void bindWhereParendId(DefaultSqlUpdate sqlUpd, Object parentId) { + + if (exportedProperties.length == 1) { + sqlUpd.addParameter(parentId); + return; } - - public void addSelectExported(DbSqlContext ctx, String tableAlias) { - - String alias = manyToMany ? "int_" : tableAlias; - if (alias == null) { - alias = "t0"; - } + EntityBean parent = (EntityBean) parentId; + for (int i = 0; i < exportedProperties.length; i++) { + Object embVal = exportedProperties[i].getValue(parent); + sqlUpd.addParameter(embVal); + } + } + + private int bindWhereParendId(int pos, Query q, Object parentId) { + + if (exportedProperties.length == 1) { + q.setParameter(pos++, parentId); + + } else { + + EntityBean parent = (EntityBean) parentId; for (int i = 0; i < exportedProperties.length; i++) { - ctx.appendColumn(alias, exportedProperties[i].getForeignDbColumn()); + Object embVal = exportedProperties[i].getValue(parent); + q.setParameter(pos++, embVal); } } - - private String deriveWhereParentIdSql(boolean inClause, String tableAlias) { - - StringBuilder sb = new StringBuilder(); - - if (inClause){ - sb.append("("); - } - for (int i = 0; i < exportedProperties.length; i++) { - String fkColumn = exportedProperties[i].getForeignDbColumn(); - if (i > 0){ - String s = inClause ? "," : " and "; - sb.append(s); - } - sb.append(tableAlias).append(fkColumn); - if (!inClause){ - sb.append("=? "); - } - } - if (inClause){ - sb.append(")"); - } - return sb.toString(); + return pos; + } + + public void addSelectExported(DbSqlContext ctx, String tableAlias) { + + String alias = manyToMany ? "int_" : tableAlias; + if (alias == null) { + alias = "t0"; } - - public void setPredicates(SpiQuery query, EntityBean parentBean) { + for (int i = 0; i < exportedProperties.length; i++) { + ctx.appendColumn(alias, exportedProperties[i].getForeignDbColumn()); + } + } - if (manyToMany){ - // for ManyToMany lazy loading we need to include a - // join to the intersection table. The predicate column - // is not on the 'destination many table'. - query.setIncludeTableJoin(inverseJoin); - } + private String deriveWhereParentIdSql(boolean inClause, String tableAlias) { - if (embeddedExportedProperties) { - // use the EmbeddedId object instead of the parentBean - BeanProperty idProp = descriptor.getIdProperty(); - parentBean = (EntityBean)idProp.getValue(parentBean); - } + StringBuilder sb = new StringBuilder(); - for (int i = 0; i < exportedProperties.length; i++) { - Object val = exportedProperties[i].getValue(parentBean); - String fkColumn = exportedProperties[i].getForeignDbColumn(); - if (!manyToMany){ - fkColumn = targetDescriptor.getBaseTableAlias()+"."+fkColumn; - } else { - // use hard coded alias for intersection table - fkColumn = "int_."+fkColumn; - } - query.where().eq(fkColumn, val); - } + if (inClause) { + sb.append("("); + } + for (int i = 0; i < exportedProperties.length; i++) { + String fkColumn = exportedProperties[i].getForeignDbColumn(); + if (i > 0) { + String s = inClause ? "," : " and "; + sb.append(s); + } + sb.append(tableAlias).append(fkColumn); + if (!inClause) { + sb.append("=? "); + } + } + if (inClause) { + sb.append(")"); + } + return sb.toString(); + } - if (extraWhere != null){ - // replace the table alias place holder - String ta = targetDescriptor.getBaseTableAlias(); - String where = StringHelper.replaceString(extraWhere, "${ta}", ta); - query.where().raw(where); - } +// public void setPredicates(SpiQuery query, EntityBean parentBean) { +// +// if (manyToMany){ +// // for ManyToMany lazy loading we need to include a +// // join to the intersection table. The predicate column +// // is not on the 'destination many table'. +// query.setIncludeTableJoin(inverseJoin); +// } +// +// if (embeddedExportedProperties) { +// // use the EmbeddedId object instead of the parentBean +// BeanProperty idProp = descriptor.getIdProperty(); +// parentBean = (EntityBean)idProp.getValue(parentBean); +// } +// +// for (int i = 0; i < exportedProperties.length; i++) { +// Object val = exportedProperties[i].getValue(parentBean); +// String fkColumn = exportedProperties[i].getForeignDbColumn(); +// if (!manyToMany){ +// fkColumn = targetDescriptor.getBaseTableAlias()+"."+fkColumn; +// } else { +// // use hard coded alias for intersection table +// fkColumn = "int_."+fkColumn; +// } +// query.where().eq(fkColumn, val); +// } +// +// if (extraWhere != null){ +// // replace the table alias place holder +// String ta = targetDescriptor.getBaseTableAlias(); +// String where = StringHelper.replaceString(extraWhere, "${ta}", ta); +// query.where().raw(where); +// } +// +// if (fetchOrderBy != null){ +// query.order(fetchOrderBy); +// } +// } - if (fetchOrderBy != null){ - query.order(fetchOrderBy); - } - } + /** + * Create the array of ExportedProperty used to build reference objects. + */ + private ExportedProperty[] createExported() { - /** - * Create the array of ExportedProperty used to build reference objects. - */ - private ExportedProperty[] createExported() { + BeanProperty idProp = descriptor.getIdProperty(); - BeanProperty idProp = descriptor.getIdProperty(); + ArrayList list = new ArrayList(); - ArrayList list = new ArrayList(); + if (idProp != null && idProp.isEmbedded()) { - if (idProp != null && idProp.isEmbedded()) { - - BeanPropertyAssocOne one = (BeanPropertyAssocOne) idProp; - BeanDescriptor targetDesc = one.getTargetDescriptor(); - BeanProperty[] emIds = targetDesc.propertiesBaseScalar(); - try { - for (int i = 0; i < emIds.length; i++) { - ExportedProperty expProp = findMatch(true, emIds[i]); - list.add(expProp); - } - } catch (PersistenceException e){ - // not found as individual scalar properties - logger.error("Could not find a exported property?", e); - } - - } else { - if (idProp != null) { - ExportedProperty expProp = findMatch(false, idProp); - list.add(expProp); - } - } - - return (ExportedProperty[]) list.toArray(new ExportedProperty[list.size()]); - } - - /** - * Find the matching foreignDbColumn for a given local property. - */ - private ExportedProperty findMatch(boolean embedded,BeanProperty prop) { - - String matchColumn = prop.getDbColumn(); - - String searchTable; - TableJoinColumn[] columns; - if (manyToMany){ - // look for column going to intersection - columns = intersectionJoin.columns(); - searchTable = intersectionJoin.getTable(); - - } else { - columns = tableJoin.columns(); - searchTable = tableJoin.getTable(); - } - for (int i = 0; i < columns.length; i++) { - String matchTo = columns[i].getLocalDbColumn(); - - if (matchColumn.equalsIgnoreCase(matchTo)) { - String foreignCol = columns[i].getForeignDbColumn(); - return new ExportedProperty(embedded, foreignCol, prop); - } - } - - String msg = "Error with the Join on ["+getFullBeanName() - +"]. Could not find the matching foreign key for ["+matchColumn+"] in table["+searchTable+"]?" - +" Perhaps using a @JoinColumn with the name/referencedColumnName attributes swapped?"; - throw new PersistenceException(msg); - } - - /** - * Return the child property that links back to the master bean. - *

- * Note that childMasterProperty will be null if a field is used instead of - * a ManyToOne bean association. - *

- */ - private BeanPropertyAssocOne initChildMasterProperty() { - - if (unidirectional){ - return null; - } - - // search for the property, to see if it exists - Class beanType = descriptor.getBeanType(); - BeanDescriptor targetDesc = getTargetDescriptor(); - - BeanPropertyAssocOne[] ones = targetDesc.propertiesOne(); - for (int i = 0; i < ones.length; i++) { - BeanPropertyAssocOne prop = ones[i]; - if (mappedBy != null){ - // match using mappedBy as property name - if (mappedBy.equalsIgnoreCase(prop.getName())) { - return prop; - } - } else { - // assume only one property that matches parent object type - if (prop.getTargetType().equals(beanType)) { - // found it, stop search - return prop; - } - } - } - - String msg = "Can not find Master [" + beanType + "] in Child[" + targetDesc + "]"; - throw new RuntimeException(msg); - } - - /** - * Search for and return the mapKey property. - */ - private BeanProperty initMapKeyProperty() { - - // search for the property - BeanDescriptor targetDesc = getTargetDescriptor(); - for (BeanProperty prop : targetDesc.propertiesAll()) { - if (mapKey.equalsIgnoreCase(prop.getName())) { - return prop; - } - } - - String from = descriptor.getFullName(); - String to = targetDesc.getFullName(); - String msg = from+": Could not find mapKey property ["+mapKey+"] on ["+to+"]"; - throw new PersistenceException(msg); - } - - public IntersectionRow buildManyDeleteChildren(EntityBean parentBean, ArrayList excludeDetailIds) { - - IntersectionRow row = new IntersectionRow(tableJoin.getTable()); - if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) { - row.setExcludeIds(excludeDetailIds, getTargetDescriptor()); + BeanPropertyAssocOne one = (BeanPropertyAssocOne) idProp; + BeanDescriptor targetDesc = one.getTargetDescriptor(); + BeanProperty[] emIds = targetDesc.propertiesBaseScalar(); + try { + for (int i = 0; i < emIds.length; i++) { + ExportedProperty expProp = findMatch(true, emIds[i]); + list.add(expProp); } - buildExport(row, parentBean); - return row; + } catch (PersistenceException e) { + // not found as individual scalar properties + logger.error("Could not find a exported property?", e); + } + + } else { + if (idProp != null) { + ExportedProperty expProp = findMatch(false, idProp); + list.add(expProp); + } } - public IntersectionRow buildManyToManyDeleteChildren(EntityBean parentBean) { + return list.toArray(new ExportedProperty[list.size()]); + } - IntersectionRow row = new IntersectionRow(intersectionJoin.getTable()); - buildExport(row, parentBean); - return row; + /** + * Find the matching foreignDbColumn for a given local property. + */ + private ExportedProperty findMatch(boolean embedded, BeanProperty prop) { + + String matchColumn = prop.getDbColumn(); + + String searchTable; + TableJoinColumn[] columns; + if (manyToMany) { + // look for column going to intersection + columns = intersectionJoin.columns(); + searchTable = intersectionJoin.getTable(); + + } else { + columns = tableJoin.columns(); + searchTable = tableJoin.getTable(); } - - public IntersectionRow buildManyToManyMapBean(EntityBean parent, EntityBean other) { + for (int i = 0; i < columns.length; i++) { + String matchTo = columns[i].getLocalDbColumn(); - IntersectionRow row = new IntersectionRow(intersectionJoin.getTable()); - - buildExport(row, parent); - buildImport(row, other); - return row; - } - - private void buildExport(IntersectionRow row, EntityBean parentBean) { - - if (embeddedExportedProperties) { - BeanProperty idProp = descriptor.getIdProperty(); - parentBean = (EntityBean)idProp.getValue(parentBean); - } - for (int i = 0; i < exportedProperties.length; i++) { - Object val = exportedProperties[i].getValue(parentBean); - String fkColumn = exportedProperties[i].getForeignDbColumn(); - - row.put(fkColumn, val); - } - } - - /** - * Set the predicates for lazy loading of the association. - * Handles predicates for both OneToMany and ManyToMany. - */ - private void buildImport(IntersectionRow row, EntityBean otherBean) { - - importedId.buildImport(row, otherBean); - } - - /** - * Return true if the otherBean has an Id value. - */ - public boolean hasImportedId(EntityBean otherBean) { - - return null != targetDescriptor.getId(otherBean); + if (matchColumn.equalsIgnoreCase(matchTo)) { + String foreignCol = columns[i].getForeignDbColumn(); + return new ExportedProperty(embedded, foreignCol, prop); + } } - public void jsonWrite(WriteJson ctx, EntityBean bean) throws IOException { - if(!this.jsonSerialize){ - return; + String msg = "Error with the Join on [" + getFullBeanName() + + "]. Could not find the matching foreign key for [" + matchColumn + "] in table[" + searchTable + "]?" + + " Perhaps using a @JoinColumn with the name/referencedColumnName attributes swapped?"; + throw new PersistenceException(msg); + } + + /** + * Return the child property that links back to the master bean. + *

+ * Note that childMasterProperty will be null if a field is used instead of + * a ManyToOne bean association. + *

+ */ + private BeanPropertyAssocOne initChildMasterProperty() { + + if (unidirectional) { + return null; + } + + // search for the property, to see if it exists + Class beanType = descriptor.getBeanType(); + BeanDescriptor targetDesc = getTargetDescriptor(); + + BeanPropertyAssocOne[] ones = targetDesc.propertiesOne(); + for (int i = 0; i < ones.length; i++) { + BeanPropertyAssocOne prop = ones[i]; + if (mappedBy != null) { + // match using mappedBy as property name + if (mappedBy.equalsIgnoreCase(prop.getName())) { + return prop; } - Boolean include = ctx.includeMany(name); - if (Boolean.FALSE.equals(include)){ - return; - } - - Object value = getValueIntercept(bean); - if (value != null){ - ctx.pushParentBeanMany(bean); - if (help != null){ - help.jsonWrite(ctx, name, value, include != null); - } else { - ctx.toJson(name, (Collection)value); - } - ctx.popParentBeanMany(); + } else { + // assume only one property that matches parent object type + if (prop.getTargetType().equals(beanType)) { + // found it, stop search + return prop; } + } } - - public void jsonRead(JsonParser parser, EntityBean parentBean) throws IOException { - jsonHelp.jsonRead(parser, parentBean); + + throw new RuntimeException("Can not find Master [" + beanType + "] in Child[" + targetDesc + "]"); + } + + /** + * Search for and return the mapKey property. + */ + private BeanProperty initMapKeyProperty() { + + // search for the property + BeanDescriptor targetDesc = getTargetDescriptor(); + for (BeanProperty prop : targetDesc.propertiesAll()) { + if (mapKey.equalsIgnoreCase(prop.getName())) { + return prop; + } } + + String from = descriptor.getFullName(); + String to = targetDesc.getFullName(); + throw new PersistenceException(from + ": Could not find mapKey property [" + mapKey + "] on [" + to + "]"); + } + + public IntersectionRow buildManyDeleteChildren(EntityBean parentBean, ArrayList excludeDetailIds) { + + IntersectionRow row = new IntersectionRow(tableJoin.getTable()); + if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) { + row.setExcludeIds(excludeDetailIds, getTargetDescriptor()); + } + buildExport(row, parentBean); + return row; + } + + public IntersectionRow buildManyToManyDeleteChildren(EntityBean parentBean) { + + IntersectionRow row = new IntersectionRow(intersectionJoin.getTable()); + buildExport(row, parentBean); + return row; + } + + public IntersectionRow buildManyToManyMapBean(EntityBean parent, EntityBean other) { + + IntersectionRow row = new IntersectionRow(intersectionJoin.getTable()); + + buildExport(row, parent); + buildImport(row, other); + return row; + } + + private void buildExport(IntersectionRow row, EntityBean parentBean) { + + if (embeddedExportedProperties) { + BeanProperty idProp = descriptor.getIdProperty(); + parentBean = (EntityBean) idProp.getValue(parentBean); + } + for (int i = 0; i < exportedProperties.length; i++) { + Object val = exportedProperties[i].getValue(parentBean); + String fkColumn = exportedProperties[i].getForeignDbColumn(); + + row.put(fkColumn, val); + } + } + + /** + * Set the predicates for lazy loading of the association. + * Handles predicates for both OneToMany and ManyToMany. + */ + private void buildImport(IntersectionRow row, EntityBean otherBean) { + + importedId.buildImport(row, otherBean); + } + + /** + * Return true if the otherBean has an Id value. + */ + public boolean hasImportedId(EntityBean otherBean) { + + return null != targetDescriptor.getId(otherBean); + } + + public void jsonWrite(WriteJson ctx, EntityBean bean) throws IOException { + if (!this.jsonSerialize) { + return; + } + Boolean include = ctx.includeMany(name); + if (Boolean.FALSE.equals(include)) { + return; + } + + Object value = getValueIntercept(bean); + if (value != null) { + ctx.pushParentBeanMany(bean); + if (help != null) { + help.jsonWrite(ctx, name, value, include != null); + } else { + ctx.toJson(name, (Collection) value); + } + ctx.popParentBeanMany(); + } + } + + public void jsonRead(JsonParser parser, EntityBean parentBean) throws IOException { + jsonHelp.jsonRead(parser, parentBean); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonContext.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonContext.java index 9e0f2576e..d47309c3d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonContext.java @@ -1,23 +1,8 @@ package com.avaje.ebeaninternal.server.text.json; -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.io.StringWriter; -import java.io.Writer; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - import com.avaje.ebean.bean.EntityBean; import com.avaje.ebean.json.EJson; import com.avaje.ebean.text.PathProperties; -import com.avaje.ebean.text.TextException; import com.avaje.ebean.text.json.JsonContext; import com.avaje.ebean.text.json.JsonWriteOptions; import com.avaje.ebeaninternal.api.SpiEbeanServer; @@ -25,16 +10,15 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; import com.avaje.ebeaninternal.util.ParamTypeHelper; import com.avaje.ebeaninternal.util.ParamTypeHelper.ManyType; import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.core.*; + +import java.io.*; +import java.lang.reflect.Type; +import java.util.*; +import java.util.Map.Entry; /** * Default implementation of JsonContext. - * - * @author rbygrave */ public class DJsonContext implements JsonContext { @@ -69,7 +53,7 @@ public class DJsonContext implements JsonContext { private T toBean(Class cls, JsonParser parser) throws IOException { - BeanDescriptor d = getDecriptor(cls); + BeanDescriptor d = getDescriptor(cls); return d.jsonRead(parser, null); } @@ -84,30 +68,25 @@ public class DJsonContext implements JsonContext { private List toList(Class cls, JsonParser src) throws IOException { - try { - BeanDescriptor d = getDecriptor(cls); + BeanDescriptor d = getDescriptor(cls); - List list = new ArrayList(); + List list = new ArrayList(); - JsonToken event = src.nextToken(); - if (event != JsonToken.START_ARRAY) { - throw new JsonParseException("Expecting start_array event but got " + event ,src.getCurrentLocation()); - } - - do { - T bean = d.jsonRead(src, null); - if (bean == null) { - break; - } else { - list.add(bean); - } - } while (true); - - return list; - - } catch (RuntimeException e) { - throw new TextException("Error parsing " + src, e); + JsonToken event = src.nextToken(); + if (event != JsonToken.START_ARRAY) { + throw new JsonParseException("Expecting start_array event but got " + event ,src.getCurrentLocation()); } + + do { + T bean = d.jsonRead(src, null); + if (bean == null) { + break; + } else { + list.add(bean); + } + } while (true); + + return list; } public Object toObject(Type genericType, String json) throws IOException { @@ -122,7 +101,7 @@ public class DJsonContext implements JsonContext { return toList(info.getBeanType(), json); default: - throw new TextException("Type " + manyType + " not supported"); + throw new IOException("Type " + manyType + " not supported"); } } @@ -138,26 +117,30 @@ public class DJsonContext implements JsonContext { return toList(info.getBeanType(), json); default: - throw new TextException("Type " + manyType + " not supported"); + throw new IOException("Type " + manyType + " not supported"); } } - public void toJsonWriter(Object o, Writer writer) throws IOException { - toJsonWriter(o, writer, null); + public void toJson(Object o, Writer writer) throws IOException { + toJson(o, writer, null); } - public void toJsonWriter(Object o, Writer writer, JsonWriteOptions options) throws IOException { + public void toJson(Object o, Writer writer, JsonWriteOptions options) throws IOException { JsonGenerator generator = createGenerator(writer); toJsonInternal(o, generator, options); generator.close(); } - public String toJsonString(Object o) throws IOException { + public String toJson(Object o) throws IOException { return toJsonString(o, null); } - public String toJsonString(Object o, JsonWriteOptions options) throws IOException { + public String toJson(Object o, JsonWriteOptions options) throws IOException { + return toJsonString(o, options); + } + + private String toJsonString(Object o, JsonWriteOptions options) throws IOException { StringWriter writer = new StringWriter(500); JsonGenerator gen = createGenerator(writer); toJsonInternal(o, gen, options); @@ -173,7 +156,7 @@ public class DJsonContext implements JsonContext { } else if (o instanceof Number) { gen.writeNumber(((Number) o).doubleValue()); } else if (o instanceof Boolean) { - gen.writeBoolean(((Boolean) o).booleanValue()); + gen.writeBoolean((Boolean) o); } else if (o instanceof String) { gen.writeString((String) o); @@ -186,7 +169,7 @@ public class DJsonContext implements JsonContext { toJsonFromCollection((Collection) o, null, gen, options); } else if (o instanceof EntityBean) { - BeanDescriptor d = getDecriptor(o.getClass()); + BeanDescriptor d = getDescriptor(o.getClass()); WriteJson writeJson = createWriteJson(gen, options); d.jsonWrite(writeJson, (EntityBean)o, null); } @@ -197,7 +180,7 @@ public class DJsonContext implements JsonContext { return new WriteJson(server, gen, pathProps); } - private void toJsonFromCollection(Collection c, String key, JsonGenerator gen, JsonWriteOptions options) throws IOException { + private void toJsonFromCollection(Collection collection, String key, JsonGenerator gen, JsonWriteOptions options) throws IOException { if (key != null) { gen.writeFieldName(key); @@ -206,11 +189,9 @@ public class DJsonContext implements JsonContext { WriteJson writeJson = createWriteJson(gen, options); - Iterator it = c.iterator(); - while (it.hasNext()) { - T t = it.next(); - BeanDescriptor d = getDecriptor(t.getClass()); - d.jsonWrite(writeJson, (EntityBean)t, null); + for (T bean : collection) { + BeanDescriptor d = getDescriptor(bean.getClass()); + d.jsonWrite(writeJson, (EntityBean) bean, null); } gen.writeEndArray(); } @@ -234,7 +215,7 @@ public class DJsonContext implements JsonContext { toJsonFromCollection((Collection) value, key, gen, options); } else if (value instanceof EntityBean) { - BeanDescriptor d = getDecriptor(value.getClass()); + BeanDescriptor d = getDescriptor(value.getClass()); d.jsonWrite(writeJson,(EntityBean) value, key); } else { @@ -245,7 +226,7 @@ public class DJsonContext implements JsonContext { gen.writeEndObject(); } - private BeanDescriptor getDecriptor(Class cls) { + private BeanDescriptor getDescriptor(Class cls) { BeanDescriptor d = server.getBeanDescriptor(cls); if (d == null) { throw new RuntimeException("No BeanDescriptor found for " + cls); diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJson.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJson.java index 58fb0e57a..8b1336bcf 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJson.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJson.java @@ -1,10 +1,5 @@ package com.avaje.ebeaninternal.server.text.json; -import java.io.IOException; -import java.util.Collection; -import java.util.Iterator; -import java.util.Set; - import com.avaje.ebean.bean.EntityBean; import com.avaje.ebean.text.PathProperties; import com.avaje.ebeaninternal.api.SpiEbeanServer; @@ -13,6 +8,10 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty; import com.avaje.ebeaninternal.server.util.ArrayStack; import com.fasterxml.jackson.core.JsonGenerator; +import java.io.IOException; +import java.util.Collection; +import java.util.Set; + public class WriteJson { private final SpiEbeanServer server; @@ -36,11 +35,7 @@ public class WriteJson { } public boolean isParentBean(Object bean) { - if (parentBeans.isEmpty()) { - return false; - } else { - return parentBeans.contains(bean); - } + return !parentBeans.isEmpty() && parentBeans.contains(bean); } public void pushParentBeanMany(Object parentBean) { @@ -61,15 +56,6 @@ public class WriteJson { pathStack.pop(); } - public Set getIncludeProperties() { - - if (pathProperties == null) { - return null; - } else { - return pathProperties.get(pathStack.peekWithNull()); - } - } - public WriteBean createWriteBean(BeanDescriptor desc, EntityBean bean) { if (pathProperties == null) { @@ -135,7 +121,6 @@ public class WriteJson { // render all the properties and invoke lazy loading if required BeanProperty[] props = desc.propertiesNonTransient(); for (int j = 0; j < props.length; j++) { - System.out.println("bean "+ currentBean+" prop:"+props[j]); if (isIncludeProperty(props[j])) { props[j].jsonWrite(writeJson, currentBean); } @@ -163,20 +148,17 @@ public class WriteJson { beginAssocMany(name); - Iterator it = c.iterator(); - while (it.hasNext()) { - EntityBean o = (EntityBean) it.next(); - BeanDescriptor d = getDecriptor(o.getClass()); - d.jsonWrite(this, o, null); + for (Object bean : c) { + BeanDescriptor d = getDescriptor(bean.getClass()); + d.jsonWrite(this, (EntityBean)bean, null); } endAssocMany(); } - private BeanDescriptor getDecriptor(Class cls) { + private BeanDescriptor getDescriptor(Class cls) { BeanDescriptor d = server.getBeanDescriptor(cls); if (d == null) { - String msg = "No BeanDescriptor found for " + cls; - throw new RuntimeException(msg); + throw new RuntimeException("No BeanDescriptor found for " + cls); } return d; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeByte.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeByte.java index c6aba32a3..f92cccf94 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeByte.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeByte.java @@ -42,13 +42,13 @@ public class ScalarTypeByte extends ScalarTypeBase { } @Override - public void jsonWrite(JsonGenerator ctx, String name, Object value) { - throw new TextException("Not supported"); + public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException { + throw new IOException("Not supported"); } @Override - public Object jsonRead(JsonParser ctx, JsonToken event) { - throw new TextException("Not supported"); + public Object jsonRead(JsonParser ctx, JsonToken event) throws IOException { + throw new IOException("Not supported"); } public String formatValue(Byte t) { diff --git a/src/test/java/com/avaje/ebean/text/json/JsonWriteOptionsTests.java b/src/test/java/com/avaje/ebean/text/json/JsonWriteOptionsTests.java index 82a570443..287d4d7c3 100644 --- a/src/test/java/com/avaje/ebean/text/json/JsonWriteOptionsTests.java +++ b/src/test/java/com/avaje/ebean/text/json/JsonWriteOptionsTests.java @@ -6,6 +6,8 @@ import org.junit.Test; import com.avaje.ebean.text.PathProperties; import com.avaje.ebean.text.json.JsonWriteOptions; +import java.util.Set; + public class JsonWriteOptionsTests { @Test @@ -19,6 +21,41 @@ public class JsonWriteOptionsTests { Assert.assertTrue(pathProps.get(null).contains("name")); Assert.assertTrue(pathProps.get(null).contains("status")); Assert.assertFalse(pathProps.get(null).contains("foo")); - } + + @Test + public void test_with_depth() { + + JsonWriteOptions options = JsonWriteOptions.parsePath("id,status,name,customer(id,name,address(street,city)),orders(qty,product(sku,prodName))"); + PathProperties pathProps = options.getPathProperties(); + + Assert.assertEquals(5, pathProps.getPaths().size()); + Assert.assertTrue(pathProps.get(null).contains("id")); + Assert.assertTrue(pathProps.get(null).contains("name")); + Assert.assertTrue(pathProps.get(null).contains("status")); + Assert.assertTrue(pathProps.get(null).contains("customer")); + Assert.assertTrue(pathProps.get(null).contains("orders")); + Assert.assertFalse(pathProps.get(null).contains("foo")); + + Set customer = pathProps.get("customer"); + Assert.assertTrue(customer.contains("id")); + Assert.assertTrue(customer.contains("name")); + Assert.assertTrue(customer.contains("address")); + + Set address = pathProps.get("customer.address"); + Assert.assertTrue(address.contains("street")); + Assert.assertTrue(address.contains("city")); + + Set orders = pathProps.get("orders"); + Assert.assertTrue(orders.contains("qty")); + Assert.assertTrue(orders.contains("product")); + + Set product = pathProps.get("orders.product"); + Assert.assertTrue(product.contains("sku")); + Assert.assertTrue(product.contains("prodName")); + + } + + + } diff --git a/src/test/java/com/avaje/ebeaninternal/server/text/json/WriteJsonTest.java b/src/test/java/com/avaje/ebeaninternal/server/text/json/WriteJsonTest.java new file mode 100644 index 000000000..318078302 --- /dev/null +++ b/src/test/java/com/avaje/ebeaninternal/server/text/json/WriteJsonTest.java @@ -0,0 +1,57 @@ +package com.avaje.ebeaninternal.server.text.json; + +import com.avaje.ebean.text.PathProperties; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import org.junit.Test; + +import java.io.IOException; +import java.io.StringWriter; + +import static org.junit.Assert.*; + +public class WriteJsonTest { + + @Test + public void test_push() throws IOException { + + JsonFactory jsonFactory = new JsonFactory(); + JsonGenerator generator = jsonFactory.createGenerator(new StringWriter()); + + PathProperties pathProperties = PathProperties.parse("id,status,name,customer(id,name,address(street,city)),orders(qty,product(sku,prodName))"); + WriteJson writeJson = new WriteJson(null, generator, pathProperties); + + WriteJson.WriteBean rootLevel = writeJson.createWriteBean(null, null); + assertTrue(rootLevel.currentIncludeProps.contains("id")); + assertTrue(rootLevel.currentIncludeProps.contains("status")); + assertTrue(rootLevel.currentIncludeProps.contains("name")); + assertTrue(rootLevel.currentIncludeProps.contains("customer")); + + writeJson.beginAssocOne("customer", null); + WriteJson.WriteBean customerLevel = writeJson.createWriteBean(null, null); + assertTrue(customerLevel.currentIncludeProps.contains("id")); + assertTrue(customerLevel.currentIncludeProps.contains("name")); + assertTrue(customerLevel.currentIncludeProps.contains("address")); + + writeJson.beginAssocOne("address", null); + WriteJson.WriteBean addressLevel = writeJson.createWriteBean(null, null); + assertTrue(addressLevel.currentIncludeProps.contains("street")); + assertTrue(addressLevel.currentIncludeProps.contains("city")); + + writeJson.endAssocOne(); + writeJson.endAssocOne(); + + writeJson.beginAssocMany("orders"); + WriteJson.WriteBean orderLevel = writeJson.createWriteBean(null, null); + assertTrue(orderLevel.currentIncludeProps.contains("qty")); + assertTrue(orderLevel.currentIncludeProps.contains("product")); + + writeJson.beginAssocOne("product", null); + WriteJson.WriteBean productLevel = writeJson.createWriteBean(null, null); + assertTrue(productLevel.currentIncludeProps.contains("sku")); + assertTrue(productLevel.currentIncludeProps.contains("prodName")); + writeJson.endAssocOne(); + writeJson.endAssocMany(); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/tests/ddd/iud/TestDPersonEl.java b/src/test/java/com/avaje/tests/ddd/iud/TestDPersonEl.java index 9f7dede2e..24cc15385 100644 --- a/src/test/java/com/avaje/tests/ddd/iud/TestDPersonEl.java +++ b/src/test/java/com/avaje/tests/ddd/iud/TestDPersonEl.java @@ -40,8 +40,8 @@ public class TestDPersonEl extends TestCase { // ElPropertyValue elCmoneyAmt = descriptor.getElGetValue("cmoney.amount"); // ElPropertyValue elCmoneyCur = descriptor.getElGetValue("cmoney.currency"); - JsonContext jsonContext = server.createJsonContext(); - String json = jsonContext.toJsonString(p); + JsonContext jsonContext = server.json(); + String json = jsonContext.toJson(p); DPerson bean = jsonContext.toBean(DPerson.class, json); Assert.assertEquals("first", bean.getFirstName()); diff --git a/src/test/java/com/avaje/tests/model/selfref/TestTextJsonSelfRef.java b/src/test/java/com/avaje/tests/model/selfref/TestTextJsonSelfRef.java index a65eb9d56..2e33211b7 100644 --- a/src/test/java/com/avaje/tests/model/selfref/TestTextJsonSelfRef.java +++ b/src/test/java/com/avaje/tests/model/selfref/TestTextJsonSelfRef.java @@ -47,7 +47,7 @@ public class TestTextJsonSelfRef extends BaseTestCase { } // JsonWriteOptions options = JsonWriteOptions.parsePath("(id,name,referredBy(id))"); -// String customerContent = Ebean.createJsonContext().toJsonString(customers);//, false, options); +// String customerContent = Ebean.createJsonContext().toJson(customers);//, false, options); // System.out.println("Customers: " + customerContent); // // Assert diff --git a/src/test/java/com/avaje/tests/text/json/TestJsonBeanWithTimeZone.java b/src/test/java/com/avaje/tests/text/json/TestJsonBeanWithTimeZone.java index 3cf09ca5a..73d0e79a2 100644 --- a/src/test/java/com/avaje/tests/text/json/TestJsonBeanWithTimeZone.java +++ b/src/test/java/com/avaje/tests/text/json/TestJsonBeanWithTimeZone.java @@ -31,8 +31,8 @@ public class TestJsonBeanWithTimeZone extends BaseTestCase { bean.setName("foo"); bean.setTimezone(TimeZone.getDefault()); - JsonContext jsonContext = Ebean.createJsonContext(); - String jsonContent = jsonContext.toJsonString(bean); + JsonContext jsonContext = Ebean.json(); + String jsonContent = jsonContext.toJson(bean); BeanWithTimeZone bean2 = jsonContext.toBean(BeanWithTimeZone.class, jsonContent); diff --git a/src/test/java/com/avaje/tests/text/json/TestJsonInheritanceDiscriminator.java b/src/test/java/com/avaje/tests/text/json/TestJsonInheritanceDiscriminator.java index 1db2dd62c..a0174a618 100644 --- a/src/test/java/com/avaje/tests/text/json/TestJsonInheritanceDiscriminator.java +++ b/src/test/java/com/avaje/tests/text/json/TestJsonInheritanceDiscriminator.java @@ -24,8 +24,8 @@ public class TestJsonInheritanceDiscriminator extends BaseTestCase { Ebean.save(cat); - JsonContext json = Ebean.createJsonContext(); - String jsonContent = json.toJsonString(cat); + JsonContext json = Ebean.json(); + String jsonContent = json.toJson(cat); Cat cat2 = json.toBean(Cat.class, jsonContent); @@ -37,9 +37,9 @@ public class TestJsonInheritanceDiscriminator extends BaseTestCase { Cat cat3 = json.toBean(Cat.class, noDiscriminator); - Assert.assertEquals(cat.getId(), cat3.getId()); - Assert.assertEquals(cat.getName(), cat3.getName()); - Assert.assertEquals(cat.getVersion(), cat3.getVersion()); + Assert.assertEquals(1L, cat3.getId().longValue()); + Assert.assertEquals("Gemma", cat3.getName()); + Assert.assertEquals(1L, cat3.getVersion().longValue()); Dog dog = new Dog(); dog.setRegistrationNumber("ABC123"); @@ -49,7 +49,7 @@ public class TestJsonInheritanceDiscriminator extends BaseTestCase { List animals = Ebean.find(Animal.class).findList(); - String listJson = json.toJsonString(animals); + String listJson = json.toJson(animals); List animals2 = json.toList(Animal.class, listJson); Assert.assertEquals(animals.size(), animals2.size()); diff --git a/src/test/java/com/avaje/tests/text/json/TestJsonMap.java b/src/test/java/com/avaje/tests/text/json/TestJsonMap.java index 92015dc2b..f408fd5c4 100644 --- a/src/test/java/com/avaje/tests/text/json/TestJsonMap.java +++ b/src/test/java/com/avaje/tests/text/json/TestJsonMap.java @@ -1,8 +1,11 @@ package com.avaje.tests.text.json; import java.io.IOException; +import java.util.List; import java.util.Map; +import com.avaje.ebean.Query; +import com.avaje.ebean.text.PathProperties; import org.junit.Test; import com.avaje.ebean.BaseTestCase; @@ -12,8 +15,29 @@ import com.avaje.ebean.text.json.JsonWriteOptions; import com.avaje.tests.model.basic.Customer; import com.avaje.tests.model.basic.ResetBasicData; +import static org.junit.Assert.assertTrue; + public class TestJsonMap extends BaseTestCase { + @Test + public void test_basic() throws IOException { + + ResetBasicData.reset(); + + List customers = Ebean.find(Customer.class).findList(); + + JsonContext jsonContext = Ebean.json(); + + JsonWriteOptions jsonWriteOptions = JsonWriteOptions.parsePath("(id,status,name)"); + String jsonString = jsonContext.toJson(customers, jsonWriteOptions); + assertTrue(jsonString.contains("{\"id\":1,\"status\":\"NEW\",\"name\":\"Rob\"}")); + + jsonWriteOptions = JsonWriteOptions.parsePath("status,name"); + jsonString = jsonContext.toJson(customers, jsonWriteOptions); + assertTrue(jsonString.contains("{\"status\":\"NEW\",\"name\":\"Rob\"}")); + + } + @Test public void test() throws IOException { @@ -21,19 +45,15 @@ public class TestJsonMap extends BaseTestCase { Map map = Ebean.find(Customer.class).findMap("id", String.class); - JsonContext jsonContext = Ebean.createJsonContext(); - JsonWriteOptions jsonWriteOptions = JsonWriteOptions.parsePath("(id,status,name)"); + JsonContext jsonContext = Ebean.json(); + JsonWriteOptions options = JsonWriteOptions.parsePath("(id,status,name)"); - String jsonString = jsonContext.toJsonString(map, jsonWriteOptions); + String jsonString = jsonContext.toJson(map, options); System.out.println(jsonString); - jsonContext = Ebean.createJsonContext(); - jsonWriteOptions = JsonWriteOptions - .parsePath("(id,status,name,shippingAddress(id,line1,city),billingAddress(*),contacts(*))"); - // jsonWriteOptions = - // JsonWriteOptions.parsePath("(id,status,billingAddress(*))"); + options = JsonWriteOptions.parsePath("(id,status,name,shippingAddress(id,line1,city),billingAddress(*),contacts(*))"); - jsonString = jsonContext.toJsonString(map, jsonWriteOptions); + jsonString = jsonContext.toJson(map, options); System.out.println(jsonString); // Assert.assertTrue(jsonString.indexOf("{\"1\":") > -1); @@ -41,4 +61,26 @@ public class TestJsonMap extends BaseTestCase { // > -1); } + + @Test + public void test_applyPathToQuery() throws IOException { + + ResetBasicData.reset(); + + PathProperties pathProperties = PathProperties.parse("(id,status,name,shippingAddress(id,line1,city),billingAddress(*),contacts(*))"); + + Query customerQuery = Ebean.find(Customer.class); + pathProperties.apply(customerQuery); + List customers = customerQuery.findList(); + + JsonWriteOptions options = JsonWriteOptions.parsePath("(id,status,name)"); + + String jsonString = Ebean.json().toJson(customers, options); + System.out.println(jsonString); + + + // Assert.assertTrue(jsonString.indexOf("{\"1\":") > -1); + // Assert.assertTrue(jsonString.indexOf("{\"id\":1,\"status\":\"NEW\",\"name\":\"Rob\"},") + // > -1); + } } diff --git a/src/test/java/com/avaje/tests/text/json/TestJsonSimple.java b/src/test/java/com/avaje/tests/text/json/TestJsonSimple.java index d2e991661..d2aa86e16 100644 --- a/src/test/java/com/avaje/tests/text/json/TestJsonSimple.java +++ b/src/test/java/com/avaje/tests/text/json/TestJsonSimple.java @@ -49,8 +49,8 @@ public class TestJsonSimple extends BaseTestCase { m.put("hello", "rob"); m.put("test", "me"); - JsonContext jsonContext = Ebean.createJsonContext(); - String jsonString = jsonContext.toJsonString(m); + JsonContext jsonContext = Ebean.json(); + String jsonString = jsonContext.toJson(m); System.out.println(jsonString); String s = "{\"parishId\":\"18\",\"contentId\":null,\"contentStatus\":null,\"contentType\":\"pg-hello\",\"content\":\"asd\"}"; diff --git a/src/test/java/com/avaje/tests/text/json/TestJsonSomeEnumWithToString.java b/src/test/java/com/avaje/tests/text/json/TestJsonSomeEnumWithToString.java index 46a075b85..8c9c1709f 100644 --- a/src/test/java/com/avaje/tests/text/json/TestJsonSomeEnumWithToString.java +++ b/src/test/java/com/avaje/tests/text/json/TestJsonSomeEnumWithToString.java @@ -21,8 +21,8 @@ public class TestJsonSomeEnumWithToString extends BaseTestCase { bean.setName("Some name"); bean.setSomeEnum(SomeEnum.ALPHA); - JsonContext json = Ebean.createJsonContext(); - String jsonContent = json.toJsonString(bean); + JsonContext json = Ebean.json(); + String jsonContent = json.toJson(bean); SomeEnumBean bean2 = json.toBean(SomeEnumBean.class, jsonContent); diff --git a/src/test/java/com/avaje/tests/text/json/TestTextJsonBeanReadVisitor.java b/src/test/java/com/avaje/tests/text/json/TestTextJsonBeanReadVisitor.java index 6a9c4f4f7..57b966cf9 100644 --- a/src/test/java/com/avaje/tests/text/json/TestTextJsonBeanReadVisitor.java +++ b/src/test/java/com/avaje/tests/text/json/TestTextJsonBeanReadVisitor.java @@ -27,10 +27,10 @@ public class TestTextJsonBeanReadVisitor extends BaseTestCase { .fetch("contacts", "firstName,email") .order().desc("id").findList(); - JsonContext json = Ebean.createJsonContext(); + JsonContext json = Ebean.json(); - String s = json.toJsonString(list); + String s = json.toJson(list); System.out.println(s); List mList = json.toList(Customer.class, s); diff --git a/src/test/java/com/avaje/tests/text/json/TestTextJsonCompoundType.java b/src/test/java/com/avaje/tests/text/json/TestTextJsonCompoundType.java index 985ea6a64..67f008149 100644 --- a/src/test/java/com/avaje/tests/text/json/TestTextJsonCompoundType.java +++ b/src/test/java/com/avaje/tests/text/json/TestTextJsonCompoundType.java @@ -31,7 +31,7 @@ public class TestTextJsonCompoundType extends BaseTestCase { // // JsonContext jsonContext = Ebean.createJsonContext(); // -// String jsonString = jsonContext.toJsonString(p, true); +// String jsonString = jsonContext.toJson(p, true); // System.out.println(jsonString); // // CMoney cm = new CMoney(new Money("12"), NZD); @@ -43,7 +43,7 @@ public class TestTextJsonCompoundType extends BaseTestCase { // ep.setOid(new Oid(112)); // ep.setExhange(exh); // -// String jsonString0 = jsonContext.toJsonString(ep, true); +// String jsonString0 = jsonContext.toJson(ep, true); // System.out.println(jsonString0); // // DExhEntity bean0 = jsonContext.toBean(DExhEntity.class, jsonString0); diff --git a/src/test/java/com/avaje/tests/text/json/TestTextJsonInheritance.java b/src/test/java/com/avaje/tests/text/json/TestTextJsonInheritance.java index b33a70585..8a0516cfd 100644 --- a/src/test/java/com/avaje/tests/text/json/TestTextJsonInheritance.java +++ b/src/test/java/com/avaje/tests/text/json/TestTextJsonInheritance.java @@ -26,8 +26,8 @@ public class TestTextJsonInheritance extends BaseTestCase { Assert.assertEquals(2, list.size()); - JsonContext jsonContext = Ebean.createJsonContext(); - String jsonString = jsonContext.toJsonString(list); + JsonContext jsonContext = Ebean.json(); + String jsonString = jsonContext.toJson(list); System.out.println(jsonString); List rebuiltList = jsonContext.toList(Vehicle.class, jsonString); diff --git a/src/test/java/com/avaje/tests/text/json/TestTextJsonInsertUpdate.java b/src/test/java/com/avaje/tests/text/json/TestTextJsonInsertUpdate.java index 1ffaffd87..c32dc132c 100644 --- a/src/test/java/com/avaje/tests/text/json/TestTextJsonInsertUpdate.java +++ b/src/test/java/com/avaje/tests/text/json/TestTextJsonInsertUpdate.java @@ -20,14 +20,14 @@ public class TestTextJsonInsertUpdate extends BaseTestCase { String json0 = "{\"name\":\"InsJson\",\"status\":\"NEW\"}"; - JsonContext jsonContext = Ebean.createJsonContext(); + JsonContext jsonContext = Ebean.json(); // insert Customer c0 = jsonContext.toBean(Customer.class, json0); Ebean.save(c0); // update with optimistic concurrency checking - String j0 = jsonContext.toJsonString(c0); + String j0 = jsonContext.toJson(c0); String j1 = StringHelper.replaceString(j0, "InsJson", "Mod1"); Customer c1 = jsonContext.toBean(Customer.class, j1); Ebean.update(c1); diff --git a/src/test/java/com/avaje/tests/text/json/TestTextJsonInvokeLazy.java b/src/test/java/com/avaje/tests/text/json/TestTextJsonInvokeLazy.java index 35e38c28e..80d518de7 100644 --- a/src/test/java/com/avaje/tests/text/json/TestTextJsonInvokeLazy.java +++ b/src/test/java/com/avaje/tests/text/json/TestTextJsonInvokeLazy.java @@ -21,11 +21,10 @@ public class TestTextJsonInvokeLazy extends BaseTestCase { List list = Ebean.find(Customer.class).select("name").findList(); - JsonWriteOptions opt = new JsonWriteOptions(); - opt.setRootPathProperties("name, status"); + JsonWriteOptions opt = JsonWriteOptions.parsePath("name, status"); - JsonContext jsonContext = Ebean.createJsonContext(); - String jsonString = jsonContext.toJsonString(list, opt); + JsonContext jsonContext = Ebean.json(); + String jsonString = jsonContext.toJson(list, opt); System.out.println(jsonString); diff --git a/src/test/java/com/avaje/tests/text/json/TestTextJsonReadManyLazyLoad.java b/src/test/java/com/avaje/tests/text/json/TestTextJsonReadManyLazyLoad.java index 04838c623..f30ad6905 100644 --- a/src/test/java/com/avaje/tests/text/json/TestTextJsonReadManyLazyLoad.java +++ b/src/test/java/com/avaje/tests/text/json/TestTextJsonReadManyLazyLoad.java @@ -24,7 +24,7 @@ public class TestTextJsonReadManyLazyLoad extends BaseTestCase { List list = Ebean.find(Customer.class).select("id, status, shippingAddress").order() .desc("id").findList(); - JsonContext json = Ebean.createJsonContext(); + JsonContext json = Ebean.json(); // test that lazy loading on PathProperties pp = PathProperties.parse("(id,name,contacts(firstName))"); @@ -32,7 +32,7 @@ public class TestTextJsonReadManyLazyLoad extends BaseTestCase { o.setPathProperties(pp); System.out.println("Expect lazy loading of Customer beans and customer contacts"); - String s = json.toJsonString(list, o); + String s = json.toJson(list, o); System.out.println(s); Assert.assertTrue(s.contains("\"contacts\"")); Assert.assertTrue(s.contains("\"name\"")); @@ -47,7 +47,7 @@ public class TestTextJsonReadManyLazyLoad extends BaseTestCase { List list = Ebean.find(Customer.class).select("id, status, shippingAddress") .fetch("contacts").order().desc("id").findList(); - JsonContext json = Ebean.createJsonContext(); + JsonContext json = Ebean.json(); // test that lazy loading on PathProperties pp = PathProperties.parse("(id,name,contacts(firstName))"); @@ -55,7 +55,7 @@ public class TestTextJsonReadManyLazyLoad extends BaseTestCase { o.setPathProperties(pp); System.out.println("expecting lazy load of Customer beans to fetch customer name"); - String s = json.toJsonString(list, o); + String s = json.toJson(list, o); System.out.println(s); Assert.assertTrue(s.contains("\"contacts\"")); Assert.assertTrue(s.contains("\"name\"")); @@ -71,7 +71,7 @@ public class TestTextJsonReadManyLazyLoad extends BaseTestCase { // .fetch("contacts") .order().desc("id").findList(); - JsonContext json = Ebean.createJsonContext(); + JsonContext json = Ebean.json(); // test that lazy loading on PathProperties pp = PathProperties.parse("(id,name,contacts(firstName))"); @@ -79,7 +79,7 @@ public class TestTextJsonReadManyLazyLoad extends BaseTestCase { o.setPathProperties(pp); System.out.println("expecting lazy load of Customer contacts "); - String s = json.toJsonString(list, o); + String s = json.toJson(list, o); System.out.println(s); Assert.assertTrue(s.contains("\"contacts\"")); Assert.assertTrue(s.contains("\"name\"")); diff --git a/src/test/java/com/avaje/tests/text/json/TestTextJsonReferenceBean.java b/src/test/java/com/avaje/tests/text/json/TestTextJsonReferenceBean.java index 31908d689..43c0549d7 100644 --- a/src/test/java/com/avaje/tests/text/json/TestTextJsonReferenceBean.java +++ b/src/test/java/com/avaje/tests/text/json/TestTextJsonReferenceBean.java @@ -28,7 +28,7 @@ public class TestTextJsonReferenceBean extends BaseTestCase { SpiEbeanServer server = (SpiEbeanServer)Ebean.getServer(null); - JsonContext jsonContext = Ebean.createJsonContext(); + JsonContext jsonContext = Ebean.json(); Product product = Ebean.getReference(Product.class, 1); @@ -38,7 +38,7 @@ public class TestTextJsonReferenceBean extends BaseTestCase { } else { - String jsonString = jsonContext.toJsonString(product); + String jsonString = jsonContext.toJson(product); System.out.println(jsonString); Product refProd = jsonContext.toBean(Product.class, jsonString); @@ -68,10 +68,9 @@ public class TestTextJsonReferenceBean extends BaseTestCase { Order order = orders.get(0); - JsonWriteOptions options = new JsonWriteOptions(); - options.setPathProperties("details.product", "id"); + JsonWriteOptions options = JsonWriteOptions.parsePath("*,details(id,orderQty,product(id))"); - String jsonOrder = jsonContext.toJsonString(order, options); + String jsonOrder = jsonContext.toJson(order, options); System.out.println(jsonOrder); Order o2 = jsonContext.toBean(Order.class, jsonOrder); diff --git a/src/test/java/com/avaje/tests/text/json/TestTextJsonSimple.java b/src/test/java/com/avaje/tests/text/json/TestTextJsonSimple.java index a2a5e1a91..ab2041638 100644 --- a/src/test/java/com/avaje/tests/text/json/TestTextJsonSimple.java +++ b/src/test/java/com/avaje/tests/text/json/TestTextJsonSimple.java @@ -1,17 +1,16 @@ package com.avaje.tests.text.json; -import java.io.IOException; -import java.util.List; - -import org.junit.Test; - import com.avaje.ebean.BaseTestCase; import com.avaje.ebean.Ebean; import com.avaje.ebean.EbeanServer; import com.avaje.ebean.text.json.JsonContext; -import com.avaje.ebean.text.json.JsonWriteOptions; import com.avaje.tests.model.basic.Customer; import com.avaje.tests.model.basic.ResetBasicData; +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.util.List; public class TestTextJsonSimple extends BaseTestCase { @@ -27,16 +26,13 @@ public class TestTextJsonSimple extends BaseTestCase { EbeanServer server = Ebean.getServer(null); - JsonContext json = server.createJsonContext(); + JsonContext json = server.json(); - JsonWriteOptions options = new JsonWriteOptions(); - options.setRootPathProperties("name, id"); - - String s = json.toJsonString(list); - System.out.println(s); + String jsonOutput = json.toJson(list); + System.out.println(jsonOutput); - List mList = json.toList(Customer.class, s); - System.out.println(mList); + List mList = json.toList(Customer.class, jsonOutput); + Assert.assertEquals(list.size(), mList.size()); } } diff --git a/src/test/java/com/avaje/tests/text/json/TestTextJsonSuperSimple.java b/src/test/java/com/avaje/tests/text/json/TestTextJsonSuperSimple.java index 18ef8c5ca..db054b7bd 100644 --- a/src/test/java/com/avaje/tests/text/json/TestTextJsonSuperSimple.java +++ b/src/test/java/com/avaje/tests/text/json/TestTextJsonSuperSimple.java @@ -25,12 +25,12 @@ public class TestTextJsonSuperSimple extends BaseTestCase { EbeanServer server = Ebean.getServer(null); - JsonContext json = server.createJsonContext(); + JsonContext json = server.json(); if (list.size() > 1) { Customer customer = list.get(0); - String s = json.toJsonString(customer); + String s = json.toJson(customer); System.out.println(s); int statusPos = s.indexOf("status"); Assert.assertEquals(-1, statusPos); diff --git a/src/test/java/com/avaje/tests/text/json/TestTextJsonUpdateCascade.java b/src/test/java/com/avaje/tests/text/json/TestTextJsonUpdateCascade.java index 75422693d..de94792d8 100644 --- a/src/test/java/com/avaje/tests/text/json/TestTextJsonUpdateCascade.java +++ b/src/test/java/com/avaje/tests/text/json/TestTextJsonUpdateCascade.java @@ -57,8 +57,8 @@ public class TestTextJsonUpdateCascade extends BaseTestCase { EbeanServer server = Ebean.getServer(null); - JsonContext jsonContext = server.createJsonContext(); - String jsonString = jsonContext.toJsonString(order); + JsonContext jsonContext = server.json(); + String jsonString = jsonContext.toJson(order); System.out.println(jsonString); Order updOrder = jsonContext.toBean(Order.class, jsonString); @@ -85,7 +85,7 @@ public class TestTextJsonUpdateCascade extends BaseTestCase { Ebean.save(u0); - String jsonUser = jsonContext.toJsonString(u0); + String jsonUser = jsonContext.toJson(u0); System.out.println(jsonUser); diff --git a/src/test/java/com/avaje/tests/update/TestJsonStatelessUpdate.java b/src/test/java/com/avaje/tests/update/TestJsonStatelessUpdate.java index 93539f917..ba536e5bb 100644 --- a/src/test/java/com/avaje/tests/update/TestJsonStatelessUpdate.java +++ b/src/test/java/com/avaje/tests/update/TestJsonStatelessUpdate.java @@ -30,10 +30,10 @@ public class TestJsonStatelessUpdate extends BaseTestCase { UUTwo twoX = Ebean.find(UUTwo.class, two.getId()); - JsonContext jsonContext = Ebean.createJsonContext(); + JsonContext jsonContext = Ebean.json(); JsonWriteOptions writeOptions = JsonWriteOptions.parsePath("(id,name,master(*))"); - String jsonString = jsonContext.toJsonString(twoX, writeOptions); + String jsonString = jsonContext.toJson(twoX, writeOptions); System.out.println(jsonString); jsonString = jsonString.replace("twoName", "twoNameModified");