OPENJPA-1191 :

Add Tuple implementation and unit tests. Also added some basic typed result and multiselect testscases

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@796773 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Michael Dick 2009-07-22 15:47:47 +00:00
parent c47bd9ee3a
commit 49f000f54a
18 changed files with 1152 additions and 7 deletions

View File

@ -0,0 +1,56 @@
/*
* 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.results;
import javax.persistence.*;
@Entity
@Table(name="CRIT_RES_ITEM")
public class Item {
@Id
@GeneratedValue
private int id;
@ManyToOne
private Order order;
@OneToOne(cascade=CascadeType.PERSIST)
private Producer producer;
public Producer getProduct() {
return producer;
}
public void setProduct(Producer product) {
this.producer = product;
}
public int getId() {
return id;
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
}

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.results;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.StaticMetamodel;
@StaticMetamodel(value=Item.class)
public class Item_ {
public static volatile SingularAttribute<Item, Order> order;
public static volatile SingularAttribute<Item, Producer> producer;
}

View File

@ -0,0 +1,89 @@
/*
* 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.results;
import java.util.Date;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name="CRIT_RES_ORD")
public class Order {
@Id
long id;
boolean filled;
Date date;
@OneToMany(cascade=CascadeType.PERSIST)
Set<Item> items;
@ManyToOne
Shop shop;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public boolean isFilled() {
return filled;
}
public void setFilled(boolean filled) {
this.filled = filled;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public Set<Item> getItems() {
return items;
}
public void setItems(Set<Item> items) {
this.items = items;
}
public Shop getShop() {
return shop;
}
public void setShop(Shop shop) {
this.shop = shop;
}
public String toPrettyString() {
return String.format("Order: %s date=%s filled=%s",getId(), getDate(), isFilled());
}
}

View File

@ -0,0 +1,51 @@
/*
* 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.results;
public class OrderProducer {
private Order order;
private Producer producer;
public OrderProducer(Order o, Producer p) {
order = o;
producer = p;
}
public String toString() {
return order.toPrettyString() + " " + producer.toString();
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
public Producer getProducer() {
return producer;
}
public void setProduct(Producer producer) {
this.producer = producer;
}
}

View File

@ -0,0 +1,34 @@
/*
* 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.results;
import java.util.Date;
import javax.persistence.metamodel.SetAttribute;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.StaticMetamodel;
@StaticMetamodel(value = Order.class)
public class Order_ {
public static volatile SetAttribute<Order, Item> items;
public static volatile SingularAttribute<Order, Boolean> filled;
public static volatile SingularAttribute<Order, Date> date;
public static volatile SingularAttribute<Order, Shop> shop;
}

View File

@ -0,0 +1,61 @@
/*
* 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.results;
import javax.persistence.Entity;
import javax.persistence.OneToOne;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Producer {
@Id
@GeneratedValue
private int id;
@OneToOne
Item item;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Item getItem() {
return item;
}
public void setItem(Item item) {
this.item = item;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,28 @@
/*
* 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.results;
import javax.persistence.metamodel.StaticMetamodel;
import javax.persistence.metamodel.SingularAttribute;
@StaticMetamodel(value=Producer.class)
public class Producer_ {
public static volatile SingularAttribute<Producer, Item> item;
}

View File

@ -0,0 +1,64 @@
/*
* 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.results;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.util.Set;
@Entity
@Table(name="CRIT_RES_SHOP")
public class Shop {
@Id
long id;
String name;
@OneToMany(cascade=CascadeType.PERSIST)
Set<Order> orders;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Order> getOrders() {
return orders;
}
public void setOrders(Set<Order> orders) {
this.orders = orders;
}
}

View File

@ -0,0 +1,30 @@
/*
* 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.results;
import javax.persistence.metamodel.SetAttribute;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.StaticMetamodel;
@StaticMetamodel(value=Shop.class)
public class Shop_ {
public static volatile SingularAttribute<Shop, String> name;
public static volatile SetAttribute<Shop, Order>orders;
}

View File

@ -0,0 +1,234 @@
/*
* 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.results;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.HashSet;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.Tuple;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.QueryBuilder;
import javax.persistence.criteria.Root;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
public class TestTypedResults extends SingleEMFTestCase {
private static final int N_ORDERS = 15;
private static final int N_ITEMS_PER_ORDER = 3;
// use short data format
private static final String[] ORDER_DATES =
{ "3/12/2008 1:00 PM", "10/01/2008 1:51 AM", "12/12/2008 10:01 AM", "5/21/2009 3:23 PM" };
DateFormat df = DateFormat.getInstance(); // uses SHORT dateformat by default
public void setUp() throws Exception {
setUp(Order.class, Item.class, Shop.class, Producer.class);
populate();
}
public void populate() throws ParseException {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Shop s = new Shop();
Order order;
Item item;
Producer p;
s.setId(1);
s.setName("eBay.com");
s.setOrders(new HashSet<Order>());
for (int i = 1; i <= N_ORDERS; i++) {
order = new Order();
order.setId(i);
order.setDate(df.parse(ORDER_DATES[i % ORDER_DATES.length]));
order.setFilled(i % 2 == 0 ? true : false);
order.setShop(s);
order.setItems(new HashSet<Item>());
s.getOrders().add(order);
for (int j = 1; j <= N_ITEMS_PER_ORDER; j++) {
item = new Item();
item.setOrder(order);
order.getItems().add(item);
p = new Producer();
p.setName("filler");
p.setItem(item);
item.setProduct(p);
}
}
em.persist(s);
em.getTransaction().commit();
em.close();
}
public void tearDown() throws Exception {
super.tearDown();
}
/**
* Verify that a query using a date field works the same with JPQL, JPQL (typed), Criteria (typed), and via a
* NativeQuery
*
* @throws Exception
*/
public void testTypedJPQLQuery() {
EntityManager em = emf.createEntityManager();
Query jpqlQuery = em.createQuery("Select o from Order o where o.filled = true");
// Don't suppress warnings.
List<Order> jpqlResults = jpqlQuery.getResultList();
assertEquals(N_ORDERS / 2, jpqlResults.size());
TypedQuery<Order> jpqlTypedQuery = em.createQuery("Select o from Order o where o.filled = true", Order.class);
List<Order> jpqlTypedResults = jpqlTypedQuery.getResultList();
assertEquals(N_ORDERS / 2, jpqlTypedResults.size());
// create the same query and get typed results.
QueryBuilder qb = em.getQueryBuilder();
CriteriaQuery<Order> cq = qb.createQuery(Order.class);
Root<Order> order = cq.from(Order.class);
cq.select(order).where(qb.equal(order.get(Order_.filled), Boolean.TRUE));
TypedQuery<Order> typedCriteriaQuery = em.createQuery(cq);
List<Order> typedCriteriaResults = typedCriteriaQuery.getResultList();
assertEquals(N_ORDERS / 2, typedCriteriaResults.size());
Query nativeQ = em.createNativeQuery("SELECT * FROM CRIT_RES_ORD o WHERE (o.filled = 1)", Order.class);
// Don't suppress warnings.
List<Order> typedNativeResults = nativeQ.getResultList();
assertEquals(N_ORDERS / 2, typedNativeResults.size());
for (Order o : jpqlResults) {
assertTrue(jpqlTypedResults.contains(o));
assertTrue(typedCriteriaResults.contains(o));
assertTrue(typedNativeResults.contains(o));
}
em.close();
}
/**
* Verify that a query using a date field works the same with JPQL, JPQL (typed), Criteria (typed), and via a
* NativeQuery
*
* @throws Exception
*/
public void testDateQuery() throws Exception {
EntityManager em = emf.createEntityManager();
Query jpqlQuery = em.createQuery("Select o from Order o where o.date < :maxDate");
jpqlQuery.setParameter("maxDate", df.parse(ORDER_DATES[2]));
List<Order> jpqlResults = jpqlQuery.getResultList();
assertEquals(N_ORDERS / 2, jpqlResults.size());
TypedQuery<Order> typedJpqlQuery = em.createQuery("Select o from Order o where o.date < :maxDate", Order.class);
typedJpqlQuery.setParameter("maxDate", df.parse(ORDER_DATES[2]));
List<Order> typedJpqlResults = typedJpqlQuery.getResultList();
assertEquals(N_ORDERS / 2, typedJpqlResults.size());
QueryBuilder qb = em.getQueryBuilder();
CriteriaQuery<Order> criteriaQuery = qb.createQuery(Order.class);
Root<Order> order = criteriaQuery.from(Order.class);
criteriaQuery.select(order).where(qb.lessThan(order.get(Order_.date), df.parse(ORDER_DATES[2])));
TypedQuery<Order> tq = em.createQuery(criteriaQuery);
List<Order> criteriaResults = tq.getResultList();
assertEquals(N_ORDERS / 2, criteriaResults.size());
String parm = new java.sql.Timestamp(df.parse(ORDER_DATES[2]).getTime()).toString();
Query nativeQuery =
em.createNativeQuery("Select * from CRIT_RES_ORD o WHERE (o.date < '" + parm + "')", Order.class);
List<Order> nativeResults = nativeQuery.getResultList();
assertEquals(N_ORDERS / 2, nativeResults.size());
for (Order o : jpqlResults) {
assertTrue(typedJpqlResults.contains(o));
assertTrue(criteriaResults.contains(o));
assertTrue(nativeResults.contains(o));
}
em.close();
}
/**
* Testcase to verify that selecting multiple results in a variety of ways returns the same results. Results are
* returned via a normal Object [] (JPQL), Tuple (Criteria), and a custom tuple (Criteria.construct)
*
* @throws Exception
*/
public void testMultiSelect() throws Exception {
// get results from traditional JPQL
EntityManager em = emf.createEntityManager();
Query jpqlQuery =
em.createQuery("SELECT o, p from Order o JOIN o.items i JOIN i.producer p WHERE o.filled = true");
// don't suppress warnings.
List<Object[]> jpqlResults = jpqlQuery.getResultList();
// Get results using Tuple
QueryBuilder qb = em.getQueryBuilder();
CriteriaQuery<Tuple> criteriaQuery = qb.createTupleQuery();
Root<Order> order = criteriaQuery.from(Order.class);
Join<Item, Producer> producer = order.join(Order_.items).join(Item_.producer);
criteriaQuery.select(qb.tuple(order, producer));
criteriaQuery.where(qb.equal(order.get(Order_.filled), Boolean.TRUE));
TypedQuery<Tuple> eq = em.createQuery(criteriaQuery);
List<Tuple> criteriaResults = eq.getResultList();
// Get results using custom class
CriteriaQuery<OrderProducer> constructQuery = qb.createQuery(OrderProducer.class);
Root<Order> order2 = constructQuery.from(Order.class);
Join<Item, Producer> producer2 = order.join(Order_.items).join(Item_.producer);
constructQuery.select(qb.construct(OrderProducer.class, order2, producer2));
constructQuery.where(qb.equal(order2.get(Order_.filled), Boolean.TRUE));
TypedQuery<OrderProducer> typedQuery = em.createQuery(constructQuery);
List<OrderProducer> constructResults = typedQuery.getResultList();
assertEquals(N_ORDERS / 2 * N_ITEMS_PER_ORDER, jpqlResults.size());
assertEquals(N_ORDERS / 2 * N_ITEMS_PER_ORDER, criteriaResults.size());
assertEquals(N_ORDERS / 2 * N_ITEMS_PER_ORDER, constructResults.size());
for (Object[] os : jpqlResults) {
assertEquals(2, os.length);
assertTrue(os[0] instanceof Order);
assertTrue(os[1] instanceof Producer);
}
// cheap way to ensure that we have the same contents.
// if needed an orderBy clause can be added to make this more robust.
Object[] jpqlTuple;
Tuple criteriaTuple;
OrderProducer constructTuple;
for (int i = 0; i < jpqlResults.size(); i++) {
jpqlTuple = jpqlResults.get(i);
criteriaTuple = criteriaResults.get(i);
constructTuple = constructResults.get(i);
assertEquals(jpqlTuple[0], criteriaTuple.get(0));
assertEquals(jpqlTuple[1], criteriaTuple.get(1));
assertEquals(jpqlTuple[0], constructTuple.getOrder());
assertEquals(jpqlTuple[1], constructTuple.getProducer());
}
em.close();
}
}

