From 2ce5299a7310130fa35d258e0dd14b65012980b0 Mon Sep 17 00:00:00 2001 From: Catalina Wei Date: Mon, 18 May 2009 22:23:04 +0000 Subject: [PATCH] OPENJPA-1094 JPA2 Query support KEY to appear in subquery git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@776117 13f79535-47bb-0310-9956-ffa450edef68 --- .../openjpa/jdbc/kernel/exps/AbstractVal.java | 5 + .../jdbc/kernel/exps/ContainsExpression.java | 2 +- .../openjpa/jdbc/kernel/exps/MapKey.java | 7 + .../openjpa/jdbc/kernel/exps/PCPath.java | 14 +- .../apache/openjpa/jdbc/kernel/exps/SubQ.java | 2 +- .../openjpa/jdbc/kernel/exps/Variable.java | 5 + .../org/apache/openjpa/kernel/exps/Val.java | 4 + .../org/apache/openjpa/kernel/exps/Value.java | 2 + .../kernel/jpql/JPQLExpressionBuilder.java | 26 +++- .../persistence/embed/TestEmbeddable.java | 141 ++++++++++++++++++ 10 files changed, 198 insertions(+), 10 deletions(-) diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/AbstractVal.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/AbstractVal.java index 0b1314830..6e79b54f4 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/AbstractVal.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/AbstractVal.java @@ -21,6 +21,7 @@ package org.apache.openjpa.jdbc.kernel.exps; import org.apache.openjpa.jdbc.sql.SQLBuffer; import org.apache.openjpa.jdbc.sql.Select; import org.apache.openjpa.kernel.exps.ExpressionVisitor; +import org.apache.openjpa.kernel.exps.Path; import org.apache.openjpa.kernel.exps.Value; /** @@ -109,5 +110,9 @@ abstract class AbstractVal public Value getSelectAs() { return _alias != null ? this : null; } + + public Path getPath() { + return null; + } } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/ContainsExpression.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/ContainsExpression.java index ae82f183a..af2189a71 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/ContainsExpression.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/ContainsExpression.java @@ -42,7 +42,7 @@ class ContainsExpression Val val1 = getValue1(); if (contains != null && val1 instanceof PCPath) { PCPath sql = (PCPath) val1; - String path = sql.getPath(); + String path = sql.getPCPathString(); // update the count for this path Integer count = (Integer) contains.get(path); diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/MapKey.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/MapKey.java index ed5d4d503..56260abfc 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/MapKey.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/MapKey.java @@ -124,4 +124,11 @@ public class MapKey public void setMetaData(ClassMetaData meta) { _meta = meta; } + + public Object toDataStoreValue(Select sel, ExpContext ctx, ExpState state, + Object val) { + KeyExpState estate = (KeyExpState) state; + return _key.toDataStoreValue(sel, ctx, + estate.key, val); + } } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/PCPath.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/PCPath.java index b46dbbd0d..06a1f6eab 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/PCPath.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/PCPath.java @@ -218,7 +218,7 @@ public class PCPath return xpath.toString(); } - public String getPath() { + public String getPCPathString() { if (_actions == null) return (_varName == null) ? "" : _varName + "."; @@ -293,10 +293,14 @@ public class PCPath case JavaTypes.COLLECTION: ValueMapping elem = pstate.field.getElementMapping(); if (pstate.field.isElementCollection() && - pstate.field.getElement().isEmbedded()) - return ((HandlerCollectionTableFieldStrategy) - pstate.field.getStrategy()).getElementColumns( - elem.getTypeMapping()); + pstate.field.getElement().isEmbedded()) { + Strategy strategy = pstate.field.getStrategy(); + if (strategy instanceof + HandlerCollectionTableFieldStrategy) + return ((HandlerCollectionTableFieldStrategy) + strategy).getElementColumns( + elem.getTypeMapping()); + } if (pstate.joinedRel && elem.getTypeCode() == JavaTypes.PC) return elem.getTypeMapping().getPrimaryKeyColumns(); if (elem.getColumns().length > 0) diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SubQ.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SubQ.java index 81f825033..6afba5a8b 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SubQ.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/SubQ.java @@ -69,7 +69,7 @@ class SubQ } public Class getType() { - if (_exps != null) { + if (_exps != null && _type == null) { if (_exps.projections.length == 0) return _candidate.getDescribedType(); if (_exps.projections.length == 1) diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Variable.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Variable.java index 71275348d..2dc3ef81f 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Variable.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Variable.java @@ -24,6 +24,7 @@ import org.apache.openjpa.jdbc.sql.Result; import org.apache.openjpa.jdbc.sql.SQLBuffer; import org.apache.openjpa.jdbc.sql.Select; import org.apache.openjpa.kernel.exps.ExpressionVisitor; +import org.apache.openjpa.kernel.exps.Path; import org.apache.openjpa.meta.ClassMetaData; /** @@ -75,6 +76,10 @@ class Variable return _path; } + public Path getPath() { + return _path; + } + /** * Set the path this variable is aliased to. */ diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/Val.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/Val.java index dc760cf42..01e1bf18a 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/Val.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/Val.java @@ -131,4 +131,8 @@ public abstract class Val public Value getSelectAs() { return _alias != null ? this : null; } + + public Path getPath() { + return null; + } } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/Value.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/Value.java index c2131b0b2..4b312a8ba 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/Value.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/exps/Value.java @@ -88,4 +88,6 @@ public interface Value * Return 'this' concrete class if alias is set, otherwise null */ public Value getSelectAs(); + + public Path getPath(); } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java index eb847e008..0ccdbf327 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/jpql/JPQLExpressionBuilder.java @@ -651,7 +651,13 @@ public class JPQLExpressionBuilder // clause, since we might be in a subquery against a collection if (isPath(left)) { Path path = getPath(left); - setCandidate(getFieldType(path.last()), alias); + FieldMetaData fmd = path.last(); + ClassMetaData candidate = getFieldType(fmd); + + if (candidate == null && fmd.isElementCollection()) + candidate = fmd.getDefiningMetaData(); + + setCandidate(candidate, alias); Path subpath = factory.newPath(ctx().subquery); subpath.setMetaData(ctx().subquery.getMetaData()); @@ -1491,6 +1497,14 @@ public class JPQLExpressionBuilder private Value validateMapPath(JPQLNode node, JPQLNode id) { Path path = (Path) getValue(id); FieldMetaData fld = path.last(); + + if (fld == null && ctx().subquery != null) { + Value var = getVariable(id.text, false); + if (var != null) { + path = factory.newPath(var); + fld = path.last(); + } + } if (fld != null) { // validate the field is of type java.util.Map @@ -1503,7 +1517,11 @@ public class JPQLExpressionBuilder throw parseException(EX_USER, "bad-qualified-identifier", new Object[]{ id.text, oper}, null); } - } + } + else + throw parseException(EX_USER, "unknown-type", + new Object[]{ id.text}, null); + return path; } @@ -1517,7 +1535,9 @@ public class JPQLExpressionBuilder FieldMetaData fld = path.last(); path = (Path) factory.getKey(path); ClassMetaData meta = fld.getKey().getTypeMetaData(); - if (inWhereClause && meta != null) + if (inWhereClause && meta != null && + fld.isElementCollection() && + fld.getElement().getEmbeddedMetaData() != null) // check basic type throw parseException(EX_USER, "bad-general-identifier", new Object[]{ id.text, "KEY" }, null); diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/TestEmbeddable.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/TestEmbeddable.java index cb5128a3f..f741eb679 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/TestEmbeddable.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/embed/TestEmbeddable.java @@ -746,6 +746,18 @@ public class TestEmbeddable extends SingleEMFTestCase { assertEmployee(e); } tran.commit(); + em.clear(); + // test range variable over element collection + String[] query = { + "select e from Employee e, in (e.nickNames) n " + + " where n like '%1'", + }; + for (int i = 0; i < query.length; i++) { + es = em.createQuery(query[i]).getResultList(); + for (Employee e : es){ + assertEmployee(e); + } + } em.close(); } @@ -2145,6 +2157,25 @@ public class TestEmbeddable extends SingleEMFTestCase { } tran.commit(); + + String query[] = { + "select d from Department1 d join d.empMap e " + + " where KEY(e) > 1 order by d", + "select d from Department1 d join d.empMap e" + + " where d.deptId = KEY(e) order by d", + "select d from Department1 d " + + " where d.deptId < ANY " + + " (select KEY(e) from in(d.empMap) e) " + + " order by d", + "select d from Department1 d " + + " where d.deptId < SOME " + + " (select KEY(e) from Department1 d1, in(d1.empMap) e) " + + " order by d", + }; + for (int i = 0; i < query.length; i++) { + ds1 = em.createQuery(query[i]).getResultList(); + assertDepartment1(ds1.get(0)); + } em.close(); } @@ -2290,6 +2321,66 @@ public class TestEmbeddable extends SingleEMFTestCase { } tran.commit(); + + String imageKey1 = (String) is1.get(0).getImages(). + keySet().toArray()[0]; + String imageKey2 = (String) is2.get(0).getImages(). + keySet().toArray()[0]; + String imageKey3 = (String) is3.get(0).getImages(). + keySet().toArray()[0]; + String[] query = { + "select i from Item1 i" + + " where ?1 = any " + + " (select KEY(e) from Item1 i, in(i.images) e) " + + " order by i", + "select i from Item2 i" + + " where ?1 = any " + + " (select KEY(e) from Item2 i, in(i.images) e) " + + " order by i", + "select i from Item3 i" + + " where ?1 = any " + + " (select KEY(e) from Item3 i, in(i.images) e) " + + " order by i", + "select i from Item1 i" + + " where exists " + + " (select e from Item1 i, in(i.images) e" + + " where ?1 = KEY(e)) " + + " order by i", + "select i from Item2 i" + + " where exists " + + " (select e from Item2 i, in(i.images) e" + + " where ?1 = KEY(e)) " + + " order by i", + "select i from Item3 i" + + " where exists " + + " (select e from Item3 i, in(i.images) e" + + " where ?1 = KEY(e)) " + + " order by i", + }; + + for (int i = 0; i < query.length; i++) { + Query q = em.createQuery(query[i]); + switch (i) { + case 0: + case 3: + q.setParameter(1, imageKey1); + is1 = q.getResultList(); + assertItem1(is1.get(0)); + break; + case 1: + case 4: + q.setParameter(1, imageKey2); + is2 = q.getResultList(); + assertItem2(is2.get(0)); + break; + case 2: + case 5: + q.setParameter(1, imageKey3); + is3 = q.getResultList(); + assertItem3(is3.get(0)); + break; + } + } em.close(); } @@ -2308,6 +2399,56 @@ public class TestEmbeddable extends SingleEMFTestCase { assertCompany2(c); } tran.commit(); + + em.clear(); + // test KEY(e) appeared in subquery + Division d1 = (Division) ((Company1) cs1.get(0)).getOrganization(). + keySet().toArray()[0]; + Division d2 = (Division) ((Company2) cs2.get(0)).getOrganization(). + keySet().toArray()[0]; + String[] query = { + "select c from Company1 c, in(c.organization) d " + + " where KEY(d) = ?1", + "select c from Company1 c " + + " where ?1 = " + + " (select KEY(d) from Company1 c, in(c.organization) d" + + " where d.id = 1)" + + " order by c ", + "select c from Company2 c" + + " where ?1 = " + + " (select KEY(d) from Company2 c, in(c.organization) d" + + " where d.id = 3)" + + " order by c ", + "select c from Company1 c where exists" + + " (select d from in(c.organization) d" + + " where KEY(d) = ?1)" + + " order by c ", + "select c from Company2 c where exists" + + " (select d from in(c.organization) d" + + " where KEY(d) = ?1)" + + " order by c ", + }; + + for (int i = 0; i < query.length; i++) { + Query q = em.createQuery(query[i]); + switch (i) { + case 0: + case 1: + case 3: + q.setParameter(1, d1); + cs1 = q.getResultList(); + if (cs1.size() > 0) + assertCompany1(cs1.get(0)); + break; + case 2: + case 4: + q.setParameter(1, d2); + cs2 = q.getResultList(); + if (cs2.size() > 0) + assertCompany2(cs2.get(0)); + break; + } + } em.close(); }