introduce new syntax for aggregate functions applying to collections

max(element x.y), min(index x.y), sum(element x.y)

and rationalize the node types here
This commit is contained in:
Gavin King 2022-01-10 11:08:05 +01:00
parent 38fc97feb3
commit 4b5e6e1969
18 changed files with 205 additions and 283 deletions

View File

@ -1782,7 +1782,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
List<Phone> phones = entityManager.createQuery( List<Phone> phones = entityManager.createQuery(
"select p " + "select p " +
"from Phone p " + "from Phone p " +
"where maxelement(p.calls) = :call", "where max(element p.calls) = :call",
Phone.class) Phone.class)
.setParameter("call", call) .setParameter("call", call)
.getResultList(); .getResultList();
@ -1800,7 +1800,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
List<Phone> phones = entityManager.createQuery( List<Phone> phones = entityManager.createQuery(
"select p " + "select p " +
"from Phone p " + "from Phone p " +
"where minelement(p.calls) = :call", "where min(element p.calls) = :call",
Phone.class) Phone.class)
.setParameter("call", call) .setParameter("call", call)
.getResultList(); .getResultList();
@ -1817,7 +1817,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
List<Person> persons = entityManager.createQuery( List<Person> persons = entityManager.createQuery(
"select p " + "select p " +
"from Person p " + "from Person p " +
"where maxindex(p.phones) = 0", "where max(index p.phones) = 0",
Person.class) Person.class)
.getResultList(); .getResultList();
//end::hql-collection-expressions-example[] //end::hql-collection-expressions-example[]
@ -1991,7 +1991,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
List<Person> persons = entityManager.createQuery( List<Person> persons = entityManager.createQuery(
"select pr " + "select pr " +
"from Person pr " + "from Person pr " +
"where pr.phones[maxindex(pr.phones)].type = 'LAND_LINE'", "where pr.phones[max(index pr.phones)].type = 'LAND_LINE'",
Person.class) Person.class)
.getResultList(); .getResultList();
//end::hql-collection-index-operator-example[] //end::hql-collection-index-operator-example[]

View File

@ -145,6 +145,7 @@ AND : [aA] [nN] [dD];
ANY : [aA] [nN] [yY]; ANY : [aA] [nN] [yY];
AS : [aA] [sS]; AS : [aA] [sS];
ASC : [aA] [sS] [cC]; ASC : [aA] [sS] [cC];
AVG : [aA] [vV] [gG];
BY : [bB] [yY]; BY : [bB] [yY];
BETWEEN : [bB] [eE] [tT] [wW] [eE] [eE] [nN]; BETWEEN : [bB] [eE] [tT] [wW] [eE] [eE] [nN];
BOTH : [bB] [oO] [tT] [hH]; BOTH : [bB] [oO] [tT] [hH];
@ -164,6 +165,7 @@ DAY : [dD] [aA] [yY];
DELETE : [dD] [eE] [lL] [eE] [tT] [eE]; DELETE : [dD] [eE] [lL] [eE] [tT] [eE];
DESC : [dD] [eE] [sS] [cC]; DESC : [dD] [eE] [sS] [cC];
DISTINCT : [dD] [iI] [sS] [tT] [iI] [nN] [cC] [tT]; DISTINCT : [dD] [iI] [sS] [tT] [iI] [nN] [cC] [tT];
ELEMENT : [eE] [lL] [eE] [mM] [eE] [nN] [tT];
ELEMENTS : [eE] [lL] [eE] [mM] [eE] [nN] [tT] [sS]; ELEMENTS : [eE] [lL] [eE] [mM] [eE] [nN] [tT] [sS];
ELSE : [eE] [lL] [sS] [eE]; ELSE : [eE] [lL] [sS] [eE];
EMPTY : [eE] [mM] [pP] [tT] [yY]; EMPTY : [eE] [mM] [pP] [tT] [yY];
@ -208,7 +210,9 @@ LOCAL_DATE : [lL] [oO] [cC] [aA] [lL] '_' [dD] [aA] [tT] [eE];
LOCAL_DATETIME : [lL] [oO] [cC] [aA] [lL] '_' [dD] [aA] [tT] [eE] [tT] [iI] [mM] [eE]; LOCAL_DATETIME : [lL] [oO] [cC] [aA] [lL] '_' [dD] [aA] [tT] [eE] [tT] [iI] [mM] [eE];
LOCAL_TIME : [lL] [oO] [cC] [aA] [lL] '_' [tT] [iI] [mM] [eE]; LOCAL_TIME : [lL] [oO] [cC] [aA] [lL] '_' [tT] [iI] [mM] [eE];
MAP : [mM] [aA] [pP]; MAP : [mM] [aA] [pP];
MAX : [mM] [aA] [xX];
MAXELEMENT : [mM] [aA] [xX] [eE] [lL] [eE] [mM] [eE] [nN] [tT]; MAXELEMENT : [mM] [aA] [xX] [eE] [lL] [eE] [mM] [eE] [nN] [tT];
MIN : [mM] [iI] [nN];
MAXINDEX : [mM] [aA] [xX] [iI] [nN] [dD] [eE] [xX]; MAXINDEX : [mM] [aA] [xX] [iI] [nN] [dD] [eE] [xX];
MEMBER : [mM] [eE] [mM] [bB] [eE] [rR]; MEMBER : [mM] [eE] [mM] [bB] [eE] [rR];
MICROSECOND : [mM] [iI] [cC] [rR] [oO] [sS] [eE] [cC] [oO] [nN] [dD]; MICROSECOND : [mM] [iI] [cC] [rR] [oO] [sS] [eE] [cC] [oO] [nN] [dD];
@ -247,6 +251,7 @@ SET : [sS] [eE] [tT];
SIZE : [sS] [iI] [zZ] [eE]; SIZE : [sS] [iI] [zZ] [eE];
SOME : [sS] [oO] [mM] [eE]; SOME : [sS] [oO] [mM] [eE];
SUBSTRING : [sS] [uU] [bB] [sS] [tT] [rR] [iI] [nN] [gG]; SUBSTRING : [sS] [uU] [bB] [sS] [tT] [rR] [iI] [nN] [gG];
SUM : [sS] [uM] [mM];
THEN : [tT] [hH] [eE] [nN]; THEN : [tT] [hH] [eE] [nN];
TIES : [tT] [iI] [eE] [sS]; TIES : [tT] [iI] [eE] [sS];
TIME : [tT] [iI] [mM] [eE]; TIME : [tT] [iI] [mM] [eE];

