OPENJPA-806: Testing Criteria API by executing against the database. Persistent test classes declare fields as appropriate to the tested queries. Criteria that use new JPQL constructs such as KEY(), CASE etc. are compared literally with handcrafted JPQL string. The supported ones are executed against the database.

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@725522 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Pinaki Poddar 2008-12-11 01:29:10 +00:00
parent 8f97b2248a
commit d1f498b59e
26 changed files with 464 additions and 89 deletions

View File

@ -45,7 +45,12 @@ public class JPQLParser
if (query.getContext().getParameterDeclaration() != null)
throw new UserException(_loc.get("param-decs-invalid"));
return new JPQLExpressionBuilder.ParsedJPQL(ql);
try {
return new JPQLExpressionBuilder.ParsedJPQL(ql);
} catch (ParseException e) {
throw new ParseException(_loc.get("jpql-parse-error",
ql, e.getMessage()).getMessage(), e);
}
}
public void populate(Object parsed, ExpressionStoreQuery query) {

View File

@ -75,6 +75,10 @@ public class ParseException
public ParseException(String message) {
super(message);
}
public ParseException(String message, Throwable t) {
super(message, t);
}
/**
* This method has the standard behavior when this object has been

View File

@ -70,3 +70,5 @@ query-extensions-error: This JPQL query uses non-standard OpenJPA \
extensions in the {0} clause. JPQL string: "{1}". The \
openjpa.Compatibility configuration setting is configured to disallow \
JPQL extensions.
jpql-parse-error: "{1}" while parsing JPQL "{0}". See nested stack trace for \
original parse error.

View File

@ -0,0 +1,40 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.persistence.criteria;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* Persistent class used in testing QueryDefinition API.
*
* @author Pinaki Poddar
*
*/
@Entity
@Table(name="CR_ACCOUNT")
public class Account {
@Id
@GeneratedValue
private long id;
private int balance;
}

View File

@ -18,10 +18,11 @@
*/
package org.apache.openjpa.persistence.criteria;
import java.util.List;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.OneToMany;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* Used for testing Criteria API.
@ -32,8 +33,14 @@ import javax.persistence.OneToMany;
*
*/
@Entity
@Table(name="CR_ADDRESS")
@Embeddable
public class Address {
@Id
@GeneratedValue
private long id;
private String state;
private String county;
private String zipCode;
@OneToMany
private List<Phone> phones;
}

View File

@ -18,9 +18,17 @@
*/
package org.apache.openjpa.persistence.criteria;
import java.util.List;
import javax.persistence.Embeddable;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
@Embeddable
public class Contact {
@OneToOne
private Address address;
@OneToMany
private List<Phone> phones;
}

View File

@ -19,8 +19,10 @@
package org.apache.openjpa.persistence.criteria;
import javax.persistence.Entity;
import javax.persistence.Table;
@Entity
@Table(name="CR_CONTRACTOR")
public class Contractor {
}

View File

@ -18,9 +18,20 @@
*/
package org.apache.openjpa.persistence.criteria;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;
@Entity
public class Course {
@Table(name="CR_COURSE")
public class Course {
private String name;
@OneToMany
@OrderBy
private List<Student> studentWaitList;
}

View File

@ -18,9 +18,18 @@
*/
package org.apache.openjpa.persistence.criteria;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.OrderBy;
import javax.persistence.Table;
@Entity
public class CreditCard {
@Table(name="CR_CREDITCARD")
public class CreditCard {
private String holdr;
@OrderBy
private List<String> transactionHistory;
}

View File

@ -18,10 +18,40 @@
*/
package org.apache.openjpa.persistence.criteria;
import java.util.List;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
@Entity
@Table(name="CR_CUSTOMER")
public class Customer {
private long id;
private String firstName;
private String lastName;
@OneToMany
private Set<Order> orders;
private int status;
private int balanceOwned;
@OneToOne
private Address address;
private int filledOrderCount;
private String country;
@OneToMany
private List<Account> accounts;
public Customer() {
}
public Customer(long id, int status, int count) {
}
}

View File

@ -22,8 +22,11 @@ import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name="CR_DEPARTMENT")
public class Department {
private int deptNo;
private String name;

View File

@ -18,9 +18,26 @@
*/
package org.apache.openjpa.persistence.criteria;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Table;
@Entity
@Table(name="CR_EMPLOYEE")
public class Employee {
private Address contactInfo;
private String name;
@Embedded
private Contact contactInfo;
@ManyToOne
private Department department;
private int rating;
private float salary;
@OneToOne
private Employee spouse;
@ManyToOne
private Manager manager;
}

View File

@ -19,8 +19,11 @@
package org.apache.openjpa.persistence.criteria;
import javax.persistence.Entity;
import javax.persistence.Table;
@Entity
@Table(name="CR_EXEMPT")
public class Exempt {
}

View File

@ -18,9 +18,18 @@
*/
package org.apache.openjpa.persistence.criteria;
import java.util.Map;
import javax.persistence.Entity;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
@Entity
public class Item {
@Table(name="CR_ITEM")
public class Item {
private String name;
@OneToMany
private Map<String, Photo> photos;
}

View File

@ -0,0 +1,32 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.persistence.criteria;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name="CR_LINEITEM")
public class LineItem {
private int price;
@ManyToOne
private Order order;
}

View File

@ -19,8 +19,11 @@
package org.apache.openjpa.persistence.criteria;
import javax.persistence.Entity;
import javax.persistence.Table;
@Entity
@Table(name="CR_MANAGER")
public class Manager {
}

View File

@ -18,16 +18,24 @@
*/
package org.apache.openjpa.persistence.criteria;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name="C_ORDER")
@Table(name="CR_ORDER")
public class Order {
private int quantity;
private int cost;
private int totalcost;
private int count;
@ManyToOne
private Customer customer;
@OneToMany(mappedBy="order")
private List<LineItem> lineItems;
}

View File

@ -18,9 +18,18 @@
*/
package org.apache.openjpa.persistence.criteria;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.apache.openjpa.persistence.PersistentCollection;
@Entity
public class Person {
@Table(name="CR_PERSON")
public class Person {
@PersistentCollection
private List<String> nicknames;
}

View File

@ -19,8 +19,11 @@
package org.apache.openjpa.persistence.criteria;
import javax.persistence.Entity;
import javax.persistence.Table;
@Entity
@Table(name="CR_PHONE")
public class Phone {
private String vendor;
}

View File

@ -0,0 +1,29 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.persistence.criteria;
import javax.persistence.Entity;
import javax.persistence.Table;
@Entity
@Table(name="CR_PHOTO")
public class Photo {
private String label;
}

View File

@ -0,0 +1,29 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.persistence.criteria;
import javax.persistence.Entity;
import javax.persistence.Table;
@Entity
@Table(name="CR_STUDENT")
public class Student {
}

View File

@ -19,13 +19,19 @@
package org.apache.openjpa.persistence.criteria;
import java.util.List;
import javax.persistence.CaseExpression;
import javax.persistence.DomainObject;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Expression;
import javax.persistence.Query;
import javax.persistence.QueryBuilder;
import javax.persistence.QueryDefinition;
import javax.persistence.SelectItem;
import org.apache.openjpa.kernel.jpql.ParseException;
import org.apache.openjpa.persistence.query.AbstractDomainObject;
import org.apache.openjpa.persistence.query.QueryBuilderImpl;
import org.apache.openjpa.persistence.query.QueryDefinitionImpl;
@ -33,11 +39,9 @@ import org.apache.openjpa.persistence.test.SingleEMFTestCase;
/**
* Tests by stringifying QueryDefinition and comparing the resultant string
* Tests QueryDefinition and comparing the resultant string
* with an equivalent JPQL.
*
* Does not execute the query.
*
* The examples are taken from Criteria API Section of Java Persistence API
* Version 2.0 [1].
*
@ -49,25 +53,39 @@ import org.apache.openjpa.persistence.test.SingleEMFTestCase;
*/
public class TestCriteria extends SingleEMFTestCase {
protected QueryBuilderImpl qb;
private static EntityManagerFactory emf = null;
protected StringComparison comparator = new StringComparison();
public void setUp() {
super.setUp(Contractor.class, Course.class, CreditCard.class,
Department.class, Employee.class, Exempt.class, Item.class,
Manager.class, Person.class, VideoStore.class, Order.class,
Customer.class);
if (emf == null) {
super.setUp(DROP_TABLES,
Account.class,
Address.class,
Contact.class,
Contractor.class,
Course.class,
CreditCard.class,
Customer.class,
Department.class,
Employee.class,
Exempt.class,
Item.class,
LineItem.class,
Manager.class,
Person.class,
Order.class,
Phone.class,
Photo.class,
Student.class,
VideoStore.class);
emf = super.emf;
}
qb = (QueryBuilderImpl)emf.getQueryBuilder();
emf.createEntityManager();
}
public void tearDown() {
// do nothing as you do not have a database connection
}
void compare(String s, QueryDefinition q) {
String actual = qb.toJPQL(q);
if (!comparator.compare(s,actual)) {
fail("\r\nExpected: [" + s + "]\r\nActual : [" + actual + "]");
}
// do nothing as we may not have a database connection
}
public void testMultipleDomainOfSameClass() {
@ -121,7 +139,7 @@ public class TestCriteria extends SingleEMFTestCase {
String jpql = "select i.name, VALUE(p)"
+ " from Item i join i.photos p"
+ " where KEY(p) like 'egret'";
compare(jpql, qdef);
compare(jpql, qdef, "VALUE(p) not supported");
}
public void testLiteral() {
@ -145,7 +163,7 @@ public class TestCriteria extends SingleEMFTestCase {
String jpql = "select TYPE(e)" +
" from Employee e" +
" where TYPE(e) <> Exempt";
compare(jpql, e);
compare(jpql, e, "Type() not supported");
}
public void testIndex() {
@ -157,7 +175,7 @@ public class TestCriteria extends SingleEMFTestCase {
String jpql = "select s.name" +
" from Course c join c.studentWaitList s" +
" where c.name = 'Calculus' and INDEX(s) = 0";
compare(jpql, c);
compare(jpql, c, "Index() not supported");
}
public void testSum() {
@ -203,7 +221,7 @@ public class TestCriteria extends SingleEMFTestCase {
+ " FROM Employee e"
+ " WHERE e.department.name = 'Engineering'";
compare(jpql, e);
compare(jpql, e, "Case not supported");
}
public void testMemberOf() {
@ -222,7 +240,7 @@ public class TestCriteria extends SingleEMFTestCase {
String jpql = "select c from Customer c " +
" where c.status = :status";
compare(jpql, qdef);
compare(jpql, qdef, null, "status", 1);
}
public void testBetween() {
@ -235,7 +253,8 @@ public class TestCriteria extends SingleEMFTestCase {
String jpql = "select t from CreditCard c JOIN c.transactionHistory t" +
" where c.holder.name = 'John Doe' AND INDEX(t) " +
" BETWEEN 0 AND 9";
compare(jpql, c);
compare(jpql, c, "Index() not supported");
}
public void testIsEmpty() {
@ -275,7 +294,8 @@ public class TestCriteria extends SingleEMFTestCase {
order.get("count")));
String jpql = "SELECT NEW org.apache.openjpa.persistence.criteria.Customer(c.id, c.status, o.count)"
String jpql = "SELECT NEW org.apache.openjpa.persistence.criteria.Customer"
+ "(c.id, c.status, o.count)"
+ " FROM Customer c JOIN c.orders o"
+ " WHERE o.count > 100";
compare(jpql, q);
@ -285,7 +305,7 @@ public class TestCriteria extends SingleEMFTestCase {
QueryDefinition q = qb.createQueryDefinition();
DomainObject v = q.addRoot(VideoStore.class);
DomainObject i = v.join("videoInventory");
q.where(v.get("location").get("zipcode").equal("94301")
q.where(v.get("location").get("zipCode").equal("94301")
.and(i.value().greaterThan(0)));
q.select(v.get("location").get("street"),
i.key().get("title"),
@ -293,8 +313,9 @@ public class TestCriteria extends SingleEMFTestCase {
String jpql = "SELECT v.location.street, KEY(v2).title, VALUE(v2)"
+ " FROM VideoStore v JOIN v.videoInventory v2"
+ " WHERE v.location.zipcode = '94301' AND VALUE(v2) > 0";
compare(jpql, q);
+ " WHERE v.location.zipCode = '94301' AND VALUE(v2) > 0";
compare(jpql, q, "KEY() and/or VALUE() not supported");
}
public void testGroupByHaving() {
@ -345,35 +366,35 @@ public class TestCriteria extends SingleEMFTestCase {
}
public void testOrderBy2() {
QueryDefinition q = qb.createQueryDefinition();
DomainObject customer = q.addRoot(Customer.class);
DomainObject order = customer.join("orders");
DomainObject address = customer.join("address");
q.where(address.get("state").equal("CA"))
.select(order.get("quantity"), address.get("zipcode"))
.orderBy(order.get("quantity").desc(), address.get("zipcode"));
String jpql = "SELECT o.quantity, a.zipcode"
+ " FROM Customer c JOIN c.orders o JOIN c.address a"
+ " WHERE a.state = 'CA'"
+ " ORDER BY o.quantity DESC, a.zipcode";
compare(jpql, q);
QueryDefinition q = qb.createQueryDefinition();
DomainObject customer = q.addRoot(Customer.class);
DomainObject order = customer.join("orders");
DomainObject address = customer.join("address");
q.where(address.get("state").equal("CA"))
.select(order.get("quantity"), address.get("zipCode"))
.orderBy(order.get("quantity").desc(), address.get("zipCode"));
String jpql = "SELECT o.quantity, a.zipCode"
+ " FROM Customer c JOIN c.orders o JOIN c.address a"
+ " WHERE a.state = 'CA'"
+ " ORDER BY o.quantity DESC, a.zipCode";
compare(jpql, q);
}
public void testOrderByExpression() {
DomainObject o = qb.createQueryDefinition(Order.class);
DomainObject a = o.join("customer").join("address");
SelectItem taxedCost = o.get("cost").times(1.08);
o.select(o.get("quantity"), taxedCost, a.get("zipcode"))
.where(a.get("state").equal("CA")
.and(a.get("county").equal("Santa Clara")))
.orderBy(o.get("quantity"), taxedCost, a.get("zipcode"));
String jpql = "SELECT o.quantity, o.cost*1.08 as o2, a.zipcode"
+ " FROM Order o JOIN o.customer c JOIN c.address a"
+ " WHERE a.state = 'CA' AND a.county = 'Santa Clara'"
+ " ORDER BY o.quantity, o2, a.zipcode";
compare(jpql, o);
DomainObject o = qb.createQueryDefinition(Order.class);
DomainObject a = o.join("customer").join("address");
SelectItem taxedCost = o.get("cost").times(1.08);
o.select(o.get("quantity"), taxedCost, a.get("zipCode"))
.where(a.get("state").equal("CA")
.and(a.get("county").equal("Santa Clara")))
.orderBy(o.get("quantity"), taxedCost, a.get("zipCode"));
String jpql = "SELECT o.quantity, o.cost*1.08 as o2, a.zipCode"
+ " FROM Order o JOIN o.customer c JOIN c.address a"
+ " WHERE a.state = 'CA' AND a.county = 'Santa Clara'"
+ " ORDER BY o.quantity, o2, a.zipCode";
compare(jpql, o);
}
public void testCorrelatedSubquery() {
@ -399,11 +420,11 @@ public class TestCriteria extends SingleEMFTestCase {
public void testCreateSubquery() {
DomainObject customer = qb.createQueryDefinition(Customer.class);
DomainObject order = qb.createSubqueryDefinition(customer.get("orders"));
customer.where(order.select(order.get("price").avg()).greaterThan(100));
customer.where(order.select(order.get("cost").avg()).greaterThan(100));
String jpql = "SELECT c "
+ " FROM Customer c"
+ " WHERE (SELECT AVG(o.price) FROM c.orders o) > 100";
+ " WHERE (SELECT AVG(o.cost) FROM c.orders o) > 100";
compare(jpql, customer);
}
@ -415,16 +436,17 @@ public class TestCriteria extends SingleEMFTestCase {
String jpql = "SELECT e "
+ " FROM Employee e"
+ " WHERE TYPE(e) IN (Exempt, Contractor)";
compare(jpql, q);
compare(jpql, q, "Type() not supported");
}
public void testStringList() {
DomainObject q = qb.createQueryDefinition(Employee.class);
DomainObject q = qb.createQueryDefinition(Customer.class);
q.where(q.get("country").in("USA", "UK", "France"));
String jpql = "SELECT e "
+ " FROM Employee e"
+ " WHERE e.country IN ('USA', 'UK', 'France')";
String jpql = "SELECT c "
+ " FROM Customer c"
+ " WHERE c.country IN ('USA', 'UK', 'France')";
compare(jpql, q);
}
@ -439,11 +461,11 @@ public class TestCriteria extends SingleEMFTestCase {
String jpql = "SELECT e.name, f.name, CONCAT("
+ " CASE WHEN f.annualMiles > 50000 THEN 'Platinum'"
+ " WHEN f.annualMiles > 25000 THEN 'Gold'"
+ " WHEN f.annualMiles > 25000 THEN 'Gold'"
+ " ELSE '' END, 'Frequent Flyer')"
+ "FROM Employee e JOIN e.frequentFlierPlan f";
+ " FROM Employee e JOIN e.frequentFlierPlan f";
compare(jpql, e);
compare(jpql, e, "Case not supported");
}
public void testCorrelatedSubquerySpecialCase1() {
@ -456,7 +478,7 @@ public class TestCriteria extends SingleEMFTestCase {
+ " where 10000 < ALL "
+ " (select a.balance from o.customer c join o.customer.accounts a)";
compare(jpql, o);
compare(jpql, o, "SubQuery generates invalid SQL on Derby");
}
public void testCorrelatedSubquerySpecialCase2() {
@ -470,7 +492,7 @@ public class TestCriteria extends SingleEMFTestCase {
+ " where 10000 < ALL "
+ " (select a.balance from c.accounts a)";
compare(jpql, o);
compare(jpql, o, "SubQuery generates invalid SQL on Derby");
}
public void testRecursiveDefinitionIsNotAllowed() {
@ -478,11 +500,100 @@ public class TestCriteria extends SingleEMFTestCase {
q.where(q.exists().and(q.get("name").equal("wrong")));
try {
compare("?", q);
qb.toJPQL(q);
fail();
} catch (RuntimeException e) {
// good
}
}
// ---------------------------------------------------------------------
// verification methods
// ---------------------------------------------------------------------
/**
* Compare by executing the queries generated from the given JPQL and
* QueryDefinition.
*/
void compare(String jpql, QueryDefinition q) {
compare(jpql, q, null, (Object[])null);
}
/**
* Compare hand crafted JPQL and QueryDefinition.
* If skip is null then execute both queries against the database, otherwise
* compare them literally.
*/
void compare(String jpql, QueryDefinition q, String skip, Object...p) {
boolean execute = (skip == null);
if (execute) {
executeActually(jpql, q, p);
} else {
System.err.println("***WARN: " + this.getName()
+ ": skips executing ["+ jpql + "] because " + skip);
compareLiterally(jpql, q);
}
}
/**
* Compare the string version of QueryDefinition and given JPQL string with
* some flexibility of case-insensitive reserved words.
*/
private void compareLiterally(String jpql, QueryDefinition q) {
String actual = qb.toJPQL(q);
if (!comparator.compare(jpql,actual))
fail("\r\nExpected: [" + jpql + "]\r\nActual : [" + actual + "]");
}
/**
* Executes the given JPQL and QueryDefinition independently and compare
* their results.
*/
private void executeActually(String jpql, QueryDefinition q, Object...p) {
EntityManager em = emf.createEntityManager();
List criteriaResult = null;
List jpqlResult = null;
Throwable criteriaError = null;
Throwable jpqlError = null;
try {
Query cq = em.createQuery(q);
setParameters(cq, p);
criteriaResult = cq.getResultList();
} catch (Exception e) {
criteriaError = e;
}
try {
Query nq = em.createQuery(jpql);
setParameters(nq, p);
jpqlResult = nq.getResultList();
} catch (Exception e) {
jpqlError = e;
}
if (criteriaError == null && jpqlError == null) {
assertEquals(criteriaResult.size(), jpqlResult.size());
} else if (criteriaError != null && jpqlError == null) {
fail("QueryDefinition generated invalid JPQL\r\n"
+ "Criteria [" + qb.toJPQL(q) + "]\r\n"
+ "error : " + criteriaError.getMessage());
} else if (criteriaError == null && jpqlError != null) {
fail("Handcrafted JPQL is invalid \r\n"
+ "JPQL [" + jpql + "]\r\n"
+ "error : " + jpqlError.getMessage());
} else {
fail("Both JPQL and QueryDefinition failed to execute.\r\n"
+ "JPQL " + jpql + "\r\n"
+ "error :" + jpqlError.getMessage() + "\r\n"
+ "Criteria " + qb.toJPQL(q) + "\r\n"
+ "error : " + criteriaError.getMessage());
}
}
void setParameters(Query q, Object...p) {
if (p == null)
return;
for (int i = 0; i < p.length; i += 2) {
q.setParameter(p[i].toString(), p[i+1]);
}
}
}

View File

@ -19,8 +19,15 @@
package org.apache.openjpa.persistence.criteria;
import javax.persistence.Entity;
import javax.persistence.Table;
/**
*
* @author Pinaki Poddar
*
*/
@Entity
@Table(name="CR_VIDEOSTORE")
public class VideoStore {
}

View File

@ -60,6 +60,7 @@ import org.apache.openjpa.kernel.QueryLanguages;
import org.apache.openjpa.kernel.Seq;
import org.apache.openjpa.kernel.FetchConfiguration;
import org.apache.openjpa.kernel.jpql.JPQLParser;
import org.apache.openjpa.kernel.jpql.ParseException;
import org.apache.openjpa.lib.util.Closeable;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.meta.ClassMetaData;
@ -1392,8 +1393,8 @@ public class EntityManagerImpl
}
public Query createQuery(QueryDefinition qdef) {
throw new UnsupportedOperationException(
"JPA 2.0 - Method not yet implemented");
String jpql = getQueryBuilder().toJPQL(qdef);
return createQuery(jpql);
}
public <T> T find(Class<T> entityClass, Object primaryKey,
@ -1413,7 +1414,7 @@ public class EntityManagerImpl
"JPA 2.0 - Method not yet implemented");
}
public QueryBuilder getQueryBuilder() {
public QueryBuilderImpl getQueryBuilder() {
return new QueryBuilderImpl(_emf);
}

View File

@ -395,9 +395,8 @@ public class QueryDefinitionImpl extends ExpressionImpl
case EXPRESSION : buffer.append(v.asExpression(ctx))
.append(i != list.size()-1 ? ", " : " ");
break;
case JOINABLE : buffer.append(v.asJoinable(ctx))
.append(i > 0 && v instanceof RootPath ?
"," : " ");
case JOINABLE : buffer.append(i > 0 && v instanceof RootPath ?
", " : " ").append(v.asJoinable(ctx));
break;
}
}

View File

@ -32,10 +32,4 @@ abstract class QueryExpression extends UnaryOperatorExpression
public QueryExpression(QueryDefinitionImpl q, UnaryFunctionalOperator op) {
super(q, op);
}
@Override
public String asExpression(AliasContext ctx) {
return "(" + super.asExpression(ctx) + ")";
}
}