CAY-1959 Chainable API for SelectQuery * merge method for PrefetchTreeNode for more comprehensive prefetch subtree merging * using the new merge in SelectQuery as well
UPGRADE NOTES: The most visible side effect of this change is that Property.xyzPrefetch() method will now return a root of the prefetch tree that can merged into the query instead of a leaf that can't. So in an unlikely case someone did MyEntity.MY_PROPERTY.disjoint().setSemantics(JOINT), the last part in this chain is not longer going to work. Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/4d8b2e1a Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/4d8b2e1a Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/4d8b2e1a Branch: refs/heads/CAY-1946 Commit: 4d8b2e1a6a6db08bd68ffab227381eb981111893 Parents: fb8660e Author: aadamchik <aadamc...@apache.org> Authored: Sun Nov 9 21:20:21 2014 +0300 Committer: aadamchik <aadamc...@apache.org> Committed: Sun Nov 9 21:24:50 2014 +0300 ---------------------------------------------------------------------- .../cayenne/query/PrefetchTreeNodeTest.java | 169 ++- .../java/org/apache/cayenne/exp/Property.java | 812 ++++++------- .../apache/cayenne/query/BaseQueryMetadata.java | 839 ++++++------- .../apache/cayenne/query/PrefetchTreeNode.java | 1106 +++++++++--------- .../org/apache/cayenne/query/SelectQuery.java | 7 +- ...ataContextDisjointByIdPrefetch_ExtrasIT.java | 1 + 6 files changed, 1546 insertions(+), 1388 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cayenne/blob/4d8b2e1a/cayenne-client/src/test/java/org/apache/cayenne/query/PrefetchTreeNodeTest.java ---------------------------------------------------------------------- diff --git a/cayenne-client/src/test/java/org/apache/cayenne/query/PrefetchTreeNodeTest.java b/cayenne-client/src/test/java/org/apache/cayenne/query/PrefetchTreeNodeTest.java index 90d4169..1dee8a9 100644 --- a/cayenne-client/src/test/java/org/apache/cayenne/query/PrefetchTreeNodeTest.java +++ b/cayenne-client/src/test/java/org/apache/cayenne/query/PrefetchTreeNodeTest.java @@ -18,51 +18,142 @@ ****************************************************************/ package org.apache.cayenne.query; -import org.apache.cayenne.map.EntityResolver; -import org.apache.cayenne.remote.hessian.service.HessianUtil; -import org.junit.Test; - import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import org.apache.cayenne.map.EntityResolver; +import org.apache.cayenne.remote.hessian.service.HessianUtil; +import org.junit.Test; public class PrefetchTreeNodeTest { - @Test - public void testTreeSerializationWithHessian() throws Exception { - PrefetchTreeNode n1 = new PrefetchTreeNode(); - PrefetchTreeNode n2 = n1.addPath("abc"); - - PrefetchTreeNode nc1 = (PrefetchTreeNode) HessianUtil.cloneViaClientServerSerialization(n1, - new EntityResolver()); - assertNotNull(nc1); - - PrefetchTreeNode nc2 = nc1.getNode("abc"); - assertNotNull(nc2); - assertNotSame(nc2, n2); - assertSame(nc1, nc2.getParent()); - assertEquals("abc", nc2.getName()); - } - - @Test - public void testSubtreeSerializationWithHessian() throws Exception { - PrefetchTreeNode n1 = new PrefetchTreeNode(); - PrefetchTreeNode n2 = n1.addPath("abc"); - PrefetchTreeNode n3 = n2.addPath("xyz"); - - // test that substree was serialized as independent tree, instead of - // sucking - PrefetchTreeNode nc2 = (PrefetchTreeNode) HessianUtil.cloneViaClientServerSerialization(n2, - new EntityResolver()); - assertNotNull(nc2); - assertNull(nc2.getParent()); - - PrefetchTreeNode nc3 = nc2.getNode("xyz"); - assertNotNull(nc3); - assertNotSame(nc3, n3); - assertSame(nc2, nc3.getParent()); - assertEquals("xyz", nc3.getName()); - } + @Test + public void testTreeSerializationWithHessian() throws Exception { + PrefetchTreeNode n1 = new PrefetchTreeNode(); + PrefetchTreeNode n2 = n1.addPath("abc"); + + PrefetchTreeNode nc1 = (PrefetchTreeNode) HessianUtil.cloneViaClientServerSerialization(n1, + new EntityResolver()); + assertNotNull(nc1); + + PrefetchTreeNode nc2 = nc1.getNode("abc"); + assertNotNull(nc2); + assertNotSame(nc2, n2); + assertSame(nc1, nc2.getParent()); + assertEquals("abc", nc2.getName()); + } + + @Test + public void testSubtreeSerializationWithHessian() throws Exception { + PrefetchTreeNode n1 = new PrefetchTreeNode(); + PrefetchTreeNode n2 = n1.addPath("abc"); + PrefetchTreeNode n3 = n2.addPath("xyz"); + + // test that substree was serialized as independent tree, instead of + // sucking + PrefetchTreeNode nc2 = (PrefetchTreeNode) HessianUtil.cloneViaClientServerSerialization(n2, + new EntityResolver()); + assertNotNull(nc2); + assertNull(nc2.getParent()); + + PrefetchTreeNode nc3 = nc2.getNode("xyz"); + assertNotNull(nc3); + assertNotSame(nc3, n3); + assertSame(nc2, nc3.getParent()); + assertEquals("xyz", nc3.getName()); + } + + @Test + public void testMerge() { + PrefetchTreeNode original = new PrefetchTreeNode(); + original.addPath("a").setPhantom(true); + original.addPath("a.b").setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS); + original.addPath("a.b").setPhantom(false); + original.addPath("c").setSemantics(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS); + original.addPath("c").setPhantom(false); + original.addPath("f").setSemantics(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS); + original.addPath("f").setPhantom(false); + + PrefetchTreeNode toMerge = new PrefetchTreeNode(); + toMerge.addPath("a").setPhantom(false); + toMerge.addPath("a.b").setSemantics(PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS); + toMerge.addPath("d.e").setSemantics(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS); + toMerge.addPath("d.e").setPhantom(false); + toMerge.addPath("c").setSemantics(PrefetchTreeNode.UNDEFINED_SEMANTICS); + + original.merge(toMerge); + + assertSame(original, original.getRoot()); + assertEquals(4, original.getChildren().size()); + + PrefetchTreeNode mergedA = original.getChild("a"); + assertEquals(1, mergedA.getChildren().size()); + assertFalse("Phantom flag wasn't turned off", mergedA.isPhantom()); + assertEquals(PrefetchTreeNode.UNDEFINED_SEMANTICS, mergedA.getSemantics()); + + PrefetchTreeNode mergedB = mergedA.getChild("b"); + assertEquals(0, mergedB.getChildren().size()); + assertFalse(mergedB.isPhantom()); + assertEquals("Semantics was't merged", PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS, + mergedB.getSemantics()); + + PrefetchTreeNode mergedC = original.getChild("c"); + assertEquals(0, mergedC.getChildren().size()); + assertFalse(mergedC.isPhantom()); + assertEquals("Semantics was overridden to undefined", PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS, + mergedC.getSemantics()); + + PrefetchTreeNode mergedD = original.getChild("d"); + assertEquals(1, mergedD.getChildren().size()); + assertTrue(mergedD.isPhantom()); + assertEquals(PrefetchTreeNode.UNDEFINED_SEMANTICS, mergedD.getSemantics()); + assertNotSame("Merged node wasn't cloned", toMerge.getChild("d"), mergedD); + + PrefetchTreeNode mergedE = mergedD.getChild("e"); + assertEquals(0, mergedE.getChildren().size()); + assertFalse(mergedE.isPhantom()); + assertEquals(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS, mergedE.getSemantics()); + + PrefetchTreeNode mergedF = original.getChild("f"); + assertEquals(0, mergedF.getChildren().size()); + assertFalse(mergedF.isPhantom()); + assertEquals(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS, mergedF.getSemantics()); + } + + @Test + public void testMerge_NonRoot() { + PrefetchTreeNode original = new PrefetchTreeNode(); + original.addPath("a").setPhantom(true); + original.addPath("a.b").setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS); + original.addPath("a.b").setPhantom(false); + + PrefetchTreeNode toMerge = new PrefetchTreeNode(null, "a.b.c"); + toMerge.setPhantom(false); + toMerge.setSemantics(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS); + + original.merge(toMerge); + + assertSame(original, original.getRoot()); + assertEquals(1, original.getChildren().size()); + + PrefetchTreeNode mergedA = original.getChild("a"); + assertEquals(1, mergedA.getChildren().size()); + assertTrue(mergedA.isPhantom()); + assertEquals(PrefetchTreeNode.UNDEFINED_SEMANTICS, mergedA.getSemantics()); + + PrefetchTreeNode mergedB = mergedA.getChild("b"); + assertEquals(1, mergedB.getChildren().size()); + assertFalse(mergedB.isPhantom()); + assertEquals(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS, mergedB.getSemantics()); + + PrefetchTreeNode mergedC = mergedB.getChild("c"); + assertEquals(0, mergedC.getChildren().size()); + assertFalse(mergedC.isPhantom()); + assertEquals(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS, mergedC.getSemantics()); + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/4d8b2e1a/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java index 999b346..48f1549 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java @@ -48,405 +48,417 @@ import org.apache.cayenne.reflect.PropertyUtils; */ public class Property<E> { - /** - * Name of the property in the object - */ - private final String name; - - /** - * Constructs a new property with the given name. - */ - public Property(String name) { - this.name = name; - } - - /** - * @return Name of the property in the object. - */ - public String getName() { - return name; - } - - @Override - public int hashCode() { - return getName().hashCode(); - } - - @Override - public boolean equals(Object obj) { - return obj instanceof Property && ((Property<?>) obj).getName().equals(getName()); - } - - /** - * @return Constructs a property path by appending the argument to the - * existing property separated by a dot - */ - public Property<Object> dot(String property) { - return new Property<Object>(getName() + "." + property); - } - - /** - * @return Constructs a property path by appending the argument to the - * existing property separated by a dot - */ - public <T> Property<T> dot(Property<T> property) { - return new Property<T>(getName() + "." + property.getName()); - } - - /** - * Returns a version of this property that represents an OUTER join. It is - * up to caller to ensure that the property corresponds to a relationship, - * as "outer" attributes make no sense. - */ - public Property<E> outer() { - return isOuter() ? this: new Property<E>(name + "+"); - } - - private boolean isOuter() { - return name.endsWith("+"); - } - - /** - * @return An expression representing null. - */ - public Expression isNull() { - return ExpressionFactory.matchExp(getName(), null); - } - - /** - * @return An expression representing a non-null value. - */ - public Expression isNotNull() { - return ExpressionFactory.matchExp(getName(), null).notExp(); - } - - /** - * @return An expression representing equality to TRUE. - */ - public Expression isTrue() { - return ExpressionFactory.matchExp(getName(), Boolean.TRUE); - } - - /** - * @return An expression representing equality to FALSE. - */ - public Expression isFalse() { - return ExpressionFactory.matchExp(getName(), Boolean.FALSE); - } - - /** - * @return An expression representing equality to a value. - */ - public Expression eq(E value) { - return ExpressionFactory.matchExp(getName(), value); - } - - /** - * @return An expression representing equality between two attributes - * (columns). - */ - public Expression eq(Property<?> value) { - return ExpressionFactory.matchExp(getName(), new ASTObjPath(value.getName())); - } - - /** - * @return An expression representing inequality to a value. - */ - public Expression ne(E value) { - return ExpressionFactory.noMatchExp(getName(), value); - } - - /** - * @return An expression representing inequality between two attributes - * (columns). - */ - public Expression ne(Property<?> value) { - return ExpressionFactory.noMatchExp(getName(), new ASTObjPath(value.getName())); - } - - /** - * @return An expression for a Database "Like" query. - */ - public Expression like(E value) { - return ExpressionFactory.likeExp(getName(), value); - } - - /** - * @return An expression for a case insensitive "Like" query. - */ - public Expression likeInsensitive(E value) { - return ExpressionFactory.likeIgnoreCaseExp(getName(), value); - } - - /** - * @return An expression for a Database "NOT LIKE" query. - */ - public Expression nlike(E value) { - return ExpressionFactory.notLikeExp(getName(), value); - } - - /** - * @return An expression for a case insensitive "NOT LIKE" query. - */ - public Expression nlikeInsensitive(E value) { - return ExpressionFactory.notLikeIgnoreCaseExp(getName(), value); - } - - /** - * @return An expression checking for objects between a lower and upper - * bound inclusive - * - * @param lower - * The lower bound. - * @param upper - * The upper bound. - */ - public Expression between(E lower, E upper) { - return ExpressionFactory.betweenExp(getName(), lower, upper); - } - - /** - * @return An expression for finding objects with values in the given set. - */ - public Expression in(E firstValue, E... moreValues) { - - int moreValuesLength = moreValues != null ? moreValues.length : 0; - - Object[] values = new Object[moreValuesLength + 1]; - values[0] = firstValue; - - if (moreValuesLength > 0) { - System.arraycopy(moreValues, 0, values, 1, moreValuesLength); - } - - return ExpressionFactory.inExp(getName(), values); - } - - /** - * @return An expression for finding objects with values not in the given - * set. - */ - public Expression nin(E firstValue, E... moreValues) { - - int moreValuesLength = moreValues != null ? moreValues.length : 0; - - Object[] values = new Object[moreValuesLength + 1]; - values[0] = firstValue; - - if (moreValuesLength > 0) { - System.arraycopy(moreValues, 0, values, 1, moreValuesLength); - } - - return ExpressionFactory.notInExp(getName(), values); - } - - /** - * @return An expression for finding objects with values in the given set. - */ - public Expression in(Collection<E> values) { - return ExpressionFactory.inExp(getName(), values); - } - - /** - * @return An expression for finding objects with values not in the given - * set. - */ - public Expression nin(Collection<E> values) { - return ExpressionFactory.notInExp(getName(), values); - } - - /** - * @return A greater than Expression. - */ - public Expression gt(E value) { - return ExpressionFactory.greaterExp(getName(), value); - } - - /** - * @return Represents a greater than relationship between two attributes - * (columns). - */ - public Expression gt(Property<?> value) { - return ExpressionFactory.greaterExp(getName(), new ASTObjPath(value.getName())); - } - - /** - * @return A greater than or equal to Expression. - */ - public Expression gte(E value) { - return ExpressionFactory.greaterOrEqualExp(getName(), value); - } - - /** - * @return Represents a greater than or equal relationship between two - * attributes (columns). - */ - public Expression gte(Property<?> value) { - return ExpressionFactory.greaterOrEqualExp(getName(), new ASTObjPath(value.getName())); - } - - /** - * @return A less than Expression. - */ - public Expression lt(E value) { - return ExpressionFactory.lessExp(getName(), value); - } - - /** - * @return Represents a less than relationship between two attributes - * (columns). - */ - public Expression lt(Property<?> value) { - return ExpressionFactory.lessExp(getName(), new ASTObjPath(value.getName())); - } - - /** - * @return A less than or equal to Expression. - */ - public Expression lte(E value) { - return ExpressionFactory.lessOrEqualExp(getName(), value); - } - - /** - * @return Represents a less than or equal relationship between two - * attributes (columns). - */ - public Expression lte(Property<?> value) { - return ExpressionFactory.lessOrEqualExp(getName(), new ASTObjPath(value.getName())); - } - - /** - * @return Ascending sort orderings on this property. - */ - public Ordering asc() { - return new Ordering(getName(), SortOrder.ASCENDING); - } - - /** - * @return Ascending sort orderings on this property. - */ - public List<Ordering> ascs() { - List<Ordering> result = new ArrayList<Ordering>(1); - result.add(asc()); - return result; - } - - /** - * @return Ascending case insensitive sort orderings on this property. - */ - public Ordering ascInsensitive() { - return new Ordering(getName(), SortOrder.ASCENDING_INSENSITIVE); - } - - /** - * @return Ascending case insensitive sort orderings on this property. - */ - public List<Ordering> ascInsensitives() { - List<Ordering> result = new ArrayList<Ordering>(1); - result.add(ascInsensitive()); - return result; - } - - /** - * @return Descending sort orderings on this property. - */ - public Ordering desc() { - return new Ordering(getName(), SortOrder.DESCENDING); - } - - /** - * @return Descending sort orderings on this property. - */ - public List<Ordering> descs() { - List<Ordering> result = new ArrayList<Ordering>(1); - result.add(desc()); - return result; - } - - /** - * @return Descending case insensitive sort orderings on this property. - */ - public Ordering descInsensitive() { - return new Ordering(getName(), SortOrder.DESCENDING_INSENSITIVE); - } - - /** - * @return Descending case insensitive sort orderings on this property. - */ - public List<Ordering> descInsensitives() { - List<Ordering> result = new ArrayList<Ordering>(1); - result.add(descInsensitive()); - return result; - } - - public PrefetchTreeNode joint() { - PrefetchTreeNode node = prefetch(); - node.setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS); - return node; - } - - public PrefetchTreeNode disjoint() { - PrefetchTreeNode node = prefetch(); - node.setSemantics(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS); - return node; - } - - public PrefetchTreeNode disjointById() { - PrefetchTreeNode node = prefetch(); - node.setSemantics(PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS); - return node; - } - - PrefetchTreeNode prefetch() { - - // TODO: not very efficient - we are creating a prefetch that - // SelectQuery would throw away and recreate... - PrefetchTreeNode root = new PrefetchTreeNode(); - PrefetchTreeNode node = root.addPath(name); - node.setPhantom(false); - return node; - } - - /** - * Extracts property value from an object using JavaBean-compatible - * introspection with one addition - a property can be a dot-separated - * property name path. - */ - @SuppressWarnings("unchecked") - public E getFrom(Object bean) { - return (E) PropertyUtils.getProperty(bean, getName()); - } - - /** - * Extracts property value from a collection of objects using - * JavaBean-compatible introspection with one addition - a property can be a - * dot-separated property name path. - */ - public List<E> getFromAll(Collection<?> beans) { - List<E> result = new ArrayList<E>(beans.size()); - for (Object bean : beans) { - result.add(getFrom(bean)); - } - return result; - } - - /** - * Sets a property value in 'obj' using JavaBean-compatible introspection - * with one addition - a property can be a dot-separated property name path. - */ - public void setIn(Object bean, E value) { - PropertyUtils.setProperty(bean, getName(), value); - } - - /** - * Sets a property value in a collection of objects using - * JavaBean-compatible introspection with one addition - a property can be a - * dot-separated property name path. - */ - public void setInAll(Collection<?> beans, E value) { - for (Object bean : beans) { - setIn(bean, value); - } - } + /** + * Name of the property in the object + */ + private final String name; + + /** + * Constructs a new property with the given name. + */ + public Property(String name) { + this.name = name; + } + + /** + * @return Name of the property in the object. + */ + public String getName() { + return name; + } + + @Override + public int hashCode() { + return getName().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof Property && ((Property<?>) obj).getName().equals(getName()); + } + + /** + * @return Constructs a property path by appending the argument to the + * existing property separated by a dot + */ + public Property<Object> dot(String property) { + return new Property<Object>(getName() + "." + property); + } + + /** + * @return Constructs a property path by appending the argument to the + * existing property separated by a dot + */ + public <T> Property<T> dot(Property<T> property) { + return new Property<T>(getName() + "." + property.getName()); + } + + /** + * Returns a version of this property that represents an OUTER join. It is + * up to caller to ensure that the property corresponds to a relationship, + * as "outer" attributes make no sense. + */ + public Property<E> outer() { + return isOuter() ? this : new Property<E>(name + "+"); + } + + private boolean isOuter() { + return name.endsWith("+"); + } + + /** + * @return An expression representing null. + */ + public Expression isNull() { + return ExpressionFactory.matchExp(getName(), null); + } + + /** + * @return An expression representing a non-null value. + */ + public Expression isNotNull() { + return ExpressionFactory.matchExp(getName(), null).notExp(); + } + + /** + * @return An expression representing equality to TRUE. + */ + public Expression isTrue() { + return ExpressionFactory.matchExp(getName(), Boolean.TRUE); + } + + /** + * @return An expression representing equality to FALSE. + */ + public Expression isFalse() { + return ExpressionFactory.matchExp(getName(), Boolean.FALSE); + } + + /** + * @return An expression representing equality to a value. + */ + public Expression eq(E value) { + return ExpressionFactory.matchExp(getName(), value); + } + + /** + * @return An expression representing equality between two attributes + * (columns). + */ + public Expression eq(Property<?> value) { + return ExpressionFactory.matchExp(getName(), new ASTObjPath(value.getName())); + } + + /** + * @return An expression representing inequality to a value. + */ + public Expression ne(E value) { + return ExpressionFactory.noMatchExp(getName(), value); + } + + /** + * @return An expression representing inequality between two attributes + * (columns). + */ + public Expression ne(Property<?> value) { + return ExpressionFactory.noMatchExp(getName(), new ASTObjPath(value.getName())); + } + + /** + * @return An expression for a Database "Like" query. + */ + public Expression like(E value) { + return ExpressionFactory.likeExp(getName(), value); + } + + /** + * @return An expression for a case insensitive "Like" query. + */ + public Expression likeInsensitive(E value) { + return ExpressionFactory.likeIgnoreCaseExp(getName(), value); + } + + /** + * @return An expression for a Database "NOT LIKE" query. + */ + public Expression nlike(E value) { + return ExpressionFactory.notLikeExp(getName(), value); + } + + /** + * @return An expression for a case insensitive "NOT LIKE" query. + */ + public Expression nlikeInsensitive(E value) { + return ExpressionFactory.notLikeIgnoreCaseExp(getName(), value); + } + + /** + * @return An expression checking for objects between a lower and upper + * bound inclusive + * + * @param lower + * The lower bound. + * @param upper + * The upper bound. + */ + public Expression between(E lower, E upper) { + return ExpressionFactory.betweenExp(getName(), lower, upper); + } + + /** + * @return An expression for finding objects with values in the given set. + */ + public Expression in(E firstValue, E... moreValues) { + + int moreValuesLength = moreValues != null ? moreValues.length : 0; + + Object[] values = new Object[moreValuesLength + 1]; + values[0] = firstValue; + + if (moreValuesLength > 0) { + System.arraycopy(moreValues, 0, values, 1, moreValuesLength); + } + + return ExpressionFactory.inExp(getName(), values); + } + + /** + * @return An expression for finding objects with values not in the given + * set. + */ + public Expression nin(E firstValue, E... moreValues) { + + int moreValuesLength = moreValues != null ? moreValues.length : 0; + + Object[] values = new Object[moreValuesLength + 1]; + values[0] = firstValue; + + if (moreValuesLength > 0) { + System.arraycopy(moreValues, 0, values, 1, moreValuesLength); + } + + return ExpressionFactory.notInExp(getName(), values); + } + + /** + * @return An expression for finding objects with values in the given set. + */ + public Expression in(Collection<E> values) { + return ExpressionFactory.inExp(getName(), values); + } + + /** + * @return An expression for finding objects with values not in the given + * set. + */ + public Expression nin(Collection<E> values) { + return ExpressionFactory.notInExp(getName(), values); + } + + /** + * @return A greater than Expression. + */ + public Expression gt(E value) { + return ExpressionFactory.greaterExp(getName(), value); + } + + /** + * @return Represents a greater than relationship between two attributes + * (columns). + */ + public Expression gt(Property<?> value) { + return ExpressionFactory.greaterExp(getName(), new ASTObjPath(value.getName())); + } + + /** + * @return A greater than or equal to Expression. + */ + public Expression gte(E value) { + return ExpressionFactory.greaterOrEqualExp(getName(), value); + } + + /** + * @return Represents a greater than or equal relationship between two + * attributes (columns). + */ + public Expression gte(Property<?> value) { + return ExpressionFactory.greaterOrEqualExp(getName(), new ASTObjPath(value.getName())); + } + + /** + * @return A less than Expression. + */ + public Expression lt(E value) { + return ExpressionFactory.lessExp(getName(), value); + } + + /** + * @return Represents a less than relationship between two attributes + * (columns). + */ + public Expression lt(Property<?> value) { + return ExpressionFactory.lessExp(getName(), new ASTObjPath(value.getName())); + } + + /** + * @return A less than or equal to Expression. + */ + public Expression lte(E value) { + return ExpressionFactory.lessOrEqualExp(getName(), value); + } + + /** + * @return Represents a less than or equal relationship between two + * attributes (columns). + */ + public Expression lte(Property<?> value) { + return ExpressionFactory.lessOrEqualExp(getName(), new ASTObjPath(value.getName())); + } + + /** + * @return Ascending sort orderings on this property. + */ + public Ordering asc() { + return new Ordering(getName(), SortOrder.ASCENDING); + } + + /** + * @return Ascending sort orderings on this property. + */ + public List<Ordering> ascs() { + List<Ordering> result = new ArrayList<Ordering>(1); + result.add(asc()); + return result; + } + + /** + * @return Ascending case insensitive sort orderings on this property. + */ + public Ordering ascInsensitive() { + return new Ordering(getName(), SortOrder.ASCENDING_INSENSITIVE); + } + + /** + * @return Ascending case insensitive sort orderings on this property. + */ + public List<Ordering> ascInsensitives() { + List<Ordering> result = new ArrayList<Ordering>(1); + result.add(ascInsensitive()); + return result; + } + + /** + * @return Descending sort orderings on this property. + */ + public Ordering desc() { + return new Ordering(getName(), SortOrder.DESCENDING); + } + + /** + * @return Descending sort orderings on this property. + */ + public List<Ordering> descs() { + List<Ordering> result = new ArrayList<Ordering>(1); + result.add(desc()); + return result; + } + + /** + * @return Descending case insensitive sort orderings on this property. + */ + public Ordering descInsensitive() { + return new Ordering(getName(), SortOrder.DESCENDING_INSENSITIVE); + } + + /** + * @return Descending case insensitive sort orderings on this property. + */ + public List<Ordering> descInsensitives() { + List<Ordering> result = new ArrayList<Ordering>(1); + result.add(descInsensitive()); + return result; + } + + /** + * Returns a prefetch tree that follows this property path, potentially + * spanning a number of phantom nodes, and having a single leaf with "joint" + * prefetch semantics. + */ + public PrefetchTreeNode joint() { + PrefetchTreeNode node = prefetch(); + node.setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS); + return node.getRoot(); + } + + /** + * Returns a prefetch tree that follows this property path, potentially + * spanning a number of phantom nodes, and having a single leaf with + * "disjoint" prefetch semantics. + */ + public PrefetchTreeNode disjoint() { + PrefetchTreeNode node = prefetch(); + node.setSemantics(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS); + return node.getRoot(); + } + + /** + * Returns a prefetch tree that follows this property path, potentially + * spanning a number of phantom nodes, and having a single leaf with + * "disjoint by id" prefetch semantics. + */ + public PrefetchTreeNode disjointById() { + PrefetchTreeNode node = prefetch(); + node.setSemantics(PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS); + return node.getRoot(); + } + + PrefetchTreeNode prefetch() { + PrefetchTreeNode root = new PrefetchTreeNode(); + PrefetchTreeNode node = root.addPath(name); + node.setPhantom(false); + return node; + } + + /** + * Extracts property value from an object using JavaBean-compatible + * introspection with one addition - a property can be a dot-separated + * property name path. + */ + @SuppressWarnings("unchecked") + public E getFrom(Object bean) { + return (E) PropertyUtils.getProperty(bean, getName()); + } + + /** + * Extracts property value from a collection of objects using + * JavaBean-compatible introspection with one addition - a property can be a + * dot-separated property name path. + */ + public List<E> getFromAll(Collection<?> beans) { + List<E> result = new ArrayList<E>(beans.size()); + for (Object bean : beans) { + result.add(getFrom(bean)); + } + return result; + } + + /** + * Sets a property value in 'obj' using JavaBean-compatible introspection + * with one addition - a property can be a dot-separated property name path. + */ + public void setIn(Object bean, E value) { + PropertyUtils.setProperty(bean, getName(), value); + } + + /** + * Sets a property value in a collection of objects using + * JavaBean-compatible introspection with one addition - a property can be a + * dot-separated property name path. + */ + public void setInAll(Collection<?> beans, E value) { + for (Object bean : beans) { + setIn(bean, value); + } + } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cayenne/blob/4d8b2e1a/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java b/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java index d97e811..7f7c6dd 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/query/BaseQueryMetadata.java @@ -43,417 +43,430 @@ import org.apache.cayenne.util.XMLSerializable; */ class BaseQueryMetadata implements QueryMetadata, XMLSerializable, Serializable { - int fetchLimit = QueryMetadata.FETCH_LIMIT_DEFAULT; - int fetchOffset = QueryMetadata.FETCH_OFFSET_DEFAULT; - - int statementFetchSize = QueryMetadata.FETCH_OFFSET_DEFAULT; - - int pageSize = QueryMetadata.PAGE_SIZE_DEFAULT; - boolean fetchingDataRows = QueryMetadata.FETCHING_DATA_ROWS_DEFAULT; - QueryCacheStrategy cacheStrategy = QueryCacheStrategy.getDefaultStrategy(); - - PrefetchTreeNode prefetchTree; - String cacheKey; - String[] cacheGroups; - - transient List<Object> resultSetMapping; - transient DbEntity dbEntity; - transient DataMap dataMap; - transient Object lastRoot; - transient ClassDescriptor classDescriptor; - transient EntityResolver lastEntityResolver; - - /** - * Copies values of another QueryMetadata object to this object. - */ - void copyFromInfo(QueryMetadata info) { - this.lastEntityResolver = null; - this.lastRoot = null; - this.classDescriptor = null; - this.dbEntity = null; - this.dataMap = null; - - this.fetchingDataRows = info.isFetchingDataRows(); - this.fetchLimit = info.getFetchLimit(); - this.pageSize = info.getPageSize(); - this.cacheStrategy = info.getCacheStrategy(); - this.cacheKey = info.getCacheKey(); - this.cacheGroups = info.getCacheGroups(); - this.resultSetMapping = info.getResultSetMapping(); - - setPrefetchTree(info.getPrefetchTree()); - } - - boolean resolve(Object root, EntityResolver resolver, String cacheKey) { - - if (lastRoot != root || lastEntityResolver != resolver) { - - this.cacheKey = cacheKey; - - this.classDescriptor = null; - this.dbEntity = null; - this.dataMap = null; - - ObjEntity entity = null; - - if (root != null) { - if (root instanceof Class<?>) { - entity = resolver.getObjEntity((Class<?>) root); - if (entity == null) { // entity not found, try to resolve it - // with - // client resolver - EntityResolver clientResolver = resolver.getClientEntityResolver(); - if (clientResolver != resolver) { - ObjEntity clientEntity = clientResolver.getObjEntity((Class<?>) root); - - if (clientEntity != null) { - entity = resolver.getObjEntity(clientEntity.getName()); - } - } - } - - if (entity != null) { - this.dbEntity = entity.getDbEntity(); - this.dataMap = entity.getDataMap(); - } - } else if (root instanceof ObjEntity) { - entity = (ObjEntity) root; - this.dbEntity = entity.getDbEntity(); - this.dataMap = entity.getDataMap(); - } else if (root instanceof String) { - entity = resolver.getObjEntity((String) root); - if (entity != null) { - this.dbEntity = entity.getDbEntity(); - this.dataMap = entity.getDataMap(); - } - } else if (root instanceof DbEntity) { - this.dbEntity = (DbEntity) root; - this.dataMap = dbEntity.getDataMap(); - } else if (root instanceof DataMap) { - this.dataMap = (DataMap) root; - } else if (root instanceof Persistent) { - entity = resolver.getObjEntity((Persistent) root); - if (entity != null) { - this.dbEntity = entity.getDbEntity(); - this.dataMap = entity.getDataMap(); - } - } - } - - if (entity != null) { - this.classDescriptor = resolver.getClassDescriptor(entity.getName()); - } - - this.lastRoot = root; - this.lastEntityResolver = resolver; - - return true; - } - - return false; - } - - void initWithProperties(Map<String, ?> properties) { - // must init defaults even if properties are empty - if (properties == null) { - properties = Collections.EMPTY_MAP; - } - - Object fetchOffset = properties.get(QueryMetadata.FETCH_OFFSET_PROPERTY); - Object fetchLimit = properties.get(QueryMetadata.FETCH_LIMIT_PROPERTY); - Object pageSize = properties.get(QueryMetadata.PAGE_SIZE_PROPERTY); - Object statementFetchSize = properties.get(QueryMetadata.STATEMENT_FETCH_SIZE_PROPERTY); - Object fetchingDataRows = properties.get(QueryMetadata.FETCHING_DATA_ROWS_PROPERTY); - - Object cacheStrategy = properties.get(QueryMetadata.CACHE_STRATEGY_PROPERTY); - - Object cacheGroups = properties.get(QueryMetadata.CACHE_GROUPS_PROPERTY); - - // init ivars from properties - this.fetchOffset = (fetchOffset != null) ? Integer.parseInt(fetchOffset.toString()) - : QueryMetadata.FETCH_OFFSET_DEFAULT; - - this.fetchLimit = (fetchLimit != null) ? Integer.parseInt(fetchLimit.toString()) - : QueryMetadata.FETCH_LIMIT_DEFAULT; - - this.pageSize = (pageSize != null) ? Integer.parseInt(pageSize.toString()) : QueryMetadata.PAGE_SIZE_DEFAULT; - - this.statementFetchSize = (statementFetchSize != null) ? Integer.parseInt(statementFetchSize.toString()) - : QueryMetadata.STATEMENT_FETCH_SIZE_DEFAULT; - - this.fetchingDataRows = (fetchingDataRows != null) ? "true".equalsIgnoreCase(fetchingDataRows.toString()) - : QueryMetadata.FETCHING_DATA_ROWS_DEFAULT; - - this.cacheStrategy = (cacheStrategy != null) ? QueryCacheStrategy.safeValueOf(cacheStrategy.toString()) - : QueryCacheStrategy.getDefaultStrategy(); - - this.cacheGroups = null; - if (cacheGroups instanceof String[]) { - this.cacheGroups = (String[]) cacheGroups; - } else if (cacheGroups instanceof String) { - StringTokenizer toks = new StringTokenizer(cacheGroups.toString(), ","); - this.cacheGroups = new String[toks.countTokens()]; - for (int i = 0; i < this.cacheGroups.length; i++) { - this.cacheGroups[i] = toks.nextToken(); - } - } - } - - public void encodeAsXML(XMLEncoder encoder) { - - if (fetchingDataRows != QueryMetadata.FETCHING_DATA_ROWS_DEFAULT) { - encoder.printProperty(QueryMetadata.FETCHING_DATA_ROWS_PROPERTY, fetchingDataRows); - } - - if (fetchOffset != QueryMetadata.FETCH_OFFSET_DEFAULT) { - encoder.printProperty(QueryMetadata.FETCH_OFFSET_PROPERTY, fetchOffset); - } - - if (fetchLimit != QueryMetadata.FETCH_LIMIT_DEFAULT) { - encoder.printProperty(QueryMetadata.FETCH_LIMIT_PROPERTY, fetchLimit); - } - - if (pageSize != QueryMetadata.PAGE_SIZE_DEFAULT) { - encoder.printProperty(QueryMetadata.PAGE_SIZE_PROPERTY, pageSize); - } - - if (cacheStrategy != null && QueryCacheStrategy.getDefaultStrategy() != cacheStrategy) { - encoder.printProperty(QueryMetadata.CACHE_STRATEGY_PROPERTY, cacheStrategy.name()); - } - - if (statementFetchSize != QueryMetadata.STATEMENT_FETCH_SIZE_DEFAULT) { - encoder.printProperty(QueryMetadata.STATEMENT_FETCH_SIZE_PROPERTY, statementFetchSize); - } - - if (prefetchTree != null) { - prefetchTree.encodeAsXML(encoder); - } - - if (cacheGroups != null && cacheGroups.length > 0) { - StringBuilder buffer = new StringBuilder(cacheGroups[0]); - for (int i = 1; i < cacheGroups.length; i++) { - buffer.append(',').append(cacheGroups[i]); - } - encoder.printProperty(QueryMetadata.CACHE_GROUPS_PROPERTY, buffer.toString()); - } - } - - /** - * @since 1.2 - */ - public String getCacheKey() { - return cacheKey; - } - - /** - * @since 1.2 - */ - public DataMap getDataMap() { - return dataMap; - } - - /** - * @since 1.2 - */ - public Procedure getProcedure() { - return null; - } - - /** - * @since 3.0 - */ - public Map<String, String> getPathSplitAliases() { - return Collections.emptyMap(); - } - - /** - * @since 1.2 - */ - public DbEntity getDbEntity() { - return dbEntity; - } - - /** - * @since 1.2 - */ - public ObjEntity getObjEntity() { - return classDescriptor != null ? classDescriptor.getEntity() : null; - } - - /** - * @since 3.0 - */ - public ClassDescriptor getClassDescriptor() { - return classDescriptor; - } - - /** - * @since 3.0 - */ - public List<Object> getResultSetMapping() { - return resultSetMapping; - } - - /** - * @since 1.2 - */ - public PrefetchTreeNode getPrefetchTree() { - return prefetchTree; - } - - void setPrefetchTree(PrefetchTreeNode prefetchTree) { - this.prefetchTree = prefetchTree != null ? deepClone(prefetchTree, null) : null; - } - - private PrefetchTreeNode deepClone(PrefetchTreeNode source, PrefetchTreeNode targetParent) { - - PrefetchTreeNode target = new PrefetchTreeNode(targetParent, source.getName()); - target.setEjbqlPathEntityId(source.getEjbqlPathEntityId()); - target.setEntityName(source.getEntityName()); - target.setPhantom(source.isPhantom()); - target.setSemantics(source.getSemantics()); - - for (PrefetchTreeNode child : source.getChildren()) { - target.addChild(deepClone(child, target)); - } - - return target; - } - - /** - * @since 3.0 - */ - public QueryCacheStrategy getCacheStrategy() { - return cacheStrategy; - } - - /** - * @since 3.0 - */ - void setCacheStrategy(QueryCacheStrategy cacheStrategy) { - this.cacheStrategy = cacheStrategy; - } - - /** - * @since 3.0 - */ - public String[] getCacheGroups() { - return cacheGroups; - } - - /** - * @since 3.0 - */ - void setCacheGroups(String... groups) { - this.cacheGroups = groups; - } - - public boolean isFetchingDataRows() { - return fetchingDataRows; - } - - public int getFetchLimit() { - return fetchLimit; - } - - public int getPageSize() { - return pageSize; - } - - public Query getOrginatingQuery() { - return null; - } - - /** - * @since 3.0 - */ - public int getFetchOffset() { - return fetchOffset; - } - - public boolean isRefreshingObjects() { - return true; - } - - void setFetchingDataRows(boolean b) { - fetchingDataRows = b; - } - - void setFetchLimit(int i) { - fetchLimit = i; - } - - void setFetchOffset(int i) { - fetchOffset = i; - } - - void setPageSize(int i) { - pageSize = i; - } - - /** - * Sets statement's fetch size (0 for no default size) - * - * @since 3.0 - */ - void setStatementFetchSize(int size) { - this.statementFetchSize = size; - } - - /** - * @return statement's fetch size - * @since 3.0 - */ - public int getStatementFetchSize() { - return statementFetchSize; - } - - /** - * Adds a joint prefetch. - * - * @since 1.2 - */ - PrefetchTreeNode addPrefetch(String path, int semantics) { - if (prefetchTree == null) { - prefetchTree = new PrefetchTreeNode(); - } - - PrefetchTreeNode node = prefetchTree.addPath(path); - node.setSemantics(semantics); - node.setPhantom(false); - return node; - } - - /** - * Adds all prefetches from a provided collection. - * - * @since 1.2 - */ - void addPrefetches(Collection<String> prefetches, int semantics) { - if (prefetches != null) { - for (String prefetch : prefetches) { - addPrefetch(prefetch, semantics); - } - } - } - - /** - * Clears all joint prefetches. - * - * @since 1.2 - */ - void clearPrefetches() { - prefetchTree = null; - } - - /** - * Removes joint prefetch. - * - * @since 1.2 - */ - void removePrefetch(String prefetch) { - if (prefetchTree != null) { - prefetchTree.removePath(prefetch); - } - } + int fetchLimit = QueryMetadata.FETCH_LIMIT_DEFAULT; + int fetchOffset = QueryMetadata.FETCH_OFFSET_DEFAULT; + + int statementFetchSize = QueryMetadata.FETCH_OFFSET_DEFAULT; + + int pageSize = QueryMetadata.PAGE_SIZE_DEFAULT; + boolean fetchingDataRows = QueryMetadata.FETCHING_DATA_ROWS_DEFAULT; + QueryCacheStrategy cacheStrategy = QueryCacheStrategy.getDefaultStrategy(); + + PrefetchTreeNode prefetchTree; + String cacheKey; + String[] cacheGroups; + + transient List<Object> resultSetMapping; + transient DbEntity dbEntity; + transient DataMap dataMap; + transient Object lastRoot; + transient ClassDescriptor classDescriptor; + transient EntityResolver lastEntityResolver; + + /** + * Copies values of another QueryMetadata object to this object. + */ + void copyFromInfo(QueryMetadata info) { + this.lastEntityResolver = null; + this.lastRoot = null; + this.classDescriptor = null; + this.dbEntity = null; + this.dataMap = null; + + this.fetchingDataRows = info.isFetchingDataRows(); + this.fetchLimit = info.getFetchLimit(); + this.pageSize = info.getPageSize(); + this.cacheStrategy = info.getCacheStrategy(); + this.cacheKey = info.getCacheKey(); + this.cacheGroups = info.getCacheGroups(); + this.resultSetMapping = info.getResultSetMapping(); + + setPrefetchTree(info.getPrefetchTree()); + } + + boolean resolve(Object root, EntityResolver resolver, String cacheKey) { + + if (lastRoot != root || lastEntityResolver != resolver) { + + this.cacheKey = cacheKey; + + this.classDescriptor = null; + this.dbEntity = null; + this.dataMap = null; + + ObjEntity entity = null; + + if (root != null) { + if (root instanceof Class<?>) { + entity = resolver.getObjEntity((Class<?>) root); + if (entity == null) { // entity not found, try to resolve it + // with + // client resolver + EntityResolver clientResolver = resolver.getClientEntityResolver(); + if (clientResolver != resolver) { + ObjEntity clientEntity = clientResolver.getObjEntity((Class<?>) root); + + if (clientEntity != null) { + entity = resolver.getObjEntity(clientEntity.getName()); + } + } + } + + if (entity != null) { + this.dbEntity = entity.getDbEntity(); + this.dataMap = entity.getDataMap(); + } + } else if (root instanceof ObjEntity) { + entity = (ObjEntity) root; + this.dbEntity = entity.getDbEntity(); + this.dataMap = entity.getDataMap(); + } else if (root instanceof String) { + entity = resolver.getObjEntity((String) root); + if (entity != null) { + this.dbEntity = entity.getDbEntity(); + this.dataMap = entity.getDataMap(); + } + } else if (root instanceof DbEntity) { + this.dbEntity = (DbEntity) root; + this.dataMap = dbEntity.getDataMap(); + } else if (root instanceof DataMap) { + this.dataMap = (DataMap) root; + } else if (root instanceof Persistent) { + entity = resolver.getObjEntity((Persistent) root); + if (entity != null) { + this.dbEntity = entity.getDbEntity(); + this.dataMap = entity.getDataMap(); + } + } + } + + if (entity != null) { + this.classDescriptor = resolver.getClassDescriptor(entity.getName()); + } + + this.lastRoot = root; + this.lastEntityResolver = resolver; + + return true; + } + + return false; + } + + void initWithProperties(Map<String, ?> properties) { + // must init defaults even if properties are empty + if (properties == null) { + properties = Collections.EMPTY_MAP; + } + + Object fetchOffset = properties.get(QueryMetadata.FETCH_OFFSET_PROPERTY); + Object fetchLimit = properties.get(QueryMetadata.FETCH_LIMIT_PROPERTY); + Object pageSize = properties.get(QueryMetadata.PAGE_SIZE_PROPERTY); + Object statementFetchSize = properties.get(QueryMetadata.STATEMENT_FETCH_SIZE_PROPERTY); + Object fetchingDataRows = properties.get(QueryMetadata.FETCHING_DATA_ROWS_PROPERTY); + + Object cacheStrategy = properties.get(QueryMetadata.CACHE_STRATEGY_PROPERTY); + + Object cacheGroups = properties.get(QueryMetadata.CACHE_GROUPS_PROPERTY); + + // init ivars from properties + this.fetchOffset = (fetchOffset != null) ? Integer.parseInt(fetchOffset.toString()) + : QueryMetadata.FETCH_OFFSET_DEFAULT; + + this.fetchLimit = (fetchLimit != null) ? Integer.parseInt(fetchLimit.toString()) + : QueryMetadata.FETCH_LIMIT_DEFAULT; + + this.pageSize = (pageSize != null) ? Integer.parseInt(pageSize.toString()) : QueryMetadata.PAGE_SIZE_DEFAULT; + + this.statementFetchSize = (statementFetchSize != null) ? Integer.parseInt(statementFetchSize.toString()) + : QueryMetadata.STATEMENT_FETCH_SIZE_DEFAULT; + + this.fetchingDataRows = (fetchingDataRows != null) ? "true".equalsIgnoreCase(fetchingDataRows.toString()) + : QueryMetadata.FETCHING_DATA_ROWS_DEFAULT; + + this.cacheStrategy = (cacheStrategy != null) ? QueryCacheStrategy.safeValueOf(cacheStrategy.toString()) + : QueryCacheStrategy.getDefaultStrategy(); + + this.cacheGroups = null; + if (cacheGroups instanceof String[]) { + this.cacheGroups = (String[]) cacheGroups; + } else if (cacheGroups instanceof String) { + StringTokenizer toks = new StringTokenizer(cacheGroups.toString(), ","); + this.cacheGroups = new String[toks.countTokens()]; + for (int i = 0; i < this.cacheGroups.length; i++) { + this.cacheGroups[i] = toks.nextToken(); + } + } + } + + public void encodeAsXML(XMLEncoder encoder) { + + if (fetchingDataRows != QueryMetadata.FETCHING_DATA_ROWS_DEFAULT) { + encoder.printProperty(QueryMetadata.FETCHING_DATA_ROWS_PROPERTY, fetchingDataRows); + } + + if (fetchOffset != QueryMetadata.FETCH_OFFSET_DEFAULT) { + encoder.printProperty(QueryMetadata.FETCH_OFFSET_PROPERTY, fetchOffset); + } + + if (fetchLimit != QueryMetadata.FETCH_LIMIT_DEFAULT) { + encoder.printProperty(QueryMetadata.FETCH_LIMIT_PROPERTY, fetchLimit); + } + + if (pageSize != QueryMetadata.PAGE_SIZE_DEFAULT) { + encoder.printProperty(QueryMetadata.PAGE_SIZE_PROPERTY, pageSize); + } + + if (cacheStrategy != null && QueryCacheStrategy.getDefaultStrategy() != cacheStrategy) { + encoder.printProperty(QueryMetadata.CACHE_STRATEGY_PROPERTY, cacheStrategy.name()); + } + + if (statementFetchSize != QueryMetadata.STATEMENT_FETCH_SIZE_DEFAULT) { + encoder.printProperty(QueryMetadata.STATEMENT_FETCH_SIZE_PROPERTY, statementFetchSize); + } + + if (prefetchTree != null) { + prefetchTree.encodeAsXML(encoder); + } + + if (cacheGroups != null && cacheGroups.length > 0) { + StringBuilder buffer = new StringBuilder(cacheGroups[0]); + for (int i = 1; i < cacheGroups.length; i++) { + buffer.append(',').append(cacheGroups[i]); + } + encoder.printProperty(QueryMetadata.CACHE_GROUPS_PROPERTY, buffer.toString()); + } + } + + /** + * @since 1.2 + */ + public String getCacheKey() { + return cacheKey; + } + + /** + * @since 1.2 + */ + public DataMap getDataMap() { + return dataMap; + } + + /** + * @since 1.2 + */ + public Procedure getProcedure() { + return null; + } + + /** + * @since 3.0 + */ + public Map<String, String> getPathSplitAliases() { + return Collections.emptyMap(); + } + + /** + * @since 1.2 + */ + public DbEntity getDbEntity() { + return dbEntity; + } + + /** + * @since 1.2 + */ + public ObjEntity getObjEntity() { + return classDescriptor != null ? classDescriptor.getEntity() : null; + } + + /** + * @since 3.0 + */ + public ClassDescriptor getClassDescriptor() { + return classDescriptor; + } + + /** + * @since 3.0 + */ + public List<Object> getResultSetMapping() { + return resultSetMapping; + } + + /** + * @since 1.2 + */ + public PrefetchTreeNode getPrefetchTree() { + return prefetchTree; + } + + void setPrefetchTree(PrefetchTreeNode prefetchTree) { + this.prefetchTree = prefetchTree != null ? deepClone(prefetchTree, null) : null; + } + + private PrefetchTreeNode deepClone(PrefetchTreeNode source, PrefetchTreeNode targetParent) { + + PrefetchTreeNode target = new PrefetchTreeNode(targetParent, source.getName()); + target.setEjbqlPathEntityId(source.getEjbqlPathEntityId()); + target.setEntityName(source.getEntityName()); + target.setPhantom(source.isPhantom()); + target.setSemantics(source.getSemantics()); + + for (PrefetchTreeNode child : source.getChildren()) { + target.addChild(deepClone(child, target)); + } + + return target; + } + + /** + * @since 3.0 + */ + public QueryCacheStrategy getCacheStrategy() { + return cacheStrategy; + } + + /** + * @since 3.0 + */ + void setCacheStrategy(QueryCacheStrategy cacheStrategy) { + this.cacheStrategy = cacheStrategy; + } + + /** + * @since 3.0 + */ + public String[] getCacheGroups() { + return cacheGroups; + } + + /** + * @since 3.0 + */ + void setCacheGroups(String... groups) { + this.cacheGroups = groups; + } + + public boolean isFetchingDataRows() { + return fetchingDataRows; + } + + public int getFetchLimit() { + return fetchLimit; + } + + public int getPageSize() { + return pageSize; + } + + public Query getOrginatingQuery() { + return null; + } + + /** + * @since 3.0 + */ + public int getFetchOffset() { + return fetchOffset; + } + + public boolean isRefreshingObjects() { + return true; + } + + void setFetchingDataRows(boolean b) { + fetchingDataRows = b; + } + + void setFetchLimit(int i) { + fetchLimit = i; + } + + void setFetchOffset(int i) { + fetchOffset = i; + } + + void setPageSize(int i) { + pageSize = i; + } + + /** + * Sets statement's fetch size (0 for no default size) + * + * @since 3.0 + */ + void setStatementFetchSize(int size) { + this.statementFetchSize = size; + } + + /** + * @return statement's fetch size + * @since 3.0 + */ + public int getStatementFetchSize() { + return statementFetchSize; + } + + /** + * Adds a joint prefetch. + * + * @since 1.2 + */ + PrefetchTreeNode addPrefetch(String path, int semantics) { + if (prefetchTree == null) { + prefetchTree = new PrefetchTreeNode(); + } + + PrefetchTreeNode node = prefetchTree.addPath(path); + node.setSemantics(semantics); + node.setPhantom(false); + return node; + } + + /** + * Adds a joint prefetch. + * + * @since 4.0 + */ + void mergePrefetch(PrefetchTreeNode node) { + if (prefetchTree == null) { + prefetchTree = new PrefetchTreeNode(); + } + + prefetchTree.merge(node); + } + + /** + * Adds all prefetches from a provided collection. + * + * @since 1.2 + */ + void addPrefetches(Collection<String> prefetches, int semantics) { + if (prefetches != null) { + for (String prefetch : prefetches) { + addPrefetch(prefetch, semantics); + } + } + } + + /** + * Clears all joint prefetches. + * + * @since 1.2 + */ + void clearPrefetches() { + prefetchTree = null; + } + + /** + * Removes joint prefetch. + * + * @since 1.2 + */ + void removePrefetch(String prefetch) { + if (prefetchTree != null) { + prefetchTree.removePath(prefetch); + } + } }