View File

@ -948,9 +948,10 @@ function
: standardFunction : standardFunction
| aggregateFunction | aggregateFunction
| jpaCollectionFunction | jpaCollectionFunction
| hqlCollectionFunction | indexAggregateFunction
| jpaNonstandardFunction | elementAggregateFunction
| collectionFunctionMisuse | collectionFunctionMisuse
| jpaNonstandardFunction
| genericFunction | genericFunction
; ;
@ -1002,13 +1003,27 @@ jpaCollectionFunction
; ;
/** /**
* The special collection functions defined by HQL * The special aggregate collection functions defined by HQL
*/ */
hqlCollectionFunction indexAggregateFunction
: MAXINDEX LEFT_PAREN path RIGHT_PAREN # MaxIndexFunction : MAXINDEX LEFT_PAREN path RIGHT_PAREN
| MAXELEMENT LEFT_PAREN path RIGHT_PAREN # MaxElementFunction | MININDEX LEFT_PAREN path RIGHT_PAREN
| MININDEX LEFT_PAREN path RIGHT_PAREN # MinIndexFunction | MAX LEFT_PAREN INDEX path RIGHT_PAREN
| MINELEMENT LEFT_PAREN path RIGHT_PAREN # MinElementFunction | MIN LEFT_PAREN INDEX path RIGHT_PAREN
| SUM LEFT_PAREN INDEX path RIGHT_PAREN
| AVG LEFT_PAREN ELEMENT path RIGHT_PAREN
;
/**
* The special aggregate collection functions defined by HQL
*/
elementAggregateFunction
: MAXELEMENT LEFT_PAREN path RIGHT_PAREN
| MINELEMENT LEFT_PAREN path RIGHT_PAREN
| MAX LEFT_PAREN ELEMENT path RIGHT_PAREN
| MIN LEFT_PAREN ELEMENT path RIGHT_PAREN
| SUM LEFT_PAREN ELEMENT path RIGHT_PAREN
| AVG LEFT_PAREN ELEMENT path RIGHT_PAREN
; ;
/** /**
@ -1365,6 +1380,7 @@ identifier
| ANY | ANY
| AS | AS
| ASC | ASC
| AVG
| BETWEEN | BETWEEN
| BOTH | BOTH
| BY | BY
@ -1429,11 +1445,13 @@ identifier
| LOCAL_DATETIME | LOCAL_DATETIME
| LOCAL_TIME | LOCAL_TIME
| MAP | MAP
| MAX
| MAXELEMENT | MAXELEMENT
| MAXINDEX | MAXINDEX
| MEMBER | MEMBER
| MICROSECOND | MICROSECOND
| MILLISECOND | MILLISECOND
| MIN
| MINELEMENT | MINELEMENT
| MININDEX | MININDEX
| MINUTE | MINUTE
@ -1469,6 +1487,7 @@ identifier
| SIZE | SIZE
| SOME | SOME
| SUBSTRING | SUBSTRING
| SUM
| THEN | THEN
| TIES | TIES
| TIME | TIME

View File

@ -102,10 +102,8 @@ import org.hibernate.query.sqm.tree.domain.AbstractSqmFrom;
import org.hibernate.query.sqm.tree.domain.SqmCorrelation; import org.hibernate.query.sqm.tree.domain.SqmCorrelation;
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference; import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
import org.hibernate.query.sqm.tree.domain.SqmMapJoin; import org.hibernate.query.sqm.tree.domain.SqmMapJoin;
import org.hibernate.query.sqm.tree.domain.SqmMaxElementPath; import org.hibernate.query.sqm.tree.domain.SqmElementAggregateFunction;
import org.hibernate.query.sqm.tree.domain.SqmMaxIndexPath; import org.hibernate.query.sqm.tree.domain.SqmIndexAggregateFunction;
import org.hibernate.query.sqm.tree.domain.SqmMinElementPath;
import org.hibernate.query.sqm.tree.domain.SqmMinIndexPath;
import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor; import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor;
@ -3969,57 +3967,40 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
} }
@Override @Override
public SqmMaxElementPath<?> visitMaxElementFunction(HqlParser.MaxElementFunctionContext ctx) { public SqmElementAggregateFunction<?> visitElementAggregateFunction(HqlParser.ElementAggregateFunctionContext ctx) {
if ( creationOptions.useStrictJpaCompliance() ) { if ( creationOptions.useStrictJpaCompliance() ) {
throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION ); throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION );
} }
return new SqmMaxElementPath<>( consumePluralAttributeReference( (HqlParser.PathContext) ctx.getChild( 2 ) ) ); SqmPath<?> pluralPath = consumePluralAttributeReference( ctx.path() );
if ( !(pluralPath instanceof SqmPluralValuedSimplePath) ) {
throw new SemanticException( "Path '" + ctx.path().getText() + "' did not resolve to a many-valued attribute" );
}
String functionName = ctx.getChild(0).getText().substring(0, 3);
return new SqmElementAggregateFunction<>( pluralPath, functionName );
} }
@Override @Override
public SqmMinElementPath<?> visitMinElementFunction(HqlParser.MinElementFunctionContext ctx) { public SqmIndexAggregateFunction<?> visitIndexAggregateFunction(HqlParser.IndexAggregateFunctionContext ctx) {
if ( creationOptions.useStrictJpaCompliance() ) { if ( creationOptions.useStrictJpaCompliance() ) {
throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION ); throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION );
} }
return new SqmMinElementPath<>( consumePluralAttributeReference( (HqlParser.PathContext) ctx.getChild( 2 ) ) ); final SqmPath<?> pluralPath = consumePluralAttributeReference( ctx.path() );
} if ( !(pluralPath instanceof SqmPluralValuedSimplePath) ) {
throw new SemanticException( "Path '" + ctx.path().getText() + "' did not resolve to a many-valued attribute" );
@Override
public SqmMaxIndexPath<?> visitMaxIndexFunction(HqlParser.MaxIndexFunctionContext ctx) {
if ( creationOptions.useStrictJpaCompliance() ) {
throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION );
} }
final SqmPath<?> pluralPath = consumePluralAttributeReference( (HqlParser.PathContext) ctx.getChild( 2 ) );
if ( !isIndexedPluralAttribute( pluralPath ) ) { if ( !isIndexedPluralAttribute( pluralPath ) ) {
throw new SemanticException( throw new SemanticException(
"maxindex() function can only be applied to path expressions which resolve to an " + "maxindex() function can only be applied to path expressions which resolve to an " +
"indexed collection (list,map); specified path [" + ctx.getChild( 2 ).getText() + "indexed collection (list,map); specified path [" + ctx.path() +
"] resolved to " + pluralPath.getReferencedPathSource() "] resolved to " + pluralPath.getReferencedPathSource()
); );
} }
return new SqmMaxIndexPath<>( pluralPath ); String functionName = ctx.getChild(0).getText().substring(0, 3);
} return new SqmIndexAggregateFunction<>( pluralPath, functionName );
@Override
public SqmMinIndexPath<?> visitMinIndexFunction(HqlParser.MinIndexFunctionContext ctx) {
if ( creationOptions.useStrictJpaCompliance() ) {
throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION );
}
final SqmPath<?> pluralPath = consumePluralAttributeReference( (HqlParser.PathContext) ctx.getChild( 2 ) );
if ( !isIndexedPluralAttribute( pluralPath ) ) {
throw new SemanticException(
"minindex() function can only be applied to path expressions which resolve to an " +
"indexed collection (list,map); specified path [" + ctx.getChild( 2 ).getText() +
"] resolved to " + pluralPath.getReferencedPathSource()
);
}
return new SqmMinIndexPath<>( pluralPath );
} }
@Override @Override

View File

@ -20,10 +20,8 @@ import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath; import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference; import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
import org.hibernate.query.sqm.tree.domain.SqmMaxElementPath; import org.hibernate.query.sqm.tree.domain.SqmElementAggregateFunction;
import org.hibernate.query.sqm.tree.domain.SqmMaxIndexPath; import org.hibernate.query.sqm.tree.domain.SqmIndexAggregateFunction;
import org.hibernate.query.sqm.tree.domain.SqmMinElementPath;
import org.hibernate.query.sqm.tree.domain.SqmMinIndexPath;
import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin; import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin;
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath; import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
@ -152,13 +150,9 @@ public interface SemanticQueryWalker<T> {
T visitIndexedPluralAccessPath(SqmIndexedCollectionAccessPath<?> path); T visitIndexedPluralAccessPath(SqmIndexedCollectionAccessPath<?> path);
T visitMaxElementPath(SqmMaxElementPath<?> path); T visitElementAggregateFunction(SqmElementAggregateFunction<?> path);
T visitMinElementPath(SqmMinElementPath<?> path); T visitIndexAggregateFunction(SqmIndexAggregateFunction<?> path);
T visitMaxIndexPath(SqmMaxIndexPath<?> path);
T visitMinIndexPath(SqmMinIndexPath<?> path);
T visitTreatedPath(SqmTreatedPath<?, ?> sqmTreatedPath); T visitTreatedPath(SqmTreatedPath<?, ?> sqmTreatedPath);

View File

@ -25,10 +25,8 @@ import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath; import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference; import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
import org.hibernate.query.sqm.tree.domain.SqmMaxElementPath; import org.hibernate.query.sqm.tree.domain.SqmElementAggregateFunction;
import org.hibernate.query.sqm.tree.domain.SqmMaxIndexPath; import org.hibernate.query.sqm.tree.domain.SqmIndexAggregateFunction;
import org.hibernate.query.sqm.tree.domain.SqmMinElementPath;
import org.hibernate.query.sqm.tree.domain.SqmMinIndexPath;
import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin; import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin;
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath; import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
@ -947,22 +945,12 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
} }
@Override @Override
public Object visitMaxElementPath(SqmMaxElementPath binding) { public Object visitElementAggregateFunction(SqmElementAggregateFunction binding) {
return null; return null;
} }
@Override @Override
public Object visitMinElementPath(SqmMinElementPath path) { public Object visitIndexAggregateFunction(SqmIndexAggregateFunction path) {
return null;
}
@Override
public Object visitMaxIndexPath(SqmMaxIndexPath path) {
return null;
}
@Override
public Object visitMinIndexPath(SqmMinIndexPath path) {
return null; return null;
} }

View File

@ -23,10 +23,8 @@ import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath; import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference; import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
import org.hibernate.query.sqm.tree.domain.SqmMaxElementPath; import org.hibernate.query.sqm.tree.domain.SqmElementAggregateFunction;
import org.hibernate.query.sqm.tree.domain.SqmMaxIndexPath; import org.hibernate.query.sqm.tree.domain.SqmIndexAggregateFunction;
import org.hibernate.query.sqm.tree.domain.SqmMinElementPath;
import org.hibernate.query.sqm.tree.domain.SqmMinIndexPath;
import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin; import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin;
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
@ -318,22 +316,12 @@ public abstract class BaseSemanticQueryWalker implements SemanticQueryWalker<Obj
} }
@Override @Override
public Object visitMaxElementPath(SqmMaxElementPath<?> path) { public Object visitElementAggregateFunction(SqmElementAggregateFunction<?> path) {
return path; return path;
} }
@Override @Override
public Object visitMinElementPath(SqmMinElementPath<?> path) { public Object visitIndexAggregateFunction(SqmIndexAggregateFunction<?> path) {
return path;
}
@Override
public Object visitMaxIndexPath(SqmMaxIndexPath<?> path) {
return path;
}
@Override
public Object visitMinIndexPath(SqmMinIndexPath<?> path) {
return path; return path;
} }

View File

@ -168,10 +168,8 @@ import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath; import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference; import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
import org.hibernate.query.sqm.tree.domain.SqmMaxElementPath; import org.hibernate.query.sqm.tree.domain.SqmElementAggregateFunction;
import org.hibernate.query.sqm.tree.domain.SqmMaxIndexPath; import org.hibernate.query.sqm.tree.domain.SqmIndexAggregateFunction;
import org.hibernate.query.sqm.tree.domain.SqmMinElementPath;
import org.hibernate.query.sqm.tree.domain.SqmMinIndexPath;
import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath; import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
@ -3270,24 +3268,29 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
); );
} }
@Override protected Expression createMinOrMaxIndexOrElement(
public Expression visitMaxElementPath(SqmMaxElementPath<?> path) { AbstractSqmSpecificPluralPartPath<?> pluralPartPath,
return createMinOrMaxIndexOrElement( path, false, true ); boolean index,
String functionName) {
boolean isMinOrMax = functionName.equalsIgnoreCase("min")
|| functionName.equalsIgnoreCase("max");
// Try to create a lateral sub-query join if possible which allows the re-use of the expression
if ( isMinOrMax && creationContext.getSessionFactory().getJdbcServices().getDialect().supportsLateral() ) {
return createLateralJoinExpression( pluralPartPath, index, functionName );
}
else {
return createCorrelatedAggregateSubQuery( pluralPartPath, index, functionName );
}
} }
@Override @Override
public Expression visitMinElementPath(SqmMinElementPath<?> path) { public Expression visitElementAggregateFunction(SqmElementAggregateFunction<?> path) {
return createMinOrMaxIndexOrElement( path, false, false ); return createMinOrMaxIndexOrElement( path, false, path.getFunctionName() );
} }
@Override @Override
public Expression visitMaxIndexPath(SqmMaxIndexPath<?> path) { public Expression visitIndexAggregateFunction(SqmIndexAggregateFunction<?> path) {
return createMinOrMaxIndexOrElement( path, true, true ); return createMinOrMaxIndexOrElement( path, true, path.getFunctionName() );
}
@Override
public Expression visitMinIndexPath(SqmMinIndexPath<?> path) {
return createMinOrMaxIndexOrElement( path, true, false );
} }
@Override @Override
@ -3466,23 +3469,10 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}; };
} }
protected Expression createMinOrMaxIndexOrElement(
AbstractSqmSpecificPluralPartPath<?> pluralPartPath,
boolean index,
boolean max) {
// Try to create a lateral sub-query join if possible which allows the re-use of the expression
if ( creationContext.getSessionFactory().getJdbcServices().getDialect().supportsLateral() ) {
return createLateralJoinExpression( pluralPartPath, index, max );
}
else {
return createCorrelatedAggregateSubQuery( pluralPartPath, index, max );
}
}
protected Expression createCorrelatedAggregateSubQuery( protected Expression createCorrelatedAggregateSubQuery(
AbstractSqmSpecificPluralPartPath<?> pluralPartPath, AbstractSqmSpecificPluralPartPath<?> pluralPartPath,
boolean index, boolean index,
boolean max) { String function) {
prepareReusablePath( pluralPartPath.getLhs(), () -> null ); prepareReusablePath( pluralPartPath.getLhs(), () -> null );
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) determineValueMapping( final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) determineValueMapping(
@ -3520,11 +3510,12 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
registerPluralTableGroupParts( tableGroup ); registerPluralTableGroupParts( tableGroup );
subQuerySpec.getFromClause().addRoot( tableGroup ); subQuerySpec.getFromClause().addRoot( tableGroup );
final AbstractSqmSelfRenderingFunctionDescriptor functionDescriptor = (AbstractSqmSelfRenderingFunctionDescriptor) creationContext final AbstractSqmSelfRenderingFunctionDescriptor functionDescriptor =
.getSessionFactory() (AbstractSqmSelfRenderingFunctionDescriptor) creationContext
.getQueryEngine() .getSessionFactory()
.getSqmFunctionRegistry() .getQueryEngine()
.findFunctionDescriptor( max ? "max" : "min" ); .getSqmFunctionRegistry()
.findFunctionDescriptor( function );
final CollectionPart collectionPart = index final CollectionPart collectionPart = index
? pluralAttributeMapping.getIndexDescriptor() ? pluralAttributeMapping.getIndexDescriptor()
: pluralAttributeMapping.getElementDescriptor(); : pluralAttributeMapping.getElementDescriptor();
@ -3591,7 +3582,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
protected Expression createLateralJoinExpression( protected Expression createLateralJoinExpression(
AbstractSqmSpecificPluralPartPath<?> pluralPartPath, AbstractSqmSpecificPluralPartPath<?> pluralPartPath,
boolean index, boolean index,
boolean max) { String functionName) {
prepareReusablePath( pluralPartPath.getLhs(), () -> null ); prepareReusablePath( pluralPartPath.getLhs(), () -> null );
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) determineValueMapping( final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) determineValueMapping(
@ -3611,7 +3602,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
modelPart = collectionPart; modelPart = collectionPart;
} }
final int jdbcTypeCount = modelPart.getJdbcTypeCount(); final int jdbcTypeCount = modelPart.getJdbcTypeCount();
final String pathName = ( max ? "max" : "min" ) + ( index ? "_index" : "_element" ); final String pathName = functionName + ( index ? "_index" : "_element" );
final String identifierVariable = parentTableGroup.getPrimaryTableReference().getIdentificationVariable() final String identifierVariable = parentTableGroup.getPrimaryTableReference().getIdentificationVariable()
+ "_" + pathName; + "_" + pathName;
final NavigablePath queryPath = new NavigablePath( parentTableGroup.getNavigablePath(), pathName, identifierVariable ); final NavigablePath queryPath = new NavigablePath( parentTableGroup.getNavigablePath(), pathName, identifierVariable );
@ -3680,7 +3671,9 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
subQuerySpec.addSortSpecification( subQuerySpec.addSortSpecification(
new SortSpecification( new SortSpecification(
columnReference, columnReference,
max ? SortOrder.DESCENDING : SortOrder.ASCENDING functionName.equalsIgnoreCase("max")
? SortOrder.DESCENDING
: SortOrder.ASCENDING
) )
); );
resultColumnReferences.add( resultColumnReferences.add(

View File

@ -14,16 +14,23 @@ import org.hibernate.query.sqm.SqmPathSource;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class SqmMaxElementPath<T> extends AbstractSqmSpecificPluralPartPath<T> { public class SqmElementAggregateFunction<T> extends AbstractSqmSpecificPluralPartPath<T> {
public static final String NAVIGABLE_NAME = "{max-element}"; public static final String NAVIGABLE_NAME = "{max-element}";
public SqmMaxElementPath(SqmPath<?> pluralDomainPath) { private final String functionName;
public SqmElementAggregateFunction(SqmPath<?> pluralDomainPath, String functionName) {
//noinspection unchecked //noinspection unchecked
super( super(
pluralDomainPath.getNavigablePath().getParent().append( pluralDomainPath.getNavigablePath().getLocalName(), NAVIGABLE_NAME ), pluralDomainPath.getNavigablePath().getParent().append( pluralDomainPath.getNavigablePath().getLocalName(), NAVIGABLE_NAME ),
pluralDomainPath, pluralDomainPath,
(PluralPersistentAttribute<?, ?, T>) pluralDomainPath.getReferencedPathSource() (PluralPersistentAttribute<?, ?, T>) pluralDomainPath.getReferencedPathSource()
); );
this.functionName = functionName;
}
public String getFunctionName() {
return functionName;
} }
@Override @Override
@ -43,12 +50,12 @@ public class SqmMaxElementPath<T> extends AbstractSqmSpecificPluralPartPath<T> {
@Override @Override
public <X> X accept(SemanticQueryWalker<X> walker) { public <X> X accept(SemanticQueryWalker<X> walker) {
return walker.visitMaxElementPath( this ); return walker.visitElementAggregateFunction( this );
} }
@Override @Override
public void appendHqlString(StringBuilder sb) { public void appendHqlString(StringBuilder sb) {
sb.append( "maxelement(" ); sb.append(functionName).append( "(" );
getLhs().appendHqlString( sb ); getLhs().appendHqlString( sb );
sb.append( ')' ); sb.append( ')' );
} }

View File

@ -11,24 +11,25 @@ import org.hibernate.metamodel.model.domain.MapPersistentAttribute;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute; import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.hql.spi.SemanticPathPart;
import org.hibernate.query.hql.spi.SqmCreationState; import org.hibernate.query.hql.spi.SqmCreationState;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class SqmMaxIndexPath<T> extends AbstractSqmSpecificPluralPartPath<T> { public class SqmIndexAggregateFunction<T> extends AbstractSqmSpecificPluralPartPath<T> {
public static final String NAVIGABLE_NAME = "{max-index}"; public static final String NAVIGABLE_NAME = "{max-index}";
private final SqmPathSource<T> indexPathSource; private final SqmPathSource<T> indexPathSource;
private final String functionName;
public SqmMaxIndexPath(SqmPath<?> pluralDomainPath) { public SqmIndexAggregateFunction(SqmPath<?> pluralDomainPath, String functionName) {
//noinspection unchecked //noinspection unchecked
super( super(
pluralDomainPath.getNavigablePath().getParent().append( pluralDomainPath.getNavigablePath().getLocalName(), NAVIGABLE_NAME ), pluralDomainPath.getNavigablePath().getParent().append( pluralDomainPath.getNavigablePath().getLocalName(), NAVIGABLE_NAME ),
pluralDomainPath, pluralDomainPath,
(PluralPersistentAttribute<?, ?, T>) pluralDomainPath.getReferencedPathSource() (PluralPersistentAttribute<?, ?, T>) pluralDomainPath.getReferencedPathSource()
); );
this.functionName = functionName;
if ( getPluralAttribute() instanceof ListPersistentAttribute ) { if ( getPluralAttribute() instanceof ListPersistentAttribute ) {
//noinspection unchecked //noinspection unchecked
@ -43,6 +44,10 @@ public class SqmMaxIndexPath<T> extends AbstractSqmSpecificPluralPartPath<T> {
} }
} }
public String getFunctionName() {
return functionName;
}
@Override @Override
public SqmPath<?> resolvePathPart( public SqmPath<?> resolvePathPart(
String name, String name,
@ -60,12 +65,12 @@ public class SqmMaxIndexPath<T> extends AbstractSqmSpecificPluralPartPath<T> {
@Override @Override
public <X> X accept(SemanticQueryWalker<X> walker) { public <X> X accept(SemanticQueryWalker<X> walker) {
return walker.visitMaxIndexPath( this ); return walker.visitIndexAggregateFunction( this );
} }
@Override @Override
public void appendHqlString(StringBuilder sb) { public void appendHqlString(StringBuilder sb) {
sb.append( "maxindex(" ); sb.append(functionName).append( "(" );
getLhs().appendHqlString( sb ); getLhs().appendHqlString( sb );
sb.append( ')' ); sb.append( ')' );
} }

View File

@ -1,58 +0,0 @@
/*
* 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 http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.query.sqm.tree.domain;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.query.SemanticException;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.hql.spi.SemanticPathPart;
import org.hibernate.query.hql.spi.SqmCreationState;
/**
* @author Steve Ebersole
*/
public class SqmMinElementPath<T> extends AbstractSqmSpecificPluralPartPath<T> {
public static final String NAVIGABLE_NAME = "{min-element}";
public SqmMinElementPath(SqmPath<?> pluralDomainPath) {
//noinspection unchecked
super(
pluralDomainPath.getNavigablePath().getParent().append( pluralDomainPath.getNavigablePath().getLocalName(), NAVIGABLE_NAME ),
pluralDomainPath,
(PluralPersistentAttribute<?, ?, T>) pluralDomainPath.getReferencedPathSource()
);
}
@Override
public SqmPath<?> resolvePathPart(
String name,
boolean isTerminal,
SqmCreationState creationState) {
final SqmPath<?> sqmPath = get( name );
creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath );
return sqmPath;
}
@Override
public SqmPathSource<T> getReferencedPathSource() {
return getPluralAttribute().getElementPathSource();
}
@Override
public <X> X accept(SemanticQueryWalker<X> walker) {
return walker.visitMinElementPath( this );
}
@Override
public void appendHqlString(StringBuilder sb) {
sb.append( "minelement(" );
getLhs().appendHqlString( sb );
sb.append( ')' );
}
}