View File

@ -945,7 +945,8 @@ public class EntityManagerImpl
}
public <T> TypedQuery<T> createQuery(String query, Class<T> resultClass) {
throw new UnsupportedOperationException();
return new QueryImpl(this, _ret,
_broker.newQuery(JPQLParser.LANG_JPQL, query));
}
public OpenJPAQuery createQuery(String query) {

View File

@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.persistence;
import javax.persistence.TupleElement;
@ -30,6 +31,7 @@ import javax.persistence.TupleElement;
public class TupleElementImpl<X> implements TupleElement<X> {
private String _alias;
protected final Class<X> _cls;
private X _value;
protected TupleElementImpl(Class<X> cls) {
_cls = cls;
@ -46,4 +48,15 @@ public class TupleElementImpl<X> implements TupleElement<X> {
public Class<X> getJavaType() {
return _cls;
}
public X getValue() {
return _value;
}
@SuppressWarnings("unchecked")
public void setValue(Object x) {
// X is unknown at compile time in TupleImpl when we construct a new Tuple.
// so we're stuck with this ugly cast.
_value = (X) x;
}
}

View File

@ -0,0 +1,173 @@
/*
* 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;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Tuple;
import javax.persistence.TupleElement;
public class TupleImpl implements Tuple {
List<TupleElement<?>> elements = new ArrayList<TupleElement<?>>();
/**
* Get the value of the specified tuple element.
*
* @param tupleElement
* tuple element
* @return value of tuple element
* @throws IllegalArgumentException
* if tuple element does not correspond to an element in the query result tuple
*/
public <X> X get(TupleElement<X> tupleElement) {
if (!elements.contains(tupleElement)) {
throw new IllegalArgumentException("tupleElement was not found in this tuple"); // TODO MDD improve
}
TupleElementImpl<X> impl = (TupleElementImpl<X>) tupleElement;
return impl.getValue();
}
/**
* Get the value of the tuple element to which the specified alias has been assigned.
*
* @param alias
* alias assigned to tuple element
* @param type
* of the tuple element
* @return value of the tuple element
* @throws IllegalArgumentException
* if alias does not correspond to an element in the query result tuple or element cannot be assigned to
* the specified type
*/
@SuppressWarnings("unchecked")
public <X> X get(String alias, Class<X> type) {
if (type == null) {
throw new IllegalArgumentException("Type was null");
}
Object rval = get(alias);
if (!type.isAssignableFrom(rval.getClass())) {
throw new IllegalArgumentException(String.format(
"TupleElement type did not match for alias: %s. Provided type: %s actual type: %s", alias, type, rval
.getClass().toString()));
}
return (X) rval;
}
/**
* Get the value of the tuple element to which the specified alias has been assigned.
*
* @param alias
* alias assigned to tuple element
* @return value of the tuple element
* @throws IllegalArgumentException
* if alias does not correspond to an element in the query result tuple
*/
public Object get(String alias) {
if (alias == null) {
throw new IllegalArgumentException(String.format("Alias was null."));
}
for (TupleElement<?> te : elements) {
if (alias.equals(te.getAlias())) {
return ((TupleElementImpl<?>) te).getValue();
}
}
throw new IllegalArgumentException(String.format("Alias %s was not found.", alias));
}
/**
* Get the value of the element at the specified position in the result tuple. The first position is 0.
*
* @param i
* position in result tuple
* @param type
* type of the tuple element
* @return value of the tuple element
* @throws IllegalArgumentException
* if i exceeds length of result tuple or element cannot be assigned to the specified type
*/
@SuppressWarnings("unchecked")
public <X> X get(int i, Class<X> type) {
if (type == null) {
throw new IllegalArgumentException("Type was null");
}
Object rval = get(i);
if(! type.isAssignableFrom(rval.getClass())) {
throw new IllegalArgumentException(String.format(
"Type did not match for position: %d. Provided type: %s actual type: %s", i, type.getClass(),
rval.getClass()));
}
return (X) rval;
}
/**
* Get the value of the element at the specified position in the result tuple. The first position is 0.
*
* @param i
* position in result tuple
* @return value of the tuple element
* @throws IllegalArgumentException
* if i exceeds length of result tuple
*/
public Object get(int i) {
if (i > elements.size()) {
throw new IllegalArgumentException(String.format(
"Attempt to read TupleElement %d when there are only %d elements available", i, elements.size()));
}
if (i == -1) {
throw new IllegalArgumentException("Cannot obtain the -1th element in this tuple. Thank you for playing");
}
return toArray()[i];
}
/**
* Return the values of the result tuple elements as an array.
*
* @return tuple element values
*/
public Object[] toArray() {
Object[] rval = new Object[elements.size()];
int i = 0;
for (TupleElement<?> tupleElement : elements) {
rval[i] = ((TupleElementImpl<?>) tupleElement).getValue();
i++;
}
return rval;
}
/**
* Return the tuple elements
*
* @return tuple elements
*/
public List<TupleElement<?>> getElements() {
return elements;
}
@SuppressWarnings("unchecked")
public void put(Object key, Object value) {
// TODO check for duplicate aliases?
TupleElementImpl<?> element = new TupleElementImpl(value.getClass());
element.setAlias((String) key);
element.setValue(value);
elements.add(element);
}
}

View File

@ -678,8 +678,7 @@ public class CriteriaBuilder implements QueryBuilder, ExpressionParser {
return new Expressions.IsNull((ExpressionImpl<?> )x);
}
public CompoundSelection<Tuple> tuple(Selection<?>... arg0) {
// TODO Auto-generated method stub
return null;
public CompoundSelection<Tuple> tuple(Selection<?>... selections) {
return new TupleSelection<Tuple>(Tuple.class, selections);
}
}

View File

@ -40,6 +40,7 @@ import org.apache.openjpa.kernel.exps.ExpressionFactory;
import org.apache.openjpa.kernel.exps.QueryExpressions;
import org.apache.openjpa.kernel.exps.Value;
import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.persistence.TupleImpl;
import org.apache.openjpa.persistence.meta.AbstractManagedType;
import org.apache.openjpa.persistence.meta.Members;
import org.apache.openjpa.persistence.meta.MetamodelImpl;
@ -242,7 +243,12 @@ public class CriteriaExpressionBuilder {
ExpressionFactory factory, CriteriaQueryImpl<?> q, MetamodelImpl model,
Map<Expression<?>, Value> exp2Vals) {
for (Selection<?> s : selections) {
if (s instanceof NewInstanceSelection<?>) {
if(s instanceof TupleSelection<?> ) {
exps.resultClass = TupleImpl.class;
getProjections(exps, ((TupleSelection<?>)s).getSelectionItems(), projections, aliases,
clauses, factory, q, model, exp2Vals);
}
else if (s instanceof NewInstanceSelection<?>) {
exps.resultClass = s.getJavaType();
getProjections(exps, ((NewInstanceSelection<?>)s).getSelectionItems(), projections, aliases,
clauses, factory, q, model, exp2Vals);

View File

@ -24,8 +24,6 @@ import java.util.List;
import javax.persistence.criteria.CompoundSelection;
import javax.persistence.criteria.Selection;
import org.apache.openjpa.persistence.TupleElementImpl;
/**
* A selection item that constructs new instance of a user-defined class with arguments specified as other selected
* items.

View File

@ -0,0 +1,10 @@
package org.apache.openjpa.persistence.criteria;
import javax.persistence.criteria.Selection;
public class TupleSelection<Tuple> extends NewInstanceSelection<Tuple> {
public TupleSelection(final Class<Tuple> cls, final Selection<?>[] selections) {
super(cls, selections);
}
}

View File

@ -0,0 +1,269 @@
/*
* 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;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.Tuple;
import javax.persistence.TupleElement;
import junit.framework.TestCase;
/**
* Test the following methods in TupleImpl
* <ul>
* <li>Tuple</li>
* <ul>
* <li>get(int)</li>
* <li>get(int, Class<X>)</li>
* <li>get(String)</li>
* <li>get(String, Class<X>)</li>
* <li>get(TupleElement<X>)</li>
* <li>getElements()</li>
* <li>toArray()</li>
* </ul>
* <li>TupleImpl</li>
* <ul>
* <li>get(Object)</li>
* <li>getValues()</li>
* <li>put(Object, Object)</li>
* </ul>
* </ul>
*/
public class TestTupleImpl extends TestCase {
protected Order _order = new Order();
protected Product _product = new Product();
protected Item _item = new Item();
protected Store _store = new Store();
protected UrgentOrder _urgentOrder = new UrgentOrder();
protected Tuple tuple = getTuple();
public void setUp() {
}
/**
* Default tuple with some arbitrary pseudo entities
*
* @return
*/
protected TupleImpl getTuple() {
TupleImpl tuple = new TupleImpl();
tuple.put("alias1", _order);
tuple.put("alias2", _product);
tuple.put("alias3", _item);
tuple.put("alias4", _store);
tuple.put("alias5", _urgentOrder);
return tuple;
}
public void testGetInt() {
assertEquals(_order, tuple.get(0));
assertEquals(_product, tuple.get(1));
assertEquals(_item, tuple.get(2));
assertEquals(_store, tuple.get(3));
assertEquals(_urgentOrder, tuple.get(4));
// TODO MDD more tests
}
public void testGetIntNegativeValueThrowsException() {
try {
Object o = tuple.get(-1);
fail("tuple.get(-1) should throw IllegalArgumentException");
o.toString(); // clean up meaningless compiler warning
} catch (IllegalArgumentException iae) {
// expected
}
}
public void testGetIntOutOfRangeThrowsException() {
try {
Object o = tuple.get(10);
fail("tuple.get(i) where i > size of TupleElements should throw IllegalArgumentException");
o.toString(); // clean up meaningless compiler warning
} catch (IllegalArgumentException iae) {
// expected
}
try {
Object o = tuple.get(Integer.MAX_VALUE);
fail("tuple.get(i) where i > size of TupleElements should throw IllegalArgumentException");
o.toString(); // clean up meaningless compiler warning
} catch (IllegalArgumentException iae) {
// expected
}
}
public void testGetIntClass() {
assertEquals(_order, tuple.get(0, Order.class));
assertEquals(_product, tuple.get(1, Product.class));
assertEquals(_item, tuple.get(2, Item.class));
assertEquals(_store, tuple.get(3, Store.class));
assertEquals(_urgentOrder, tuple.get(4, UrgentOrder.class));
assertEquals(_urgentOrder, tuple.get(4, Order.class));
}
public void testGetIntClassExceptions() {
// duplicate code, but could be useful later if impl changes.
try {
Object o = tuple.get(-1, Order.class);
fail("tuple.get(-1) should throw IllegalArgumentException");
o.toString(); // clean up meaningless compiler warning
} catch (IllegalArgumentException iae) {
// expected
}
try {
Object o = tuple.get(200, Product.class);
fail("tuple.get(i) where i > size of TupleElements should throw IllegalArgumentException");
o.toString(); // clean up meaningless compiler warning
} catch (IllegalArgumentException iae) {
// expected
}
try {
Product product = (Product) tuple.get(0, Product.class);
fail("Expecting IllegalArgumentException when the wrong type is specified on Tuple.get(int, Class)");
product.toString(); // remove compiler warning for unused variable <sigh>
} catch (IllegalArgumentException iae) {
// expected
}
try {
tuple.get(0, UrgentOrder.class);
fail("Should not be able to upcast Order to UrgentOrder");
} catch (IllegalArgumentException iae) {
// expected
}
}
public void testGetString() {
assertEquals(_order, tuple.get("alias1"));
assertEquals(_product, tuple.get("alias2"));
assertEquals(_item, tuple.get("alias3"));
assertEquals(_store, tuple.get("alias4"));
assertEquals(_urgentOrder, tuple.get("alias5"));
try {
tuple.get("NotAnAlias");
fail("Expected an IllegalArgumentException for an alias that wasn't found");
} catch (IllegalArgumentException iae) {
// expected
}
try {
tuple.get((String) null);
fail("Expected an IllegalArgumentException for null alias");
} catch (IllegalArgumentException iae) {
// expected
}
try {
tuple.get("");
fail("Expected an IllegalArgumentException for null alias");
} catch (IllegalArgumentException iae) {
// expected
}
}
public void testGetStringClass() {
// TODO MDD convert to equals tests
assertTrue(tuple.get("alias1", Order.class) instanceof Order);
assertTrue(tuple.get("alias2", Product.class) instanceof Product);
assertTrue(tuple.get("alias3", Item.class) instanceof Item);
assertTrue(tuple.get("alias4", Store.class) instanceof Store);
try {
tuple.get("NotAnAlias", Product.class);
fail("Expected an IllegalArgumentException for an alias that wasn't found");
} catch (IllegalArgumentException iae) {
// expected
}
try {
tuple.get((String) null, Item.class);
fail("Expected an IllegalArgumentException for null alias");
} catch (IllegalArgumentException iae) {
// expected
}
try {
tuple.get("", Store.class);
fail("Expected an IllegalArgumentException for null alias");
} catch (IllegalArgumentException iae) {
// expected
}
}
public void testGetTupleElement() {
for (TupleElement<?> element : tuple.getElements()) {
assertEquals(((TupleElementImpl) element).getValue(), tuple.get(element));
}
}
public void testToArray() {
Object[] objects = tuple.toArray();
assertEquals(5, objects.length);
assertEquals(_order, objects[0]);
assertEquals(_product, objects[1]);
assertEquals(_item, objects[2]);
assertEquals(_store, objects[3]);
assertEquals(_urgentOrder, objects[4]);
}
@SuppressWarnings("unchecked")
public void testGetElements() {
List<TupleElement<?>> elements = tuple.getElements();
assertEquals(5, elements.size());
TupleElement<Order> orderElement = (TupleElement<Order>) elements.get(0);
TupleElement<Product> productElement = (TupleElement<Product>) elements.get(1);
TupleElement<Item> itemElement = (TupleElement<Item>) elements.get(2);
TupleElement<Store> storeElement = (TupleElement<Store>) elements.get(3);
TupleElement<UrgentOrder> urgentOrderElement = (TupleElement<UrgentOrder>) elements.get(4);
assertEquals("alias1", orderElement.getAlias());
assertEquals(Order.class, orderElement.getJavaType());
assertEquals("alias2", productElement.getAlias());
assertEquals(Product.class, productElement.getJavaType());
assertEquals("alias3", itemElement.getAlias());
assertEquals(Item.class, itemElement.getJavaType());
assertEquals("alias4", storeElement.getAlias());
assertEquals(Store.class, storeElement.getJavaType());
assertEquals("alias5", urgentOrderElement.getAlias());
assertEquals(UrgentOrder.class, urgentOrderElement.getJavaType());
}
// Begin fake entities.
class Order {
// public Order() {
// new Exception().printStackTrace();
// }
}
class UrgentOrder extends Order {
}
class Item {
}
class Product {
}
class Store {
}
}