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:
parent
1dfa028169
commit
ef30ca4408
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue