org.hibernate.query.criteria.internal.CriteriaQueryImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate-core Show documentation
Show all versions of hibernate-core Show documentation
Hibernate's core ORM functionality
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or .
*/
package org.hibernate.query.criteria.internal;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.Tuple;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Selection;
import javax.persistence.criteria.Subquery;
import javax.persistence.metamodel.EntityType;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.jpa.spi.HibernateEntityManagerImplementor;
import org.hibernate.query.criteria.internal.compile.CompilableCriteria;
import org.hibernate.query.criteria.internal.compile.CriteriaInterpretation;
import org.hibernate.query.criteria.internal.compile.CriteriaQueryTypeQueryAdapter;
import org.hibernate.query.criteria.internal.compile.ImplicitParameterBinding;
import org.hibernate.query.criteria.internal.compile.InterpretedParameterMetadata;
import org.hibernate.query.criteria.internal.compile.RenderingContext;
import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.sql.ast.Clause;
import org.hibernate.type.Type;
import org.jboss.logging.Logger;
/**
* The Hibernate implementation of the JPA {@link CriteriaQuery} contract. Mostly a set of delegation to its
* internal {@link QueryStructure}.
*
* @author Steve Ebersole
*/
public class CriteriaQueryImpl extends AbstractNode implements CriteriaQuery, CompilableCriteria, Serializable {
private static final Logger log = Logger.getLogger( CriteriaQueryImpl.class );
private final Class returnType;
private final QueryStructure queryStructure;
private List orderSpecs = Collections.emptyList();
public CriteriaQueryImpl(
CriteriaBuilderImpl criteriaBuilder,
Class returnType) {
super( criteriaBuilder );
this.returnType = returnType;
this.queryStructure = new QueryStructure( this, criteriaBuilder );
}
protected QueryStructure getQueryStructure() {
return queryStructure;
}
@Override
public Class getResultType() {
return returnType;
}
// SELECTION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public CriteriaQuery distinct(boolean applyDistinction) {
queryStructure.setDistinct( applyDistinction );
return this;
}
@Override
public boolean isDistinct() {
return queryStructure.isDistinct();
}
@Override
@SuppressWarnings({ "unchecked" })
public Selection getSelection() {
return ( Selection ) queryStructure.getSelection();
}
public void applySelection(Selection selection) {
queryStructure.setSelection( selection );
}
@Override
public CriteriaQuery select(Selection selection) {
applySelection( selection );
return this;
}
@Override
@SuppressWarnings({ "unchecked" })
public CriteriaQuery multiselect(Selection... selections) {
return multiselect( Arrays.asList( selections ) );
}
@Override
@SuppressWarnings({ "unchecked" })
public CriteriaQuery multiselect(List> selections) {
final Selection selection;
if ( Tuple.class.isAssignableFrom( getResultType() ) ) {
selection = ( Selection ) criteriaBuilder().tuple( selections );
}
else if ( getResultType().isArray() ) {
selection = criteriaBuilder().array( getResultType(), selections );
}
else if ( Object.class.equals( getResultType() ) ) {
switch ( selections.size() ) {
case 0: {
throw new IllegalArgumentException(
"empty selections passed to criteria query typed as Object"
);
}
case 1: {
selection = ( Selection ) selections.get( 0 );
break;
}
default: {
selection = ( Selection ) criteriaBuilder().array( selections );
}
}
}
else {
selection = criteriaBuilder().construct( getResultType(), selections );
}
applySelection( selection );
return this;
}
// ROOTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public Set> getRoots() {
return queryStructure.getRoots();
}
@Override
public Root from(EntityType entityType) {
return queryStructure.from( entityType );
}
@Override
public Root from(Class entityClass) {
return queryStructure.from( entityClass );
}
// RESTRICTION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public Predicate getRestriction() {
return queryStructure.getRestriction();
}
@Override
public CriteriaQuery where(Expression expression) {
queryStructure.setRestriction( criteriaBuilder().wrap( expression ) );
return this;
}
@Override
public CriteriaQuery where(Predicate... predicates) {
// TODO : assuming this should be a conjunction, but the spec does not say specifically...
queryStructure.setRestriction( criteriaBuilder().and( predicates ) );
return this;
}
// GROUPING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public List> getGroupList() {
return queryStructure.getGroupings();
}
@Override
public CriteriaQuery groupBy(Expression... groupings) {
queryStructure.setGroupings( groupings );
return this;
}
@Override
public CriteriaQuery groupBy(List> groupings) {
queryStructure.setGroupings( groupings );
return this;
}
@Override
public Predicate getGroupRestriction() {
return queryStructure.getHaving();
}
@Override
public CriteriaQuery having(Expression expression) {
queryStructure.setHaving( criteriaBuilder().wrap( expression ) );
return this;
}
@Override
public CriteriaQuery having(Predicate... predicates) {
queryStructure.setHaving( criteriaBuilder().and( predicates ) );
return this;
}
// ORDERING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public List getOrderList() {
return orderSpecs;
}
@Override
public CriteriaQuery orderBy(Order... orders) {
if ( orders != null && orders.length > 0 ) {
orderSpecs = Arrays.asList( orders );
}
else {
orderSpecs = Collections.emptyList();
}
return this;
}
@Override
public CriteriaQuery orderBy(List orders) {
orderSpecs = orders;
return this;
}
@Override
public Set> getParameters() {
return queryStructure.getParameters();
}
@Override
public Subquery subquery(Class subqueryType) {
return queryStructure.subquery( subqueryType );
}
@Override
public void validate() {
// getRoots() is explicitly supposed to return empty if none defined, no need to check for null
if ( getRoots().isEmpty() ) {
throw new IllegalStateException( "No criteria query roots were specified" );
}
// if there is not an explicit selection, there is an *implicit* selection of the root entity provided only
// a single query root was defined.
if ( getSelection() == null && !hasImplicitSelection() ) {
throw new IllegalStateException( "No explicit selection and an implicit one could not be determined" );
}
}
/**
* If no explicit selection was defined, we have a condition called an implicit selection if the query specified
* a single {@link Root} and the java type of that {@link Root root's} model is the same as this criteria's
* {@link #getResultType() result type}.
*
* @return True if there is an explicit selection; false otherwise.
*/
private boolean hasImplicitSelection() {
if ( getRoots().size() != 1 ) {
return false;
}
Root root = getRoots().iterator().next();
Class javaType = root.getModel().getJavaType();
if ( javaType != null && javaType != returnType ) {
return false;
}
// if we get here, the query defined no selection but defined a single root of the same type as the
// criteria query return, so we use that as the implicit selection
//
// todo : should we put an implicit marker in the selection to this fact to make later processing easier?
return true;
}
@Override
public CriteriaInterpretation interpret(RenderingContext renderingContext) {
final StringBuilder jpaqlBuffer = new StringBuilder();
queryStructure.render( jpaqlBuffer, renderingContext );
renderOrderByClause( renderingContext, jpaqlBuffer );
final String jpaqlString = jpaqlBuffer.toString();
log.debugf( "Rendered criteria query -> %s", jpaqlString );
return new CriteriaInterpretation() {
@Override
@SuppressWarnings("unchecked")
public QueryImplementor buildCompiledQuery(
SharedSessionContractImplementor entityManager,
final InterpretedParameterMetadata parameterMetadata) {
final Map implicitParameterTypes = extractTypeMap( parameterMetadata.implicitParameterBindings() );
QueryImplementor jpaqlQuery = entityManager.createQuery(
jpaqlString,
getResultType(),
getSelection(),
new HibernateEntityManagerImplementor.QueryOptions() {
@Override
public List getValueHandlers() {
SelectionImplementor selection = (SelectionImplementor) queryStructure.getSelection();
return selection == null
? null
: selection.getValueHandlers();
}
@Override
public Map getNamedParameterExplicitTypes() {
return implicitParameterTypes;
}
@Override
public ResultMetadataValidator getResultMetadataValidator() {
return new HibernateEntityManagerImplementor.QueryOptions.ResultMetadataValidator() {
@Override
public void validate(Type[] returnTypes) {
SelectionImplementor selection = (SelectionImplementor) queryStructure.getSelection();
if ( selection != null ) {
if ( selection.isCompoundSelection() ) {
if ( returnTypes.length != selection.getCompoundSelectionItems().size() ) {
throw new IllegalStateException(
"Number of return values [" + returnTypes.length +
"] did not match expected [" +
selection.getCompoundSelectionItems().size() + "]"
);
}
}
else {
if ( returnTypes.length > 1 ) {
throw new IllegalStateException(
"Number of return values [" + returnTypes.length +
"] did not match expected [1]"
);
}
}
}
}
};
}
}
);
for ( ImplicitParameterBinding implicitParameterBinding : parameterMetadata.implicitParameterBindings() ) {
implicitParameterBinding.bind( jpaqlQuery );
}
return new CriteriaQueryTypeQueryAdapter(
entityManager,
jpaqlQuery,
parameterMetadata.explicitParameterInfoMap()
);
}
private Map extractTypeMap(List implicitParameterBindings) {
final HashMap map = new HashMap();
for ( ImplicitParameterBinding implicitParameter : implicitParameterBindings ) {
map.put( implicitParameter.getParameterName(), implicitParameter.getJavaType() );
}
return map;
}
};
}
protected void renderOrderByClause(RenderingContext renderingContext, StringBuilder jpaqlBuffer) {
if ( getOrderList().isEmpty() ) {
return;
}
renderingContext.getClauseStack().push( Clause.ORDER );
try {
jpaqlBuffer.append( " order by " );
String sep = "";
for ( Order orderSpec : getOrderList() ) {
jpaqlBuffer.append( sep )
.append( ( (Renderable) orderSpec.getExpression() ).render( renderingContext ) )
.append( orderSpec.isAscending() ? " asc" : " desc" );
sep = ", ";
}
}
finally {
renderingContext.getClauseStack().pop();
}
}
}