View File

@ -1,73 +0,0 @@
/*
* 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 http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.query.sqm.tree.domain;
import org.hibernate.metamodel.model.domain.ListPersistentAttribute;
import org.hibernate.metamodel.model.domain.MapPersistentAttribute;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.hql.spi.SemanticPathPart;
import org.hibernate.query.hql.spi.SqmCreationState;
/**
* @author Steve Ebersole
*/
public class SqmMinIndexPath<T> extends AbstractSqmSpecificPluralPartPath<T> {
public static final String NAVIGABLE_NAME = "{min-index}";
private final SqmPathSource<T> indexPathSource;
public SqmMinIndexPath(SqmPath<?> pluralDomainPath) {
//noinspection unchecked
super(
pluralDomainPath.getNavigablePath().getParent().append( pluralDomainPath.getNavigablePath().getLocalName(), NAVIGABLE_NAME ),
pluralDomainPath,
(PluralPersistentAttribute<?, ?, T>) pluralDomainPath.getReferencedPathSource()
);
if ( getPluralAttribute() instanceof ListPersistentAttribute ) {
//noinspection unchecked
this.indexPathSource = (SqmPathSource<T>) getPluralAttribute().getIndexPathSource();
}
else if ( getPluralAttribute() instanceof MapPersistentAttribute ) {
//noinspection unchecked
this.indexPathSource = ( (MapPersistentAttribute<?, T, ?>) getPluralAttribute() ).getKeyPathSource();
}
else {
throw new UnsupportedOperationException( "Plural attribute [" + getPluralAttribute() + "] is not indexed" );
}
}
@Override
public SqmPath<?> resolvePathPart(
String name,
boolean isTerminal,
SqmCreationState creationState) {
final SqmPath<?> sqmPath = get( name );
creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath );
return sqmPath;
}
@Override
public SqmPathSource<T> getReferencedPathSource() {
return indexPathSource;
}
@Override
public <X> X accept(SemanticQueryWalker<X> walker) {
return walker.visitMinIndexPath( this );
}
@Override
public void appendHqlString(StringBuilder sb) {
sb.append( "minindex(" );
getLhs().appendHqlString( sb );
sb.append( ')' );
}
}

