| /* |
| * 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.query; |
| |
| import java.util.Date; |
| import java.util.List; |
| |
| import javax.persistence.EntityManager; |
| import javax.persistence.Query; |
| |
| import org.apache.openjpa.jdbc.conf.JDBCConfiguration; |
| import org.apache.openjpa.jdbc.sql.DBDictionary; |
| import org.apache.openjpa.jdbc.sql.MariaDBDictionary; |
| import org.apache.openjpa.jdbc.sql.MySQLDictionary; |
| import org.apache.openjpa.persistence.query.Customer.CreditRating; |
| import org.apache.openjpa.persistence.test.SingleEMFTestCase; |
| |
| /** |
| * Test JPQL subquery |
| */ |
| public class TestSubquery |
| extends SingleEMFTestCase { |
| |
| @Override |
| public void setUp() { |
| setUp(Customer.class, Customer.CustomerKey.class, Order.class, |
| OrderItem.class, Magazine.class, Publisher.class, Employee.class, |
| Dependent.class, DependentId.class, Account.class, DROP_TABLES); |
| } |
| |
| static String[] querys = new String[] { |
| "select c from Customer c where EXISTS" + |
| " (select o from in(c.orders) o)" , |
| "select c from Customer c where EXISTS" + |
| " (select o from c.orders o)" , |
| "select c from Customer c where NOT EXISTS" + |
| " (select o from in (c.orders) o)" , |
| "select c from Customer c where NOT EXISTS" + |
| " (select o from c.orders o)" , |
| "select o1.oid from Order o1 where o1.oid in " + |
| " (select distinct o.oid from OrderItem i, Order o" + |
| " where i.quantity > 10 and o.amount > 1000 and i.lid = o.oid)" , |
| "select o.oid from Order o where o.customer.name =" + |
| " (select max(o2.customer.name) from Order o2" + |
| " where o.customer.cid.id = o2.customer.cid.id)", |
| "select o from Order o where o.customer.name =" + |
| " (select max(o2.customer.name) from Order o2" + |
| " where o.customer.cid.id = o2.customer.cid.id)", |
| "select o.oid from Order o where o.amount >" + |
| " (select count(i) from o.lineitems i)", |
| "select o.oid from Order o where o.amount >" + |
| " (select count(o.amount) from Order o)", |
| "select o.oid from Order o where o.amount >" + |
| " (select count(o.oid) from Order o)", |
| "select o.oid from Order o where o.amount >" + |
| " (select avg(o.amount) from Order o)", |
| "select c.name from Customer c where exists" + |
| " (select o from c.orders o where o.oid = 1) or exists" + |
| " (select o from c.orders o where o.oid = 2)", |
| "select c.name from Customer c, in(c.orders) o where o.amount " + |
| "between" + |
| " (select max(o.amount) from Order o) and" + |
| " (select avg(o.amount) from Order o) ", |
| "select o.oid from Order o where o.amount >" + |
| " (select sum(o2.amount) from Customer c, in(c.orders) o2) ", |
| "select o.oid from Order o where o.amount between" + |
| " (select avg(o2.amount) from Customer c, in(c.orders) o2)" + |
| " and (select min(o2.amount) from Customer c, in(c.orders) o2)", |
| "select o.oid from Customer c, in(c.orders)o where o.amount >" + |
| " (select sum(o2.amount) from c.orders o2)", |
| "select o1.oid, c.name from Order o1, Customer c where o1.amount = " + |
| " any(select o2.amount from in(c.orders) o2)", |
| "SELECT p, m "+ |
| "FROM Publisher p "+ |
| "LEFT OUTER JOIN p.magazineCollection m "+ |
| "WHERE m.id = (SELECT MAX(m2.id) "+ |
| "FROM Magazine m2 "+ |
| "WHERE m2.idPublisher.id = p.id "+ |
| "AND m2.datePublished = "+ |
| "(SELECT MAX(m3.datePublished) "+ |
| "FROM Magazine m3 "+ |
| "WHERE m3.idPublisher.id = p.id)) ", |
| "select o from Order o where o.amount > " + |
| " (select count(o) from Order o)", |
| "select o from Order o where o.amount > " + |
| "(select count(o2) from Order o2)", |
| "select c from Customer c left join c.orders o where not exists" |
| + " (select o2 from c.orders o2 where o2 = o)", |
| }; |
| |
| static String[] querys_jpa20 = new String[] { |
| "select o.oid from Order o where o.delivered =" + |
| " (select " + |
| " CASE WHEN o2.amount > 10 THEN true" + |
| " WHEN o2.amount = 10 THEN false " + |
| " ELSE false " + |
| " END " + |
| " from Order o2" + |
| " where o.customer.cid.id = o2.customer.cid.id)", |
| |
| "select o1.oid from Order o1 where o1.amount > " + |
| " (select o.amount*0.8 from OrderItem i, Order o" + |
| " where i.quantity > 10 and o.amount > 1000 and i.lid = o.oid)", |
| |
| "select o.oid from Order o where o.customer.name =" + |
| " (select substring(o2.customer.name, 3) from Order o2" + |
| " where o.customer.cid.id = o2.customer.cid.id)", |
| |
| "select o.oid from Order o where o.orderTs >" + |
| " (select CURRENT_TIMESTAMP from o.lineitems i)", |
| |
| "select o.oid from Order o where o.amount >" + |
| " (select SQRT(o.amount) from Order o where o.delivered = true)", |
| |
| "select o.oid from Order o where o.customer.name in" + |
| " (select CONCAT(o.customer.name, 'XX') from Order o" + |
| " where o.amount > 10)", |
| |
| "select c from Customer c where c.creditRating =" + |
| " (select " + |
| " CASE WHEN o2.amount > 10 THEN " + |
| "org.apache.openjpa.persistence.query.Customer$CreditRating.POOR" + |
| " WHEN o2.amount = 10 THEN " + |
| "org.apache.openjpa.persistence.query.Customer$CreditRating." + |
| "GOOD " + |
| " ELSE " + |
| "org.apache.openjpa.persistence.query." + |
| "Customer$CreditRating.EXCELLENT " + |
| " END " + |
| " from Order o2" + |
| " where c.cid.id = o2.customer.cid.id)", |
| |
| "select c from Customer c " + |
| "where c.creditRating = (select COALESCE (c1.creditRating, " + |
| "org.apache.openjpa.persistence.query." + |
| "Customer$CreditRating.POOR) " + |
| "from Customer c1 where c1.name = 'Famzy') order by c.name DESC", |
| |
| "select c from Customer c " + |
| "where c.creditRating = (select NULLIF (c1.creditRating, " + |
| "org.apache.openjpa.persistence.query." + |
| "Customer$CreditRating.POOR) " + |
| "from Customer c1 where c1.name = 'Famzy') order by c.name DESC", |
| }; |
| |
| static String[] updates = new String[] { |
| "update Order o set o.amount = 1000 where o.customer.name = " + |
| " (select max(o2.customer.name) from Order o2 " + |
| " where o.customer.cid.id = o2.customer.cid.id)", |
| }; |
| |
| static String[] querys2 = new String[] { |
| // 0 |
| "select o1.oid, c.name from Order o1, Customer c" + |
| " where o1.customer.name = " + |
| " any(select o2.customer.name from in(c.orders) o2)", |
| // 1 |
| "select o1.oid, c.name from Order o1, Customer c" + |
| " where o1.amount = " + |
| " any(select o2.amount from in(c.orders) o2)", |
| // 2 |
| "select DISTINCT c.name FROM Customer c JOIN c.orders o " + |
| "WHERE EXISTS (SELECT o FROM o.lineitems l where l.quantity > 2 ) ", |
| // 3 |
| "select DISTINCT c.name FROM Customer c, IN(c.orders) co " + |
| "WHERE co.amount > ALL " + |
| "(Select o.amount FROM Order o, in(o.lineitems) l WHERE l.quantity > 2)", |
| // 4 |
| "select distinct c.name FROM Customer C, IN(C.orders) co " + |
| "WHERE co.amount < ALL " + |
| "(Select o.amount FROM Order o, IN(o.lineitems) l WHERE l.quantity > 2)", |
| //5 |
| "select c.name FROM Customer c, IN(c.orders) co " + |
| "WHERE co.amount <= ALL " + |
| "(Select o.amount FROM Order o, IN(o.lineitems) l WHERE l.quantity > 2)", |
| // 6 |
| "select DISTINCT c.name FROM Customer c, IN(c.orders) co " + |
| "WHERE co.amount > ANY " + |
| "(Select o.amount FROM Order o, IN(o.lineitems) l WHERE l.quantity = 2)", |
| // 7 |
| "select DISTINCT c.name FROM Customer c " + |
| "WHERE EXISTS (SELECT o FROM c.orders o where o.amount " + |
| "BETWEEN 1000 AND 1200)", |
| // 8 |
| "select DISTINCT c.name FROM Customer c " + |
| "WHERE EXISTS (SELECT o FROM c.orders o where o.amount > 1000 )", |
| // 9 |
| "SELECT o.oid from Order o WHERE " + |
| "EXISTS (SELECT c.name From o.customer c WHERE c.name LIKE '%los') ", |
| // 10 |
| "select Distinct c.name FROM Customer c, IN(c.orders) co " + |
| "WHERE co.amount >= SOME" + |
| "(Select o.amount FROM Order o, IN(o.lineitems) l WHERE l.quantity = 2)", |
| // 11 |
| "select c FROM Customer c WHERE EXISTS" + |
| " (SELECT o FROM c.orders o where o.amount > 1000)", |
| // 12 |
| "select c FROM Customer c WHERE EXISTS" + |
| " (SELECT o FROM c.orders o)", |
| // 13 |
| "SELECT c FROM Customer c WHERE " |
| + "(SELECT COUNT(o) FROM c.orders o) > 10", |
| // 14 |
| "SELECT o FROM Order o JOIN o.customer c WHERE c.name = " |
| + "SOME (SELECT a.name FROM c.accounts a)", |
| |
| }; |
| |
| public void testSubquery2() { |
| EntityManager em = emf.createEntityManager(); |
| for (int i = 0; i < querys2.length; i++) { |
| String q = querys2[i]; |
| System.err.println(">>> JPQL JPA2 :[ " + i + "]" +q); |
| List rs = em.createQuery(q).getResultList(); |
| assertEquals(0, rs.size()); |
| } |
| em.close(); |
| } |
| |
| |
| public void testSubquery() { |
| JDBCConfiguration conf = (JDBCConfiguration) emf.getConfiguration(); |
| DBDictionary dict = conf.getDBDictionaryInstance(); |
| |
| EntityManager em = emf.createEntityManager(); |
| for (int i = 0; i < querys_jpa20.length; i++) { |
| String q = querys_jpa20[i]; |
| System.err.println(">>> JPQL JPA2 :[ " + i + "]" +q); |
| List rs = em.createQuery(q).getResultList(); |
| assertEquals(0, rs.size()); |
| } |
| for (int i = 0; i < querys.length; i++) { |
| String q = querys[i]; |
| System.err.println(">>> JPQL: [ " + i + "]"+q); |
| List rs = em.createQuery(q).getResultList(); |
| assertEquals(0, rs.size()); |
| } |
| |
| // MySQL throws exception for the jpql in the updates: |
| // "You can't specify target table 'xxx' for update in FROM clause". The MySQL manual mentions |
| // this at the bottom of the UPDATE documentation(http://dev.mysql.com/doc/refman/5.0/en/update.html): |
| // Currently, you cannot update a table and select from the same table in a subquery. |
| |
| if (dict instanceof MySQLDictionary || dict instanceof MariaDBDictionary) |
| return; |
| |
| em.getTransaction().begin(); |
| for (String update : updates) { |
| int updateCount = em.createQuery(update).executeUpdate(); |
| assertEquals(0, updateCount); |
| } |
| |
| em.getTransaction().rollback(); |
| em.close(); |
| } |
| |
| /** |
| * Verify a sub query can contain MAX and additional date comparisons |
| * without losing the correct alias information. This sort of query |
| * originally caused problems for DBDictionaries which used DATABASE |
| * syntax. |
| */ |
| public void testSubSelectMaxDateRange() { |
| String query = |
| "SELECT e,d from Employee e, Dependent d " |
| + "WHERE e.empId = :empid " |
| + "AND d.id.empid = (SELECT MAX (e2.empId) FROM Employee e2) " |
| + "AND d.id.effDate > :minDate " |
| + "AND d.id.effDate < :maxDate "; |
| EntityManager em = emf.createEntityManager(); |
| Query q = em.createQuery(query); |
| q.setParameter("empid", (long) 101); |
| q.setParameter("minDate", new Date(100)); |
| q.setParameter("maxDate", new Date(100000)); |
| q.getResultList(); |
| em.close(); |
| } |
| |
| public void testUpdateWithCorrelatedSubquery() { |
| String update = "update Customer c set c.creditRating = ?1 where EXISTS" + |
| " (select o from in(c.orders) o)"; |
| EntityManager em = emf.createEntityManager(); |
| em.getTransaction().begin(); |
| CreditRating creditRating = CreditRating.GOOD; |
| int updateCount = em.createQuery(update). |
| setParameter(1, creditRating).executeUpdate(); |
| em.getTransaction().rollback(); |
| em.close(); |
| } |
| } |