HHH-7387 - Integrate Draft 6 of the JPA 2.1 spec : TREAT
This commit is contained in:
parent
8b87ae8830
commit
2adab60d15
|
@ -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',
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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() + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() + ")";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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() + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue