mirror of https://github.com/apache/openjpa.git
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:
parent
8f97b2248a
commit
d1f498b59e
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 {
|
||||
|
||||
}
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue