HHH-4724 - query.multiselect() on a CriteriaQuery<Tuple> returns List<Object[]> instead of List<Tuple>

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18444 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Steve Ebersole 2010-01-07 20:41:54 +00:00
parent 1dfa028169
commit ef30ca4408
6 changed files with 175 additions and 86 deletions

View File

@ -41,12 +41,15 @@ import javax.persistence.PersistenceException;
import javax.persistence.PessimisticLockException;
import javax.persistence.Query;
import javax.persistence.TransactionRequiredException;
import javax.persistence.Tuple;
import javax.persistence.TupleElement;
import javax.persistence.TypedQuery;
import javax.persistence.PessimisticLockScope;
import javax.persistence.LockTimeoutException;
import javax.persistence.QueryTimeoutException;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Selection;
import javax.persistence.metamodel.Metamodel;
import javax.persistence.spi.PersistenceUnitTransactionType;
import javax.transaction.Status;
@ -60,6 +63,7 @@ import org.slf4j.LoggerFactory;
import org.hibernate.*;
import org.hibernate.cfg.Environment;
import org.hibernate.ejb.criteria.ValueHandlerFactory;
import org.hibernate.ejb.criteria.expression.CompoundSelectionImpl;
import org.hibernate.ejb.transaction.JoinableCMTTransaction;
import org.hibernate.ejb.util.ConfigurationHelper;
import org.hibernate.ejb.criteria.CriteriaQueryCompiler;
@ -68,6 +72,7 @@ import org.hibernate.engine.SessionImplementor;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.transaction.TransactionFactory;
import org.hibernate.transform.BasicTransformerAdapter;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.util.CollectionHelper;
import org.hibernate.util.JTAHelper;
@ -145,15 +150,24 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage
public <T> TypedQuery<T> createQuery(
String jpaqlString,
Class<T> resultClass,
Selection selection,
Options options) {
try {
org.hibernate.Query hqlQuery = getSession().createQuery( jpaqlString );
if ( options.getValueHandlers() != null ) {
hqlQuery.setResultTransformer( new ValueConversionResultTransformer( options.getValueHandlers() ) );
}
else {
if ( options.getValueHandlers() == null ) {
options.getResultMetadataValidator().validate( hqlQuery.getReturnTypes() );
}
// determine if we need a result transformer
List tupleElements = Tuple.class.equals( resultClass )
? ( (CompoundSelectionImpl<Tuple>) selection ).getCompoundSelectionItems()
: null;
if ( options.getValueHandlers() != null || tupleElements != null ) {
hqlQuery.setResultTransformer(
new CriteriaQueryTransformer( options.getValueHandlers(), tupleElements )
);
}
return new QueryImpl<T>( hqlQuery, this, options.getNamedParameterExplicitTypes() );
}
catch ( HibernateException he ) {
@ -161,23 +175,108 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage
}
}
private static class ValueConversionResultTransformer extends BasicTransformerAdapter {
private List<ValueHandlerFactory.ValueHandler> valueHandlers;
private static class CriteriaQueryTransformer extends BasicTransformerAdapter {
private final List<ValueHandlerFactory.ValueHandler> valueHandlers;
private final List tupleElements;
private ValueConversionResultTransformer(List<ValueHandlerFactory.ValueHandler> valueHandlers) {
private CriteriaQueryTransformer(List<ValueHandlerFactory.ValueHandler> valueHandlers, List tupleElements) {
// todo : should these 2 sizes match *always*?
this.valueHandlers = valueHandlers;
this.tupleElements = tupleElements;
}
@Override
public Object transformTuple(Object[] tuple, String[] aliases) {
Object[] result = new Object[ tuple.length ];
for ( int i = 0; i < tuple.length; i++ ) {
ValueHandlerFactory.ValueHandler valueHandler = valueHandlers.get( i );
result[i] = valueHandler == null
? tuple[i]
: valueHandler.convert( tuple[i] );
final Object[] valueHandlerResult;
if ( valueHandlers == null ) {
valueHandlerResult = tuple;
}
else {
valueHandlerResult = new Object[ tuple.length ];
for ( int i = 0; i < tuple.length; i++ ) {
ValueHandlerFactory.ValueHandler valueHandler = valueHandlers.get( i );
valueHandlerResult[i] = valueHandler == null
? tuple[i]
: valueHandler.convert( tuple[i] );
}
}
return tupleElements == null
? valueHandlerResult.length == 1 ? valueHandlerResult[0] : valueHandlerResult
: new TupleImpl( tuple );
}
private class TupleImpl implements Tuple {
private final Object[] tuples;
private TupleImpl(Object[] tuples) {
if ( tuples.length != tupleElements.size() ) {
throw new IllegalArgumentException(
"Size mismatch between tuple result [" + tuples.length
+ "] and expected tuple elements [" + tupleElements.size() + "]"
);
}
this.tuples = tuples;
}
public <X> X get(TupleElement<X> tupleElement) {
int index = tupleElements.indexOf( tupleElement );
if ( index < 0 ) {
throw new IllegalArgumentException( "Requested tuple element did not correspond to element in the result tuple" );
}
// index should be "in range" by nature of size check in ctor
return (X) tuples[index];
}
public Object get(String alias) {
int index = -1;
if ( alias != null ) {
alias = alias.trim();
if ( alias.length() > 0 ) {
int i = 0;
for ( TupleElement selection : (List<TupleElement>) tupleElements ) {
if ( alias.equals( selection.getAlias() ) ) {
index = i;
break;
}
i++;
}
}
}
if ( index < 0 ) {
throw new IllegalArgumentException(
"Given alias [" + alias + "] did not correspond to an element in the result tuple"
);
}
// index should be "in range" by nature of size check in ctor
return tuples[index];
}
public <X> X get(String alias, Class<X> type) {
return (X) get( alias );
}
public Object get(int i) {
if ( i >= tuples.length ) {
throw new IllegalArgumentException(
"Given index [" + i + "] was outside the range of result tuple size [" + tuples.length + "] "
);
}
return tuples[i];
}
public <X> X get(int i, Class<X> type) {
return (X) get( i );
}
public Object[] toArray() {
return tuples;
}
public List<TupleElement<?>> getElements() {
return (List<TupleElement<?>>) tupleElements;
}
return result.length == 1 ? result[0] : result;
}
}

View File

@ -26,6 +26,7 @@ package org.hibernate.ejb;
import javax.persistence.PersistenceException;
import javax.persistence.LockModeType;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.Selection;
import org.hibernate.HibernateException;
import org.hibernate.StaleStateException;
@ -141,9 +142,10 @@ public interface HibernateEntityManagerImplementor extends HibernateEntityManage
*
* @param jpaqlString The criteria query rendered as a JPA QL string
* @param resultClass The result type (the type expected in the result list)
* @param selection The selection(s)
* @param options The options to use to build the query.
* @param <T> The query type
* @return The typed query
*/
public <T> TypedQuery<T> createQuery(String jpaqlString, Class<T> resultClass, Options options);
public <T> TypedQuery<T> createQuery(String jpaqlString, Class<T> resultClass, Selection selection, Options options);
}

View File

@ -154,6 +154,7 @@ public class CriteriaQueryCompiler implements Serializable {
TypedQuery<T> jpaqlQuery = entityManager.createQuery(
renderedCriteriaQuery.getQueryString(),
criteriaQuery.getResultType(),
criteriaQuery.getSelection(),
new HibernateEntityManagerImplementor.Options() {
public List<ValueHandlerFactory.ValueHandler> getValueHandlers() {
return renderedCriteriaQuery.getValueHandlers();

View File

@ -1,43 +0,0 @@
package org.hibernate.ejb.criteria.tuple;
import javax.persistence.Table;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;
/**
* @author Emmanuel Bernard
*/
@Entity
@Table(name="tup_cust")
public class Customer {
private Long id;
private String name;
private Integer age;
@Id
@GeneratedValue
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 Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}

View File

@ -3,72 +3,92 @@ package org.hibernate.ejb.criteria.tuple;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Tuple;
import javax.persistence.TupleElement;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import org.hibernate.ejb.test.TestCase;
import org.hibernate.ejb.metamodel.AbstractMetamodelSpecificTest;
import org.hibernate.ejb.metamodel.Customer;
import org.hibernate.ejb.metamodel.Customer_;
/**
* @author Emmanuel Bernard
*/
public class TupleCriteriaTest extends TestCase {
public class TupleCriteriaTest extends AbstractMetamodelSpecificTest {
public void testArray() {
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
Customer c1 = new Customer();
c1.setId( "c1" );
c1.setAge( 18 );
c1.setName( "Bob" );
em.getTransaction().begin();
em.persist( c1 );
em.flush();
em.getTransaction().commit();
em.close();
em = factory.createEntityManager();
em.getTransaction().begin();
final CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Object[]> q = cb.createQuery(Object[].class);
Root<Customer> c = q.from(Customer.class);
q.select( cb.array( c.get(Customer_.name), c.get(Customer_.age) ) );
List<Object[]> result = em.createQuery(q).getResultList();
assertEquals( 1, result.size() );
assertEquals( c1.getName(), result.get( 0 )[0] );
assertEquals( c1.getAge(), result.get( 0 )[1] );
em.getTransaction().commit();
em.close();
em.getTransaction().rollback();
em = factory.createEntityManager();
em.getTransaction().begin();
em.createQuery( "delete Customer" ).executeUpdate();
em.getTransaction().commit();
em.close();
}
public void testTuple() {
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
Customer c1 = new Customer();
c1.setId( "c1" );
c1.setAge( 18 );
c1.setName( "Bob" );
em.getTransaction().begin();
em.persist( c1 );
em.flush();
em.getTransaction().commit();
em.close();
final CriteriaBuilder cb = em.getCriteriaBuilder();
em = factory.createEntityManager();
em.getTransaction().begin();
final CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Tuple> criteria = builder.createTupleQuery();
Root<Customer> customerRoot = criteria.from( Customer.class );
Path<String> namePath = customerRoot.get( Customer_.name );
Path<Integer> agePath = customerRoot.get( Customer_.age );
agePath.alias( "age" );
criteria.multiselect( namePath, agePath );
List<Tuple> results = em.createQuery( criteria ).getResultList();
assertEquals( 1, results.size() );
Object resultElement = results.get( 0 );
assertTrue( "Check result 'row' as Tuple", Tuple.class.isInstance( resultElement ) );
Tuple resultElementTuple = (Tuple) resultElement;
Object[] tupleArray = resultElementTuple.toArray();
assertEquals( 2, tupleArray.length );
assertEquals( tupleArray[0], resultElementTuple.get( 0 ) );
assertEquals( resultElementTuple.get( namePath ), resultElementTuple.get( 0 ) );
assertEquals( tupleArray[1], resultElementTuple.get( 1 ) );
assertEquals( resultElementTuple.get( agePath ), resultElementTuple.get( 1 ) );
assertEquals( resultElementTuple.get( agePath ), resultElementTuple.get( "age" ) );
em.getTransaction().commit();
em.close();
CriteriaQuery<Tuple> q = cb.createTupleQuery();
Root<Customer> c = q.from(Customer.class);
Path<String> tname = c.get(Customer_.name);
q.multiselect( tname, c.get(Customer_.age).alias("age") );
List<Tuple> result = em.createQuery(q).getResultList();
assertEquals( 1, result.size() );
//FIXME uncomment when HHH-4724 is fixed
// assertEquals( c1.getName(), result.get(0).get(tname) );
// assertEquals( c1.getAge(), result.get(0).get("age") );
em.getTransaction().rollback();
em = factory.createEntityManager();
em.getTransaction().begin();
em.createQuery( "delete Customer" ).executeUpdate();
em.getTransaction().commit();
em.close();
}
public Class[] getAnnotatedClasses() {
return new Class[] {
Customer.class
};
}
}

View File

@ -45,6 +45,7 @@ import javax.persistence.Table;
public class Customer implements java.io.Serializable {
private String id;
private String name;
private Integer age;
private Address home;
private Address work;
private Country country;
@ -96,6 +97,15 @@ public class Customer implements java.io.Serializable {
this.name = v;
}
@Column(name = "AGE")
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Embedded
public Country getCountry() {
return country;