View File

@ -43,13 +43,13 @@ public class MapIndexFormulaTest {
session.createQuery( "from Group g join g.users u where g.name = 'something' and index(u) = 'nada'" ) session.createQuery( "from Group g join g.users u where g.name = 'something' and index(u) = 'nada'" )
.list(); .list();
session.createQuery( session.createQuery(
"from Group g join g.users u where g.name = 'something' and minindex(u) = 'nada'" ) "from Group g where g.name = 'something' and minindex(g.users) = 'nada'" )
.list(); .list();
session.createQuery( session.createQuery(
"from Group g join g.users u where g.name = 'something' and maxindex(u) = 'nada'" ) "from Group g where g.name = 'something' and maxindex(g.users) = 'nada'" )
.list(); .list();
session.createQuery( session.createQuery(
"from Group g join g.users u where g.name = 'something' and maxindex(u) = 'nada' and maxindex(u) = 'nada'" ) "from Group g where g.name = 'something' and maxindex(g.users) = 'nada' and maxindex(g.users) = 'nada'" )
.list(); .list();
} }
); );

View File

@ -52,7 +52,7 @@ public class PluralAttributeMappingTests {
final MappingMetamodel domainModel = scope.getSessionFactory().getDomainModel(); final MappingMetamodel domainModel = scope.getSessionFactory().getDomainModel();
final EntityMappingType containerEntityDescriptor = domainModel.getEntityDescriptor( EntityOfLists.class ); final EntityMappingType containerEntityDescriptor = domainModel.getEntityDescriptor( EntityOfLists.class );
assertThat( containerEntityDescriptor.getNumberOfAttributeMappings(), is( 7 ) ); assertThat( containerEntityDescriptor.getNumberOfAttributeMappings(), is( 8 ) );
final AttributeMapping listOfBasics = containerEntityDescriptor.findAttributeMapping( "listOfBasics" ); final AttributeMapping listOfBasics = containerEntityDescriptor.findAttributeMapping( "listOfBasics" );
assertThat( listOfBasics, notNullValue() ); assertThat( listOfBasics, notNullValue() );
@ -116,7 +116,7 @@ public class PluralAttributeMappingTests {
final MappingMetamodel domainModel = scope.getSessionFactory().getDomainModel(); final MappingMetamodel domainModel = scope.getSessionFactory().getDomainModel();
final EntityMappingType containerEntityDescriptor = domainModel.getEntityDescriptor( EntityOfMaps.class ); final EntityMappingType containerEntityDescriptor = domainModel.getEntityDescriptor( EntityOfMaps.class );
assertThat( containerEntityDescriptor.getNumberOfAttributeMappings(), is( 16 ) ); assertThat( containerEntityDescriptor.getNumberOfAttributeMappings(), is( 17 ) );
final PluralAttributeMapping basicByBasic = (PluralAttributeMapping) containerEntityDescriptor.findAttributeMapping( "basicByBasic" ); final PluralAttributeMapping basicByBasic = (PluralAttributeMapping) containerEntityDescriptor.findAttributeMapping( "basicByBasic" );
assertThat( basicByBasic, notNullValue() ); assertThat( basicByBasic, notNullValue() );

View File

@ -67,10 +67,13 @@ public class FunctionTests {
EntityOfLists eol = new EntityOfLists(1,""); EntityOfLists eol = new EntityOfLists(1,"");
eol.addBasic("hello"); eol.addBasic("hello");
eol.addNumber(1.0);
eol.addNumber(2.0);
em.persist(eol); em.persist(eol);
EntityOfMaps eom = new EntityOfMaps(2,""); EntityOfMaps eom = new EntityOfMaps(2,"");
eom.addBasicByBasic("hello", "world"); eom.addBasicByBasic("hello", "world");
eom.addNumberByNumber(1,1.0);
em.persist(eom); em.persist(eom);
} }
); );
@ -90,6 +93,45 @@ public class FunctionTests {
); );
} }
@Test
@RequiresDialect(H2Dialect.class)
@RequiresDialect(HSQLDialect.class)
@RequiresDialect(DerbyDialect.class)
@RequiresDialect(MySQLDialect.class)
@RequiresDialect(SybaseDialect.class)
@RequiresDialect(MariaDBDialect.class)
@RequiresDialect(TiDBDialect.class)
// it's failing on the other dialects due to a bug in query translator
public void testMaxMinSumIndexElement(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
assertThat( session.createQuery("select max(index eol.listOfNumbers) from EntityOfLists eol")
.getSingleResult(), is(1) );
assertThat( session.createQuery("select max(element eol.listOfNumbers) from EntityOfLists eol")
.list().get(0),
// .getSingleResult(),
is(2.0) );
assertThat( session.createQuery("select sum(index eol.listOfNumbers) from EntityOfLists eol")
.getSingleResult(), is(1) );
assertThat( session.createQuery("select sum(element eol.listOfNumbers) from EntityOfLists eol")
.list().get(0),
// .getSingleResult(),
is(3.0) );
assertThat( session.createQuery("select max(index eom.numberByNumber) from EntityOfMaps eom")
.getSingleResult(), is(1) );
assertThat( session.createQuery("select max(element eom.numberByNumber) from EntityOfMaps eom")
.getSingleResult(), is(1.0) );
assertThat( session.createQuery("select sum(index eom.numberByNumber) from EntityOfMaps eom")
.getSingleResult(), is(1) );
assertThat( session.createQuery("select sum(element eom.numberByNumber) from EntityOfMaps eom")
.getSingleResult(), is(1.0) );
}
);
}
@Test @Test
@RequiresDialect(H2Dialect.class) @RequiresDialect(H2Dialect.class)
@RequiresDialect(HSQLDialect.class) @RequiresDialect(HSQLDialect.class)
@ -102,19 +144,11 @@ public class FunctionTests {
public void testMaxindexMaxelement(SessionFactoryScope scope) { public void testMaxindexMaxelement(SessionFactoryScope scope) {
scope.inTransaction( scope.inTransaction(
session -> { session -> {
assertThat( session.createQuery("select maxindex(l) from EntityOfLists eol join eol.listOfBasics l")
.getSingleResult(), is(0) );
assertThat( session.createQuery("select maxelement(l) from EntityOfLists eol join eol.listOfBasics l")
.getSingleResult(), is("hello") );
assertThat( session.createQuery("select maxindex(eol.listOfBasics) from EntityOfLists eol") assertThat( session.createQuery("select maxindex(eol.listOfBasics) from EntityOfLists eol")
.getSingleResult(), is(0) ); .getSingleResult(), is(0) );
assertThat( session.createQuery("select maxelement(eol.listOfBasics) from EntityOfLists eol") assertThat( session.createQuery("select maxelement(eol.listOfBasics) from EntityOfLists eol")
.getSingleResult(), is("hello") ); .getSingleResult(), is("hello") );
assertThat( session.createQuery("select maxindex(m) from EntityOfMaps eom join eom.basicByBasic m")
.getSingleResult(), is("hello") );
assertThat( session.createQuery("select maxelement(m) from EntityOfMaps eom join eom.basicByBasic m")
.getSingleResult(), is("world") );
assertThat( session.createQuery("select maxindex(eom.basicByBasic) from EntityOfMaps eom") assertThat( session.createQuery("select maxindex(eom.basicByBasic) from EntityOfMaps eom")
.getSingleResult(), is("hello") ); .getSingleResult(), is("hello") );
assertThat( session.createQuery("select maxelement(eom.basicByBasic) from EntityOfMaps eom") assertThat( session.createQuery("select maxelement(eom.basicByBasic) from EntityOfMaps eom")

View File

@ -126,9 +126,9 @@ public class TernaryTest extends BaseCoreFunctionalTestCase {
session.beginTransaction(); session.beginTransaction();
session.createQuery( "from Employee e join e.managerBySite as m where index(m) is not null" ) session.createQuery( "from Employee e join e.managerBySite as m where index(m) is not null" )
.list(); .list();
session.createQuery( "from Employee e join e.managerBySite as m where minIndex(m) is not null" ) session.createQuery( "from Employee e where minIndex(e.managerBySite) is not null" )
.list(); .list();
session.createQuery( "from Employee e join e.managerBySite as m where maxIndex(m) is not null" ) session.createQuery( "from Employee e where maxIndex(e.managerBySite) is not null" )
.list(); .list();
session.getTransaction().commit(); session.getTransaction().commit();
session.close(); session.close();

View File

@ -29,6 +29,7 @@ public class EntityOfLists {
private String name; private String name;
private List<String> listOfBasics; private List<String> listOfBasics;
private List<Double> listOfNumbers;
private List<EnumValue> listOfConvertedEnums; private List<EnumValue> listOfConvertedEnums;
private List<EnumValue> listOfEnums; private List<EnumValue> listOfEnums;
@ -78,6 +79,17 @@ public class EntityOfLists {
this.listOfBasics = listOfBasics; this.listOfBasics = listOfBasics;
} }
@ElementCollection
@OrderColumn(name="num_indx")
@CollectionTable(name = "EntityOfLists_numbers")
public List<Double> getListOfNumbers() {
return listOfNumbers;
}
public void setListOfNumbers(List<Double> listOfNumbers) {
this.listOfNumbers = listOfNumbers;
}
public void addBasic(String basic) { public void addBasic(String basic) {
if ( listOfBasics == null ) { if ( listOfBasics == null ) {
listOfBasics = new ArrayList<>(); listOfBasics = new ArrayList<>();
@ -85,6 +97,13 @@ public class EntityOfLists {
listOfBasics.add( basic ); listOfBasics.add( basic );
} }
public void addNumber(double number) {
if ( listOfNumbers == null ) {
listOfNumbers = new ArrayList<>();
}
listOfNumbers.add( number );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// listOfConvertedEnums // listOfConvertedEnums

View File

@ -40,6 +40,7 @@ public class EntityOfMaps {
private String name; private String name;
private Map<String, String> basicByBasic; private Map<String, String> basicByBasic;
private Map<Integer, Double> numberByNumber;
private SortedMap<String, String> sortedBasicByBasic; private SortedMap<String, String> sortedBasicByBasic;
private SortedMap<String, String> sortedBasicByBasicWithComparator; private SortedMap<String, String> sortedBasicByBasicWithComparator;
@ -109,6 +110,25 @@ public class EntityOfMaps {
basicByBasic.put( key, val ); basicByBasic.put( key, val );
} }
@ElementCollection
@CollectionTable(name = "EntityOfMaps_number_number1")
@MapKeyColumn(name = "number_key")
@Column(name = "number_val")
public Map<Integer, Double> getNumberByNumber() {
return numberByNumber;
}
public void setNumberByNumber(Map<Integer, Double> numberByNumber) {
this.numberByNumber = numberByNumber;
}
public void addNumberByNumber(int key, double val) {
if ( numberByNumber == null ) {
numberByNumber = new HashMap<>();
}
numberByNumber.put( key, val );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// sortedBasicByBasic // sortedBasicByBasic