org.hibernate.query.criteria.internal.path.AbstractFromImpl 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.path;
import java.io.Serializable;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.persistence.criteria.CollectionJoin;
import javax.persistence.criteria.Fetch;
import javax.persistence.criteria.From;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.ListJoin;
import javax.persistence.criteria.MapJoin;
import javax.persistence.criteria.SetJoin;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.CollectionAttribute;
import javax.persistence.metamodel.ListAttribute;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.MapAttribute;
import javax.persistence.metamodel.PluralAttribute;
import javax.persistence.metamodel.SetAttribute;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.Type;
import org.hibernate.query.criteria.internal.BasicPathUsageException;
import org.hibernate.query.criteria.internal.CollectionJoinImplementor;
import org.hibernate.query.criteria.internal.CriteriaBuilderImpl;
import org.hibernate.query.criteria.internal.CriteriaSubqueryImpl;
import org.hibernate.query.criteria.internal.FromImplementor;
import org.hibernate.query.criteria.internal.JoinImplementor;
import org.hibernate.query.criteria.internal.ListJoinImplementor;
import org.hibernate.query.criteria.internal.MapJoinImplementor;
import org.hibernate.query.criteria.internal.PathSource;
import org.hibernate.query.criteria.internal.SetJoinImplementor;
import org.hibernate.query.criteria.internal.compile.RenderingContext;
/**
* Convenience base class for various {@link javax.persistence.criteria.From} implementations.
*
* @author Steve Ebersole
*/
public abstract class AbstractFromImpl
extends AbstractPathImpl
implements From, FromImplementor, Serializable {
public static final JoinType DEFAULT_JOIN_TYPE = JoinType.INNER;
private Set> joins;
private Set> fetches;
public AbstractFromImpl(CriteriaBuilderImpl criteriaBuilder, Class javaType) {
this( criteriaBuilder, javaType, null );
}
public AbstractFromImpl(CriteriaBuilderImpl criteriaBuilder, Class javaType, PathSource pathSource) {
super( criteriaBuilder, javaType, pathSource );
}
@Override
@SuppressWarnings({"unchecked"})
public PathSource getPathSource() {
return super.getPathSource();
}
@Override
public String getPathIdentifier() {
return getAlias();
}
@Override
protected boolean canBeDereferenced() {
return true;
}
@Override
public void prepareAlias(RenderingContext renderingContext) {
if ( getAlias() == null ) {
if ( isCorrelated() ) {
setAlias( getCorrelationParent().getAlias() );
}
else {
setAlias( renderingContext.generateAlias() );
}
}
}
@Override
public String render(RenderingContext renderingContext) {
prepareAlias( renderingContext );
return getAlias();
}
@Override
public Attribute getAttribute() {
return null;
}
public From getParent() {
return null;
}
@Override
@SuppressWarnings({"unchecked"})
protected Attribute locateAttributeInternal(String name) {
return (Attribute) locateManagedType().getAttribute( name );
}
@SuppressWarnings({"unchecked"})
protected ManagedType locateManagedType() {
// by default, this should be the model
return (ManagedType) getModel();
}
// CORRELATION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// IMPL NOTE : another means from handling correlations is to create a series of
// specialized From implementations that represent the correlation roots. While
// that may be cleaner code-wise, it is certainly means creating a lot of "extra"
// classes since we'd need one for each Subquery#correlate method
private FromImplementor correlationParent;
private JoinScope joinScope = new BasicJoinScope();
/**
* Helper contract used to define who/what keeps track of joins and fetches made from this FROM.
*/
public static interface JoinScope extends Serializable {
public void addJoin(Join join);
public void addFetch(Fetch fetch);
}
protected class BasicJoinScope implements JoinScope {
@Override
public void addJoin(Join join) {
if ( joins == null ) {
joins = new LinkedHashSet>();
}
joins.add( join );
}
@Override
public void addFetch(Fetch fetch) {
if ( fetches == null ) {
fetches = new LinkedHashSet>();
}
fetches.add( fetch );
}
}
protected class CorrelationJoinScope implements JoinScope {
@Override
public void addJoin(Join join) {
if ( joins == null ) {
joins = new LinkedHashSet>();
}
joins.add( join );
}
@Override
public void addFetch(Fetch fetch) {
throw new UnsupportedOperationException( "Cannot define fetch from a subquery correlation" );
}
}
@Override
public boolean isCorrelated() {
return correlationParent != null;
}
@Override
public FromImplementor getCorrelationParent() {
if ( correlationParent == null ) {
throw new IllegalStateException(
String.format(
"Criteria query From node [%s] is not part of a subquery correlation",
getPathIdentifier()
)
);
}
return correlationParent;
}
@Override
@SuppressWarnings({"unchecked"})
public FromImplementor correlateTo(CriteriaSubqueryImpl subquery) {
final FromImplementor correlationDelegate = createCorrelationDelegate();
correlationDelegate.prepareCorrelationDelegate( this );
return correlationDelegate;
}
protected abstract FromImplementor createCorrelationDelegate();
@Override
public void prepareCorrelationDelegate(FromImplementor parent) {
this.joinScope = new CorrelationJoinScope();
this.correlationParent = parent;
}
@Override
public String getAlias() {
return isCorrelated() ? getCorrelationParent().getAlias() : super.getAlias();
}
// JOINS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
protected abstract boolean canBeJoinSource();
protected RuntimeException illegalJoin() {
return new IllegalArgumentException(
"Collection of values [" + getPathIdentifier() + "] cannot be source of a join"
);
}
@Override
@SuppressWarnings({"unchecked"})
public Set> getJoins() {
return joins == null
? Collections.EMPTY_SET
: joins;
}
@Override
public Join join(SingularAttribute singularAttribute) {
return join( singularAttribute, DEFAULT_JOIN_TYPE );
}
@Override
public Join join(SingularAttribute attribute, JoinType jt) {
if ( !canBeJoinSource() ) {
throw illegalJoin();
}
Join join = constructJoin( attribute, jt );
joinScope.addJoin( join );
return join;
}
private JoinImplementor constructJoin(SingularAttribute attribute, JoinType jt) {
if ( Type.PersistenceType.BASIC.equals( attribute.getType().getPersistenceType() ) ) {
throw new BasicPathUsageException( "Cannot join to attribute of basic type", attribute );
}
// TODO : runtime check that the attribute in fact belongs to this From's model/bindable
if ( jt.equals( JoinType.RIGHT ) ) {
throw new UnsupportedOperationException( "RIGHT JOIN not supported" );
}
final Class attributeType = attribute.getBindableJavaType();
return new SingularAttributeJoin(
criteriaBuilder(),
attributeType,
this,
attribute,
jt
);
}
@Override
public CollectionJoin join(CollectionAttribute collection) {
return join( collection, DEFAULT_JOIN_TYPE );
}
@Override
public CollectionJoin join(CollectionAttribute collection, JoinType jt) {
if ( !canBeJoinSource() ) {
throw illegalJoin();
}
final CollectionJoin join = constructJoin( collection, jt );
joinScope.addJoin( join );
return join;
}
private CollectionJoinImplementor constructJoin(
CollectionAttribute collection,
JoinType jt) {
if ( jt.equals( JoinType.RIGHT ) ) {
throw new UnsupportedOperationException( "RIGHT JOIN not supported" );
}
// TODO : runtime check that the attribute in fact belongs to this From's model/bindable
final Class attributeType = collection.getBindableJavaType();
return new CollectionAttributeJoin(
criteriaBuilder(),
attributeType,
this,
collection,
jt
);
}
@Override
public SetJoin join(SetAttribute set) {
return join( set, DEFAULT_JOIN_TYPE );
}
@Override
public SetJoin join(SetAttribute set, JoinType jt) {
if ( !canBeJoinSource() ) {
throw illegalJoin();
}
final SetJoin join = constructJoin( set, jt );
joinScope.addJoin( join );
return join;
}
private SetJoinImplementor constructJoin(SetAttribute set, JoinType jt) {
if ( jt.equals( JoinType.RIGHT ) ) {
throw new UnsupportedOperationException( "RIGHT JOIN not supported" );
}
// TODO : runtime check that the attribute in fact belongs to this From's model/bindable
final Class attributeType = set.getBindableJavaType();
return new SetAttributeJoin( criteriaBuilder(), attributeType, this, set, jt );
}
@Override
public ListJoin join(ListAttribute list) {
return join( list, DEFAULT_JOIN_TYPE );
}
@Override
public ListJoin join(ListAttribute list, JoinType jt) {
if ( !canBeJoinSource() ) {
throw illegalJoin();
}
final ListJoin join = constructJoin( list, jt );
joinScope.addJoin( join );
return join;
}
private ListJoinImplementor constructJoin(ListAttribute list, JoinType jt) {
if ( jt.equals( JoinType.RIGHT ) ) {
throw new UnsupportedOperationException( "RIGHT JOIN not supported" );
}
// TODO : runtime check that the attribute in fact belongs to this From's model/bindable
final Class attributeType = list.getBindableJavaType();
return new ListAttributeJoin( criteriaBuilder(), attributeType, this, list, jt );
}
@Override
public MapJoin join(MapAttribute map) {
return join( map, DEFAULT_JOIN_TYPE );
}
@Override
public MapJoin join(MapAttribute map, JoinType jt) {
if ( !canBeJoinSource() ) {
throw illegalJoin();
}
final MapJoin join = constructJoin( map, jt );
joinScope.addJoin( join );
return join;
}
private MapJoinImplementor constructJoin(MapAttribute map, JoinType jt) {
if ( jt.equals( JoinType.RIGHT ) ) {
throw new UnsupportedOperationException( "RIGHT JOIN not supported" );
}
// TODO : runtime check that the attribute in fact belongs to this From's model/bindable
final Class attributeType = map.getBindableJavaType();
return new MapAttributeJoin( criteriaBuilder(), attributeType, this, map, jt );
}
@Override
public Join join(String attributeName) {
return join( attributeName, DEFAULT_JOIN_TYPE );
}
@Override
@SuppressWarnings({"unchecked"})
public Join join(String attributeName, JoinType jt) {
if ( !canBeJoinSource() ) {
throw illegalJoin();
}
if ( jt.equals( JoinType.RIGHT ) ) {
throw new UnsupportedOperationException( "RIGHT JOIN not supported" );
}
final Attribute attribute = (Attribute) locateAttribute( attributeName );
if ( attribute.isCollection() ) {
final PluralAttribute pluralAttribute = (PluralAttribute) attribute;
if ( PluralAttribute.CollectionType.COLLECTION.equals( pluralAttribute.getCollectionType() ) ) {
return (Join) join( (CollectionAttribute) attribute, jt );
}
else if ( PluralAttribute.CollectionType.LIST.equals( pluralAttribute.getCollectionType() ) ) {
return (Join) join( (ListAttribute) attribute, jt );
}
else if ( PluralAttribute.CollectionType.SET.equals( pluralAttribute.getCollectionType() ) ) {
return (Join) join( (SetAttribute) attribute, jt );
}
else {
return (Join) join( (MapAttribute) attribute, jt );
}
}
else {
return (Join) join( (SingularAttribute) attribute, jt );
}
}
@Override
public CollectionJoin joinCollection(String attributeName) {
return joinCollection( attributeName, DEFAULT_JOIN_TYPE );
}
@Override
@SuppressWarnings({"unchecked"})
public CollectionJoin joinCollection(String attributeName, JoinType jt) {
final Attribute attribute = (Attribute) locateAttribute( attributeName );
if ( !attribute.isCollection() ) {
throw new IllegalArgumentException( "Requested attribute was not a collection" );
}
final PluralAttribute pluralAttribute = (PluralAttribute) attribute;
if ( !PluralAttribute.CollectionType.COLLECTION.equals( pluralAttribute.getCollectionType() ) ) {
throw new IllegalArgumentException( "Requested attribute was not a collection" );
}
return (CollectionJoin) join( (CollectionAttribute) attribute, jt );
}
@Override
public SetJoin joinSet(String attributeName) {
return joinSet( attributeName, DEFAULT_JOIN_TYPE );
}
@Override
@SuppressWarnings({"unchecked"})
public SetJoin joinSet(String attributeName, JoinType jt) {
final Attribute attribute = (Attribute) locateAttribute( attributeName );
if ( !attribute.isCollection() ) {
throw new IllegalArgumentException( "Requested attribute was not a set" );
}
final PluralAttribute pluralAttribute = (PluralAttribute) attribute;
if ( !PluralAttribute.CollectionType.SET.equals( pluralAttribute.getCollectionType() ) ) {
throw new IllegalArgumentException( "Requested attribute was not a set" );
}
return (SetJoin) join( (SetAttribute) attribute, jt );
}
@Override
public ListJoin joinList(String attributeName) {
return joinList( attributeName, DEFAULT_JOIN_TYPE );
}
@Override
@SuppressWarnings({"unchecked"})
public ListJoin joinList(String attributeName, JoinType jt) {
final Attribute attribute = (Attribute) locateAttribute( attributeName );
if ( !attribute.isCollection() ) {
throw new IllegalArgumentException( "Requested attribute was not a list" );
}
final PluralAttribute pluralAttribute = (PluralAttribute) attribute;
if ( !PluralAttribute.CollectionType.LIST.equals( pluralAttribute.getCollectionType() ) ) {
throw new IllegalArgumentException( "Requested attribute was not a list" );
}
return (ListJoin) join( (ListAttribute) attribute, jt );
}
@Override
public MapJoin joinMap(String attributeName) {
return joinMap( attributeName, DEFAULT_JOIN_TYPE );
}
@Override
@SuppressWarnings({"unchecked"})
public MapJoin joinMap(String attributeName, JoinType jt) {
final Attribute attribute = (Attribute) locateAttribute( attributeName );
if ( !attribute.isCollection() ) {
throw new IllegalArgumentException( "Requested attribute was not a map" );
}
final PluralAttribute pluralAttribute = (PluralAttribute) attribute;
if ( !PluralAttribute.CollectionType.MAP.equals( pluralAttribute.getCollectionType() ) ) {
throw new IllegalArgumentException( "Requested attribute was not a map" );
}
return (MapJoin) join( (MapAttribute) attribute, jt );
}
// FETCHES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
protected boolean canBeFetchSource() {
// the conditions should be the same...
return canBeJoinSource();
}
protected RuntimeException illegalFetch() {
return new IllegalArgumentException(
"Collection of values [" + getPathIdentifier() + "] cannot be source of a fetch"
);
}
@Override
@SuppressWarnings({"unchecked"})
public Set> getFetches() {
return fetches == null
? Collections.EMPTY_SET
: fetches;
}
@Override
public Fetch fetch(SingularAttribute singularAttribute) {
return fetch( singularAttribute, DEFAULT_JOIN_TYPE );
}
@Override
public Fetch fetch(SingularAttribute attribute, JoinType jt) {
if ( !canBeFetchSource() ) {
throw illegalFetch();
}
Fetch fetch = constructJoin( attribute, jt );
joinScope.addFetch( fetch );
return fetch;
}
@Override
public Fetch fetch(PluralAttribute pluralAttribute) {
return fetch( pluralAttribute, DEFAULT_JOIN_TYPE );
}
@Override
public Fetch fetch(PluralAttribute pluralAttribute, JoinType jt) {
if ( !canBeFetchSource() ) {
throw illegalFetch();
}
final Fetch fetch;
// TODO : combine Fetch and Join hierarchies (JoinImplementor extends Join,Fetch???)
if ( PluralAttribute.CollectionType.COLLECTION.equals( pluralAttribute.getCollectionType() ) ) {
fetch = constructJoin( (CollectionAttribute) pluralAttribute, jt );
}
else if ( PluralAttribute.CollectionType.LIST.equals( pluralAttribute.getCollectionType() ) ) {
fetch = constructJoin( (ListAttribute) pluralAttribute, jt );
}
else if ( PluralAttribute.CollectionType.SET.equals( pluralAttribute.getCollectionType() ) ) {
fetch = constructJoin( (SetAttribute) pluralAttribute, jt );
}
else {
fetch = constructJoin( (MapAttribute) pluralAttribute, jt );
}
joinScope.addFetch( fetch );
return fetch;
}
@Override
public Fetch fetch(String attributeName) {
return fetch( attributeName, DEFAULT_JOIN_TYPE );
}
@Override
@SuppressWarnings({"unchecked"})
public Fetch fetch(String attributeName, JoinType jt) {
if ( !canBeFetchSource() ) {
throw illegalFetch();
}
Attribute attribute = (Attribute) locateAttribute( attributeName );
if ( attribute.isCollection() ) {
return (Fetch) fetch( (PluralAttribute) attribute, jt );
}
else {
return (Fetch) fetch( (SingularAttribute) attribute, jt );
}
}
}