HHH-7387 - Integrate Draft 6 of the JPA 2.1 spec : TREAT

This commit is contained in:
Steve Ebersole 2012-06-22 11:04:13 -05:00
parent 8b87ae8830
commit 2adab60d15
22 changed files with 571 additions and 101 deletions

View File

@ -71,7 +71,7 @@ libraries = [
javassist: 'org.javassist:javassist:3.15.0-GA',
// javax
jpa: 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Draft-6',
jpa: 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Draft-6b',
jta: 'org.jboss.spec.javax.transaction:jboss-transaction-api_1.1_spec:1.0.0.Final',
validation: 'javax.validation:validation-api:1.0.0.GA',
jacc: 'org.jboss.spec.javax.security.jacc:jboss-jacc-api_1.4_spec:1.0.0.Final',

View File

@ -191,9 +191,25 @@ tokens
return x;
}
public void weakKeywords() throws TokenStreamException { }
public void weakKeywords() throws TokenStreamException {
}
public void processMemberOf(Token n,AST p,ASTPair currentAST) { }
public void processMemberOf(Token n,AST p,ASTPair currentAST) {
}
protected boolean validateSoftKeyword(String text) throws TokenStreamException {
return validateLookAheadText(1, text);
}
protected boolean validateLookAheadText(int lookAheadPosition, String text) throws TokenStreamException {
String text2Validate = retrieveLookAheadText( lookAheadPosition );
return text2Validate == null ? false : text2Validate.equalsIgnoreCase( text );
}
protected String retrieveLookAheadText(int lookAheadPosition) throws TokenStreamException {
Token token = LT(lookAheadPosition);
return token == null ? null : token.getText();
}
protected String unquote(String text) {
return text.substring( 1, text.length() - 1 );
@ -334,9 +350,25 @@ fromClause
fromJoin
: ( ( ( LEFT | RIGHT ) (OUTER)? ) | FULL | INNER )? JOIN^ (FETCH)?
path (asAlias)? (propertyFetch)? (withClause)?
joinPath (asAlias)? (propertyFetch)? (withClause)?
;
joinPath
: { validateSoftKeyword("treat") && LA(2) == OPEN }? castedJoinPath
| path
;
/**
* Represents the JPA 2.1 TREAT construct when applied to a join. Hibernate already handles subclass
* property references implicitly, so we simply "eat" all tokens of the TREAT construct and just return the
* join path itself.
*
* Uses a validating semantic predicate to make sure the text of the matched first IDENT is the TREAT keyword
*/
castedJoinPath
: i:IDENT! OPEN! p:path AS! path! CLOSE! {i.getText().equals("treat") }?
;
withClause
: WITH^ logicalExpression
// JPA 2.1 support for an ON clause that isn't really an ON clause...
@ -607,7 +639,7 @@ quantifiedExpression
// * method call ( '.' ident '(' exprList ') )
// * function : differentiated from method call via explicit keyword
atom
: { LT(1).getText().equalsIgnoreCase("function") && LA(2) == OPEN && LA(3) == QUOTED_STRING }? jpaFunctionSyntax
: { validateSoftKeyword("function") && LA(2) == OPEN && LA(3) == QUOTED_STRING }? jpaFunctionSyntax
| primaryExpression
(
DOT^ identifier
@ -659,7 +691,7 @@ vectorExpr
// NOTE: handleDotIdent() is called immediately after the first IDENT is recognized because
// the method looks a head to find keywords after DOT and turns them into identifiers.
identPrimary
: i:identifier { handleDotIdent(); }
: i:identPrimaryBase { handleDotIdent(); }
( options { greedy=true; } : DOT^ ( identifier | ELEMENTS | o:OBJECT { #o.setType(IDENT); } ) )*
( options { greedy=true; } :
( op:OPEN^ { #op.setType(METHOD_CALL);} e:exprList CLOSE! ) {
@ -679,6 +711,15 @@ identPrimary
| aggregate
;
identPrimaryBase
: { validateSoftKeyword("treat") && LA(2) == OPEN }? castedIdentPrimaryBase
| i:identifier
;
castedIdentPrimaryBase
: i:IDENT! OPEN! p:path AS! path! CLOSE! { i.getText().equals("treat") }?
;
aggregate
: ( SUM^ | AVG^ | MAX^ | MIN^ ) OPEN! additiveExpression CLOSE! { #aggregate.setType(AGGREGATE); }
// Special case for count - It's 'parameters' can be keywords.

View File

@ -903,6 +903,18 @@ public class ASTParserLoadingTest extends BaseCoreFunctionalTestCase {
new SyntaxChecker( "from DomesticAnimal da join da.owner as o where o.nickName = 'Gavin'" ).checkAll();
new SyntaxChecker( "select da.father from DomesticAnimal da join da.owner as o where o.nickName = 'Gavin'" ).checkAll();
new SyntaxChecker( "select da.father from DomesticAnimal da where da.owner.nickName = 'Gavin'" ).checkAll();
}
/**
* {@link #testSubclassOrSuperclassPropertyReferenceInJoinedSubclass} tests the implicit form of entity casting
* that Hibernate has always supported. THis method tests the explicit variety added by JPA 2.1 using the TREAT
* keyword.
*/
@Test
public void testExplicitEntityCasting() {
new SyntaxChecker( "from Zoo z join treat(z.mammals as Human) as m where m.name.first = 'John'" ).checkIterate();
new SyntaxChecker( "from Zoo z join z.mammals as m where treat(m as Human).name.first = 'John'" ).checkIterate();
}
@Test

View File

@ -41,4 +41,7 @@ public interface CollectionJoinImplementor<Z,X> extends JoinImplementor<Z,X>, Co
@Override
public CollectionJoinImplementor<Z, X> on(Predicate... restrictions);
@Override
public <T extends X> CollectionJoinImplementor<Z, T> treatAs(Class<T> treatAsType);
}

View File

@ -81,6 +81,7 @@ import org.hibernate.ejb.criteria.expression.function.SubstringFunction;
import org.hibernate.ejb.criteria.expression.function.TrimFunction;
import org.hibernate.ejb.criteria.expression.function.UpperFunction;
import org.hibernate.ejb.criteria.path.PluralAttributePath;
import org.hibernate.ejb.criteria.path.RootImpl;
import org.hibernate.ejb.criteria.predicate.BetweenPredicate;
import org.hibernate.ejb.criteria.predicate.BooleanAssertionPredicate;
import org.hibernate.ejb.criteria.predicate.BooleanExpressionPredicate;
@ -1108,38 +1109,45 @@ public class CriteriaBuilderImpl implements CriteriaBuilder, Serializable {
}
@Override
@SuppressWarnings("unchecked")
public <X, T, V extends T> Join<X, V> treat(Join<X, T> join, Class<V> type) {
throw new NotYetImplementedException();
return ( (JoinImplementor) join ).treatAs( type );
}
@Override
@SuppressWarnings("unchecked")
public <X, T, E extends T> CollectionJoin<X, E> treat(CollectionJoin<X, T> join, Class<E> type) {
throw new NotYetImplementedException();
return ( (CollectionJoinImplementor) join ).treatAs( type );
}
@Override
@SuppressWarnings("unchecked")
public <X, T, E extends T> SetJoin<X, E> treat(SetJoin<X, T> join, Class<E> type) {
throw new NotYetImplementedException();
return ( (SetJoinImplementor) join ).treatAs( type );
}
@Override
@SuppressWarnings("unchecked")
public <X, T, E extends T> ListJoin<X, E> treat(ListJoin<X, T> join, Class<E> type) {
throw new NotYetImplementedException();
return ( (ListJoinImplementor) join ).treatAs( type );
}
@Override
@SuppressWarnings("unchecked")
public <X, K, T, V extends T> MapJoin<X, K, V> treat(MapJoin<X, K, T> join, Class<V> type) {
throw new NotYetImplementedException();
return ( (MapJoinImplementor) join ).treatAs( type );
}
@Override
public <X, T extends X> Path<X> treat(Path<T> path, Class<X> type) {
throw new NotYetImplementedException();
@SuppressWarnings("unchecked")
public <X, T extends X> Path<T> treat(Path<X> path, Class<T> type) {
return ( (PathImplementor) path ).treatAs( type );
}
@Override
public <X, T extends X> Root<X> treat(Root<T> root, Class<X> type) {
throw new NotYetImplementedException();
@SuppressWarnings("unchecked")
public <X, T extends X> Root<T> treat(Root<X> root, Class<T> type) {
return ( (RootImpl) root ).treatAs( type );
}

View File

@ -52,4 +52,7 @@ public interface JoinImplementor<Z,X> extends Join<Z,X>, Fetch<Z,X>, FromImpleme
*/
@Override
public JoinImplementor<Z, X> on(Predicate... restrictions);
@Override
public <T extends X> JoinImplementor<Z, T> treatAs(Class<T> treatAsType);
}

View File

@ -41,4 +41,7 @@ public interface ListJoinImplementor<Z,X> extends JoinImplementor<Z,X>, ListJoin
@Override
public ListJoinImplementor<Z, X> on(Predicate... restrictions);
@Override
public <T extends X> ListJoinImplementor<Z, T> treatAs(Class<T> treatAsType);
}

View File

@ -41,4 +41,7 @@ public interface MapJoinImplementor<Z,K,V> extends JoinImplementor<Z,V>, MapJoin
@Override
public MapJoinImplementor<Z, K, V> on(Predicate... restrictions);
@Override
public <T extends V> MapJoinImplementor<Z, K, T> treatAs(Class<T> treatAsType);
}

View File

@ -22,6 +22,7 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.ejb.criteria;
import javax.persistence.criteria.Path;
import javax.persistence.metamodel.Attribute;
@ -37,4 +38,14 @@ public interface PathImplementor<X> extends ExpressionImplementor<X>, Path<X>, P
* @return The metamodel attribute.
*/
public Attribute<?, ?> getAttribute();
/**
* Defines handling for the JPA 2.1 TREAT down-casting feature.
*
* @param treatAsType The type to treat the path as.
* @param <T> The parameterized type representation of treatAsType.
*
* @return The properly typed view of this path.
*/
public <T extends X> PathImplementor<T> treatAs(Class<T> treatAsType);
}

View File

@ -41,4 +41,7 @@ public interface SetJoinImplementor<Z,X> extends JoinImplementor<Z,X>, SetJoin<Z
@Override
public SetJoinImplementor<Z, X> on(Predicate... restrictions);
@Override
public <T extends X> SetJoinImplementor<Z, T> treatAs(Class<T> treatAsType);
}

View File

@ -49,9 +49,7 @@ public abstract class AbstractTupleElement<X>
this.javaType = javaType;
}
/**
* {@inheritDoc}
*/
@Override
public Class<X> getJavaType() {
return javaType;
}
@ -69,16 +67,12 @@ public abstract class AbstractTupleElement<X>
this.valueHandler = valueHandler;
}
/**
* {@inheritDoc}
*/
@Override
public ValueHandlerFactory.ValueHandler<X> getValueHandler() {
return valueHandler;
}
/**
* {@inheritDoc}
*/
@Override
public String getAlias() {
return alias;
}

View File

@ -43,7 +43,7 @@ import org.hibernate.ejb.criteria.expression.ExpressionImpl;
import org.hibernate.ejb.criteria.expression.PathTypeExpression;
/**
* Convenience base class for various {@link Path} implementors.
* Convenience base class for various {@link Path} implementations.
*
* @author Steve Ebersole
*/
@ -76,24 +76,18 @@ public abstract class AbstractPathImpl<X>
return pathSource;
}
/**
* {@inheritDoc}
*/
@Override
public PathSource<?> getParentPath() {
return getPathSource();
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings({ "unchecked" })
public Expression<Class<? extends X>> type() {
return typeExpression;
}
/**
* {@inheritDoc}
*/
@Override
public String getPathIdentifier() {
return getPathSource().getPathIdentifier() + "." + getAttribute().getName();
}
@ -131,9 +125,7 @@ public abstract class AbstractPathImpl<X>
attributePathRegistry.put( attributeName, path );
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings({ "unchecked" })
public <Y> Path<Y> get(SingularAttribute<? super X, Y> attribute) {
if ( ! canBeDereferenced() ) {
@ -148,9 +140,7 @@ public abstract class AbstractPathImpl<X>
return path;
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings({ "unchecked" })
public <E, C extends Collection<E>> Expression<C> get(PluralAttribute<X, C, E> attribute) {
if ( ! canBeDereferenced() ) {
@ -165,9 +155,7 @@ public abstract class AbstractPathImpl<X>
return path;
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings({ "unchecked" })
public <K, V, M extends Map<K, V>> Expression<M> get(MapAttribute<X, K, V> attribute) {
if ( ! canBeDereferenced() ) {
@ -182,9 +170,7 @@ public abstract class AbstractPathImpl<X>
return path;
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings({ "unchecked" })
public <Y> Path<Y> get(String attributeName) {
if ( ! canBeDereferenced() ) {
@ -238,13 +224,12 @@ public abstract class AbstractPathImpl<X>
*/
protected abstract Attribute locateAttributeInternal(String attributeName);
/**
* {@inheritDoc}
*/
@Override
public void registerParameters(ParameterRegistry registry) {
// none to register
}
@Override
public void prepareAlias(CriteriaQueryCompiler.RenderingContext renderingContext) {
// Make sure we delegate up to our source (eventually up to the path root) to
// prepare the path properly.
@ -254,22 +239,19 @@ public abstract class AbstractPathImpl<X>
}
}
/**
* {@inheritDoc}
*/
@Override
public String render(CriteriaQueryCompiler.RenderingContext renderingContext) {
PathSource<?> source = getPathSource();
if ( source != null ) {
source.prepareAlias( renderingContext );
return source.getPathIdentifier() + "." + getAttribute().getName();
} else {
}
else {
return getAttribute().getName();
}
}
/**
* {@inheritDoc}
*/
@Override
public String renderProjection(CriteriaQueryCompiler.RenderingContext renderingContext) {
return render( renderingContext );
}

View File

@ -32,6 +32,7 @@ import javax.persistence.metamodel.CollectionAttribute;
import org.hibernate.ejb.criteria.CollectionJoinImplementor;
import org.hibernate.ejb.criteria.CriteriaBuilderImpl;
import org.hibernate.ejb.criteria.CriteriaQueryCompiler;
import org.hibernate.ejb.criteria.CriteriaSubqueryImpl;
import org.hibernate.ejb.criteria.FromImplementor;
import org.hibernate.ejb.criteria.PathImplementor;
@ -88,4 +89,42 @@ public class CollectionAttributeJoin<O,E>
public CollectionAttributeJoin<O, E> on(Expression<Boolean> restriction) {
return (CollectionAttributeJoin<O,E>) super.on( restriction );
}
@Override
public <T extends E> CollectionAttributeJoin<O,T> treatAs(Class<T> treatAsType) {
return new TreatedCollectionAttributeJoin<O,T>( this, treatAsType );
}
public static class TreatedCollectionAttributeJoin<O,T> extends CollectionAttributeJoin<O, T> {
private final CollectionAttributeJoin<O, ? super T> original;
private final Class<T> treatAsType;
@SuppressWarnings("unchecked")
public TreatedCollectionAttributeJoin(CollectionAttributeJoin<O, ? super T> original, Class<T> treatAsType) {
super(
original.criteriaBuilder(),
treatAsType,
original.getPathSource(),
(CollectionAttribute<? super O,T>) original.getAttribute(),
original.getJoinType()
);
this.original = original;
this.treatAsType = treatAsType;
}
@Override
public String getAlias() {
return original.getAlias();
}
@Override
public void prepareAlias(CriteriaQueryCompiler.RenderingContext renderingContext) {
// do nothing...
}
@Override
public String render(CriteriaQueryCompiler.RenderingContext renderingContext) {
return "treat(" + original.render( renderingContext ) + " as " + treatAsType.getName() + ")";
}
}
}

View File

@ -31,6 +31,7 @@ import javax.persistence.criteria.Predicate;
import javax.persistence.metamodel.ListAttribute;
import org.hibernate.ejb.criteria.CriteriaBuilderImpl;
import org.hibernate.ejb.criteria.CriteriaQueryCompiler;
import org.hibernate.ejb.criteria.CriteriaSubqueryImpl;
import org.hibernate.ejb.criteria.FromImplementor;
import org.hibernate.ejb.criteria.ListJoinImplementor;
@ -96,4 +97,42 @@ public class ListAttributeJoin<O,E>
public ListAttributeJoin<O, E> on(Expression<Boolean> restriction) {
return (ListAttributeJoin<O, E>) super.on( restriction );
}
@Override
public <T extends E> ListAttributeJoin<O,T> treatAs(Class<T> treatAsType) {
return new TreatedListAttributeJoin<O,T>( this, treatAsType );
}
public static class TreatedListAttributeJoin<O,T> extends ListAttributeJoin<O, T> {
private final ListAttributeJoin<O, ? super T> original;
private final Class<T> treatAsType;
@SuppressWarnings("unchecked")
public TreatedListAttributeJoin(ListAttributeJoin<O, ? super T> original, Class<T> treatAsType) {
super(
original.criteriaBuilder(),
treatAsType,
original.getPathSource(),
(ListAttribute<? super O,T>) original.getAttribute(),
original.getJoinType()
);
this.original = original;
this.treatAsType = treatAsType;
}
@Override
public String getAlias() {
return original.getAlias();
}
@Override
public void prepareAlias(CriteriaQueryCompiler.RenderingContext renderingContext) {
// do nothing...
}
@Override
public String render(CriteriaQueryCompiler.RenderingContext renderingContext) {
return "treat(" + original.render( renderingContext ) + " as " + treatAsType.getName() + ")";
}
}
}

View File

@ -32,6 +32,7 @@ import javax.persistence.criteria.Predicate;
import javax.persistence.metamodel.MapAttribute;
import org.hibernate.ejb.criteria.CriteriaBuilderImpl;
import org.hibernate.ejb.criteria.CriteriaQueryCompiler;
import org.hibernate.ejb.criteria.CriteriaSubqueryImpl;
import org.hibernate.ejb.criteria.FromImplementor;
import org.hibernate.ejb.criteria.MapJoinImplementor;
@ -117,4 +118,42 @@ public class MapAttributeJoin<O,K,V>
public MapJoinImplementor<O, K, V> on(Expression<Boolean> restriction) {
return (MapJoinImplementor<O, K, V>) super.on( restriction );
}
@Override
public <T extends V> MapAttributeJoin<O, K, T> treatAs(Class<T> treatAsType) {
return new TreatedMapAttributeJoin<O,K,T>( this, treatAsType );
}
public static class TreatedMapAttributeJoin<O, K, T> extends MapAttributeJoin<O, K, T> {
private final MapAttributeJoin<O, K, ? super T> original;
protected final Class<T> treatAsType;
@SuppressWarnings("unchecked")
public TreatedMapAttributeJoin(MapAttributeJoin<O, K, ? super T> original, Class<T> treatAsType) {
super(
original.criteriaBuilder(),
treatAsType,
original.getPathSource(),
(MapAttribute<? super O,K,T>) original.getAttribute(),
original.getJoinType()
);
this.original = original;
this.treatAsType = treatAsType;
}
@Override
public String getAlias() {
return original.getAlias();
}
@Override
public void prepareAlias(CriteriaQueryCompiler.RenderingContext renderingContext) {
// do nothing...
}
@Override
public String render(CriteriaQueryCompiler.RenderingContext renderingContext) {
return "treat(" + original.render( renderingContext ) + " as " + treatAsType.getName() + ")";
}
}
}

View File

@ -102,6 +102,13 @@ public class MapKeyHelpers {
public Bindable<K> getModel() {
return mapKeyAttribute;
}
@Override
@SuppressWarnings("unchecked")
public <T extends K> MapKeyPath<T> treatAs(Class<T> treatAsType) {
// todo : if key is an entity, this is probably not enough
return (MapKeyPath<T>) this;
}
}
/**
@ -153,6 +160,10 @@ public class MapKeyHelpers {
throw new IllegalArgumentException( "Map [" + mapJoin.getPathIdentifier() + "] cannot be dereferenced" );
}
@Override
public <T extends Map<K, V>> PathImplementor<T> treatAs(Class<T> treatAsType) {
throw new UnsupportedOperationException();
}
}
/**
@ -186,8 +197,7 @@ public class MapKeyHelpers {
: BindableType.SINGULAR_ATTRIBUTE;
String guessedRoleName = determineRole( attribute );
SessionFactoryImplementor sfi = (SessionFactoryImplementor)
criteriaBuilder.getEntityManagerFactory().getSessionFactory();
SessionFactoryImplementor sfi = criteriaBuilder.getEntityManagerFactory().getSessionFactory();
mapPersister = sfi.getCollectionPersister( guessedRoleName );
if ( mapPersister == null ) {
throw new IllegalStateException( "Could not locate collection persister [" + guessedRoleName + "]" );
@ -209,78 +219,70 @@ public class MapKeyHelpers {
'.' + attribute.getName();
}
/**
* {@inheritDoc}
*/
@Override
public String getName() {
// TODO : ???
return "map-key";
}
/**
* {@inheritDoc}
*/
@Override
public PersistentAttributeType getPersistentAttributeType() {
return persistentAttributeType;
}
/**
* {@inheritDoc}
*/
@Override
public ManagedType<Map<K, ?>> getDeclaringType() {
// TODO : ???
return null;
}
/**
* {@inheritDoc}
*/
@Override
public Class<K> getJavaType() {
return attribute.getKeyJavaType();
}
/**
* {@inheritDoc}
*/
@Override
public Member getJavaMember() {
// TODO : ???
return null;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isAssociation() {
return mapKeyType.isEntityType();
}
/**
* {@inheritDoc}
*/
@Override
public boolean isCollection() {
return false;
}
@Override
public boolean isId() {
return false;
}
@Override
public boolean isVersion() {
return false;
}
@Override
public boolean isOptional() {
return false;
}
@Override
public Type<K> getType() {
return jpaType;
}
@Override
public BindableType getBindableType() {
return jpaBindableType;
}
@Override
public Class<K> getBindableJavaType() {
return jpaBinableJavaType;
}

View File

@ -91,4 +91,12 @@ public class PluralAttributePath<X> extends AbstractPathImpl<X> implements Seria
// TODO : throw exception instead?
return null;
}
@Override
public <T extends X> PluralAttributePath<T> treatAs(Class<T> treatAsType) {
throw new UnsupportedOperationException(
"Plural attribute path [" + getPathSource().getPathIdentifier() + '.'
+ attribute.getName() + "] cannot be dereferenced"
);
}
}

View File

@ -33,7 +33,7 @@ import org.hibernate.ejb.criteria.CriteriaSubqueryImpl;
import org.hibernate.ejb.criteria.FromImplementor;
/**
* TODO : javadoc
* Hibernate implementation of the JPA {@link Root} contract
*
* @author Steve Ebersole
*/
@ -90,4 +90,39 @@ public class RootImpl<X> extends AbstractFromImpl<X,X> implements Root<X>, Seria
public String renderProjection(CriteriaQueryCompiler.RenderingContext renderingContext) {
return render( renderingContext );
}
@Override
public <T extends X> RootImpl<T> treatAs(Class<T> treatAsType) {
return new TreatedRoot<T>( this, treatAsType );
}
public static class TreatedRoot<T> extends RootImpl<T> {
private final RootImpl<? super T> original;
private final Class<T> treatAsType;
public TreatedRoot(RootImpl<? super T> original, Class<T> treatAsType) {
super(
original.criteriaBuilder(),
original.criteriaBuilder().getEntityManagerFactory().getMetamodel().entity( treatAsType )
);
this.original = original;
this.treatAsType = treatAsType;
}
@Override
public String getAlias() {
return original.getAlias();
}
@Override
public void prepareAlias(CriteriaQueryCompiler.RenderingContext renderingContext) {
// do nothing...
}
@Override
public String render(CriteriaQueryCompiler.RenderingContext renderingContext) {
return "treat(" + original.getAlias() + " as " + treatAsType.getName() + ")";
}
}
}

View File

@ -31,6 +31,7 @@ import javax.persistence.criteria.Predicate;
import javax.persistence.metamodel.SetAttribute;
import org.hibernate.ejb.criteria.CriteriaBuilderImpl;
import org.hibernate.ejb.criteria.CriteriaQueryCompiler;
import org.hibernate.ejb.criteria.CriteriaSubqueryImpl;
import org.hibernate.ejb.criteria.FromImplementor;
import org.hibernate.ejb.criteria.PathImplementor;
@ -40,6 +41,9 @@ import org.hibernate.ejb.criteria.SetJoinImplementor;
/**
* Models a join based on a set-style plural association attribute.
*
* @param <O> Represents the parameterized type of the set owner
* @param <E> Represents the parameterized type of the set elements
*
* @author Steve Ebersole
*/
public class SetAttributeJoin<O,E>
@ -90,4 +94,42 @@ public class SetAttributeJoin<O,E>
public SetJoinImplementor<O, E> on(Expression<Boolean> restriction) {
return (SetJoinImplementor<O, E>) super.on( restriction );
}
@Override
public <T extends E> SetAttributeJoin<O,T> treatAs(Class<T> treatAsType) {
return new TreatedSetAttributeJoin<O,T>( this, treatAsType );
}
public static class TreatedSetAttributeJoin<O,T> extends SetAttributeJoin<O, T> {
private final SetAttributeJoin<O, ? super T> original;
private final Class<T> treatAsType;
@SuppressWarnings("unchecked")
public TreatedSetAttributeJoin(SetAttributeJoin<O, ? super T> original, Class<T> treatAsType) {
super(
original.criteriaBuilder(),
treatAsType,
original.getPathSource(),
(SetAttribute<? super O,T>) original.getAttribute(),
original.getJoinType()
);
this.original = original;
this.treatAsType = treatAsType;
}
@Override
public String getAlias() {
return original.getAlias();
}
@Override
public void prepareAlias(CriteriaQueryCompiler.RenderingContext renderingContext) {
// do nothing...
}
@Override
public String render(CriteriaQueryCompiler.RenderingContext renderingContext) {
return "treat(" + original.render( renderingContext ) + " as " + treatAsType.getName() + ")";
}
}
}

View File

@ -29,6 +29,7 @@ import javax.persistence.metamodel.Bindable;
import javax.persistence.metamodel.SingularAttribute;
import org.hibernate.ejb.criteria.CriteriaBuilderImpl;
import org.hibernate.ejb.criteria.CriteriaQueryCompiler;
import org.hibernate.ejb.criteria.CriteriaSubqueryImpl;
import org.hibernate.ejb.criteria.FromImplementor;
import org.hibernate.ejb.criteria.PathSource;
@ -36,17 +37,20 @@ import org.hibernate.ejb.criteria.PathSource;
/**
* Models a join based on a singular attribute
*
* @param <O> Represents the parameterized type of the attribute owner
* @param <X> Represents the parameterized type of the attribute
*
* @author Steve Ebersole
*/
public class SingularAttributeJoin<Z,X> extends AbstractJoinImpl<Z,X> {
public class SingularAttributeJoin<O,X> extends AbstractJoinImpl<O,X> {
private final Bindable<X> model;
@SuppressWarnings({ "unchecked" })
public SingularAttributeJoin(
CriteriaBuilderImpl criteriaBuilder,
Class<X> javaType,
PathSource<Z> pathSource,
SingularAttribute<? super Z, ?> joinAttribute,
PathSource<O> pathSource,
SingularAttribute<? super O, ?> joinAttribute,
JoinType joinType) {
super( criteriaBuilder, javaType, pathSource, joinAttribute, joinType );
this.model = (Bindable<X>) (
@ -57,18 +61,18 @@ public class SingularAttributeJoin<Z,X> extends AbstractJoinImpl<Z,X> {
}
@Override
public SingularAttribute<? super Z, ?> getAttribute() {
return (SingularAttribute<? super Z, ?>) super.getAttribute();
public SingularAttribute<? super O, ?> getAttribute() {
return (SingularAttribute<? super O, ?>) super.getAttribute();
}
@Override
public SingularAttributeJoin<Z, X> correlateTo(CriteriaSubqueryImpl subquery) {
return (SingularAttributeJoin<Z, X>) super.correlateTo( subquery );
public SingularAttributeJoin<O, X> correlateTo(CriteriaSubqueryImpl subquery) {
return (SingularAttributeJoin<O, X>) super.correlateTo( subquery );
}
@Override
protected FromImplementor<Z, X> createCorrelationDelegate() {
return new SingularAttributeJoin<Z,X>(
protected FromImplementor<O, X> createCorrelationDelegate() {
return new SingularAttributeJoin<O,X>(
criteriaBuilder(),
getJavaType(),
getPathSource(),
@ -85,4 +89,41 @@ public class SingularAttributeJoin<Z,X> extends AbstractJoinImpl<Z,X> {
public Bindable<X> getModel() {
return model;
}
@Override
public <T extends X> SingularAttributeJoin<O,T> treatAs(Class<T> treatAsType) {
return new TreatedSingularAttributeJoin<O,T>( this, treatAsType );
}
public static class TreatedSingularAttributeJoin<O,T> extends SingularAttributeJoin<O, T> {
private final SingularAttributeJoin<O, ? super T> original;
private final Class<T> treatAsType;
public TreatedSingularAttributeJoin(SingularAttributeJoin<O, ? super T> original, Class<T> treatAsType) {
super(
original.criteriaBuilder(),
treatAsType,
original.getPathSource(),
original.getAttribute(),
original.getJoinType()
);
this.original = original;
this.treatAsType = treatAsType;
}
@Override
public String getAlias() {
return original.getAlias();
}
@Override
public void prepareAlias(CriteriaQueryCompiler.RenderingContext renderingContext) {
// do nothing...
}
@Override
public String render(CriteriaQueryCompiler.RenderingContext renderingContext) {
return "treat(" + original.render( renderingContext ) + " as " + treatAsType.getName() + ")";
}
}
}

View File

@ -32,6 +32,7 @@ import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.SingularAttribute;
import org.hibernate.ejb.criteria.CriteriaBuilderImpl;
import org.hibernate.ejb.criteria.CriteriaQueryCompiler;
import org.hibernate.ejb.criteria.PathSource;
/**
@ -64,29 +65,19 @@ public class SingularAttributePath<X> extends AbstractPathImpl<X> implements Ser
}
else {
return (IdentifiableType<X>) attribute.getType();
// return criteriaBuilder.getEntityManagerFactory()
// .getMetamodel()
// .managedType( javaType );
}
}
/**
* {@inheritDoc}
*/
@Override
public SingularAttribute<?, X> getAttribute() {
return attribute;
}
/**
* {@inheritDoc}
*/
@Override
public Bindable<X> getModel() {
return getAttribute();
}
/**
* {@inheritDoc}
*/
@Override
protected boolean canBeDereferenced() {
return managedType != null;
@ -102,4 +93,41 @@ public class SingularAttributePath<X> extends AbstractPathImpl<X> implements Ser
}
return attribute;
}
@Override
public <T extends X> SingularAttributePath<T> treatAs(Class<T> treatAsType) {
return new TreatedSingularAttributePath<T>( this, treatAsType );
}
public static class TreatedSingularAttributePath<T> extends SingularAttributePath<T> {
private final SingularAttributePath<? super T> original;
private final Class<T> treatAsType;
@SuppressWarnings("unchecked")
public TreatedSingularAttributePath(SingularAttributePath<? super T> original, Class<T> treatAsType) {
super(
original.criteriaBuilder(),
treatAsType,
original.getPathSource(),
(SingularAttribute<?,T>) original.getAttribute()
);
this.original = original;
this.treatAsType = treatAsType;
}
@Override
public String getAlias() {
return original.getAlias();
}
@Override
public void prepareAlias(CriteriaQueryCompiler.RenderingContext renderingContext) {
// do nothing...
}
@Override
public String render(CriteriaQueryCompiler.RenderingContext renderingContext) {
return "treat(" + original.render( renderingContext ) + " as " + treatAsType.getName() + ")";
}
}
}

View File

@ -0,0 +1,134 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.ejb.criteria;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.hibernate.ejb.metamodel.Thing;
import org.hibernate.ejb.metamodel.ThingWithQuantity;
import org.hibernate.ejb.metamodel.ThingWithQuantity_;
import org.hibernate.ejb.test.BaseEntityManagerFunctionalTestCase;
import org.junit.Test;
/**
* @author Steve Ebersole
*/
public class TreatKeywordTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Animal.class, Human.class, Thing.class, ThingWithQuantity.class };
}
@Test
public void basicTest() {
EntityManager em = getOrCreateEntityManager();
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Thing> criteria = builder.createQuery( Thing.class );
Root<Thing> root = criteria.from( Thing.class );
criteria.select( root );
criteria.where(
builder.equal(
builder.treat( root, ThingWithQuantity.class ).get( ThingWithQuantity_.quantity ),
2
)
);
em.createQuery( criteria ).getResultList();
em.close();
}
@Test
public void basicTest2() {
EntityManager em = getOrCreateEntityManager();
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Animal> criteria = builder.createQuery( Animal.class );
Root<Animal> root = criteria.from( Animal.class );
criteria.select( root );
criteria.where(
builder.equal(
builder.treat( root, Human.class ).get( "name" ),
2
)
);
em.createQuery( criteria ).getResultList();
em.close();
}
@Entity
@Table( name = "ANIMAL" )
public static class Animal {
private Long id;
private Animal mother;
private Animal father;
@Id
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@ManyToOne
public Animal getMother() {
return mother;
}
public void setMother(Animal mother) {
this.mother = mother;
}
@ManyToOne
public Animal getFather() {
return father;
}
public void setFather(Animal father) {
this.father = father;
}
}
@Entity
@Table( name = "HUMAN" )
public static class Human extends Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}