re-enable tests
re-organize some tests work on `org.hibernate.test.hql.size` improved key/target handling for fk-descriptor
This commit is contained in:
parent
c4445fbf5c
commit
3958ee2360
|
@ -163,7 +163,10 @@ public class ExceptionConverterImpl implements ExceptionConverter {
|
|||
return new IllegalStateException( e ); //Spec 3.2.3 Synchronization rules
|
||||
}
|
||||
else {
|
||||
final PersistenceException converted = new PersistenceException( cause );
|
||||
final PersistenceException converted = new PersistenceException(
|
||||
"Converting `" + cause.getClass().getName() + "` to JPA `PersistenceException` : " + cause.getMessage(),
|
||||
cause
|
||||
);
|
||||
handlePersistenceException( converted );
|
||||
return converted;
|
||||
}
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
*/
|
||||
package org.hibernate.metamodel.mapping;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
|
@ -34,6 +32,22 @@ public interface ForeignKeyDescriptor extends VirtualModelPart {
|
|||
|
||||
ModelPart getKeyPart();
|
||||
|
||||
/**
|
||||
* Create a DomainResult for the referring-side of the fk
|
||||
*/
|
||||
DomainResult<?> createKeyDomainResult(
|
||||
NavigablePath collectionPath,
|
||||
TableGroup tableGroup,
|
||||
DomainResultCreationState creationState);
|
||||
|
||||
/**
|
||||
* Create a DomainResult for the target-side of the fk
|
||||
*/
|
||||
DomainResult<?> createTargetDomainResult(
|
||||
NavigablePath collectionPath,
|
||||
TableGroup tableGroup,
|
||||
DomainResultCreationState creationState);
|
||||
|
||||
DomainResult createCollectionFetchDomainResult(
|
||||
NavigablePath collectionPath,
|
||||
TableGroup tableGroup,
|
||||
|
|
|
@ -8,8 +8,6 @@ package org.hibernate.metamodel.mapping.internal;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
|
@ -135,6 +133,38 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResult<?> createKeyDomainResult(
|
||||
NavigablePath collectionPath,
|
||||
TableGroup tableGroup,
|
||||
DomainResultCreationState creationState) {
|
||||
assert tableGroup.getTableReference( keyTable ) != null;
|
||||
|
||||
return createDomainResult(
|
||||
collectionPath,
|
||||
tableGroup,
|
||||
keyTable,
|
||||
keyMappingType,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResult<?> createTargetDomainResult(
|
||||
NavigablePath collectionPath,
|
||||
TableGroup tableGroup,
|
||||
DomainResultCreationState creationState) {
|
||||
assert tableGroup.getTableReference( targetTable ) != null;
|
||||
|
||||
return createDomainResult(
|
||||
collectionPath,
|
||||
tableGroup,
|
||||
targetTable,
|
||||
targetMappingType,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResult createCollectionFetchDomainResult(
|
||||
NavigablePath collectionPath,
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.metamodel.mapping.internal;
|
|||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntFunction;
|
||||
|
@ -46,6 +47,7 @@ import org.hibernate.sql.ast.tree.from.TableReference;
|
|||
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
|
||||
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
import org.hibernate.sql.results.DomainResultCreationException;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.graph.Fetch;
|
||||
|
@ -112,6 +114,33 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResult<?> createKeyDomainResult(
|
||||
NavigablePath collectionPath,
|
||||
TableGroup tableGroup,
|
||||
DomainResultCreationState creationState) {
|
||||
assert tableGroup.getTableReference( keySide.getContainingTableExpression() ) != null;
|
||||
|
||||
return createDomainResult(
|
||||
collectionPath,
|
||||
tableGroup,
|
||||
keySide,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResult<?> createTargetDomainResult(NavigablePath collectionPath, TableGroup tableGroup, DomainResultCreationState creationState) {
|
||||
assert tableGroup.getTableReference( targetSide.getContainingTableExpression() ) != null;
|
||||
|
||||
return createDomainResult(
|
||||
collectionPath,
|
||||
tableGroup,
|
||||
targetSide,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResult<?> createCollectionFetchDomainResult(
|
||||
NavigablePath collectionPath,
|
||||
|
@ -129,8 +158,22 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
|
|||
NavigablePath navigablePath,
|
||||
TableGroup tableGroup,
|
||||
DomainResultCreationState creationState) {
|
||||
try {
|
||||
return createDomainResult( navigablePath, tableGroup, keySide, creationState );
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new DomainResultCreationException(
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"Unable to create fk key domain-result `%s.%s` relative to `%s`",
|
||||
keySide.getContainingTableExpression(),
|
||||
keySide.getSelectionExpression(),
|
||||
tableGroup
|
||||
),
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResult<?> createDomainResult(
|
||||
|
@ -160,8 +203,10 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
|
|||
DomainResultCreationState creationState) {
|
||||
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
|
||||
final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver();
|
||||
|
||||
final TableReference tableReference = tableGroup.resolveTableReference( selectableMapping.getContainingTableExpression() );
|
||||
final String identificationVariable = tableReference.getIdentificationVariable();
|
||||
|
||||
final SqlSelection sqlSelection = sqlExpressionResolver.resolveSqlSelection(
|
||||
sqlExpressionResolver.resolveSqlExpression(
|
||||
SqlExpressionResolver.createColumnReferenceKey(
|
||||
|
|
|
@ -10,8 +10,8 @@ import org.hibernate.NotYetImplementedFor6Exception;
|
|||
import org.hibernate.query.SemanticException;
|
||||
import org.hibernate.query.hql.HqlLogging;
|
||||
import org.hibernate.query.hql.spi.SemanticPathPart;
|
||||
import org.hibernate.query.sqm.SqmPathSource;
|
||||
import org.hibernate.query.hql.spi.SqmCreationState;
|
||||
import org.hibernate.query.sqm.SqmPathSource;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||
|
||||
|
@ -55,6 +55,22 @@ public class DomainPathPart implements SemanticPathPart {
|
|||
return this;
|
||||
}
|
||||
|
||||
// if we want to allow re-use of matched unaliased SqmFrom nodes
|
||||
//
|
||||
// final SqmPathRegistry pathRegistry = creationState.getCurrentProcessingState().getPathRegistry();
|
||||
// final NavigablePath possibleImplicitAliasPath = lhs.getNavigablePath().append( name );
|
||||
// final SqmPath fromByImplicitAlias = pathRegistry.findPath( possibleImplicitAliasPath );
|
||||
//
|
||||
// if ( fromByImplicitAlias != null ) {
|
||||
// if ( fromByImplicitAlias instanceof SqmFrom ) {
|
||||
// final String explicitPathAlias = fromByImplicitAlias.getExplicitAlias();
|
||||
// if ( explicitPathAlias == null || Objects.equals( possibleImplicitAliasPath.getFullPath(), explicitPathAlias ) ) {
|
||||
// currentPath = fromByImplicitAlias;
|
||||
// return isTerminal ? currentPath : this;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
currentPath = subPathSource.createSqmPath( lhs, creationState );
|
||||
if ( isTerminal ) {
|
||||
return currentPath;
|
||||
|
|
|
@ -13,7 +13,7 @@ import org.jboss.logging.Logger;
|
|||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface ResultGraphLogger {
|
||||
public interface ResultGraphLogging {
|
||||
Logger LOGGER = ResultsLogger.subLogger( "graph" );
|
||||
|
||||
boolean DEBUG_ENABLED = LOGGER.isDebugEnabled();
|
|
@ -15,6 +15,7 @@ import org.hibernate.engine.FetchTiming;
|
|||
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
|
@ -28,6 +29,7 @@ import org.hibernate.sql.results.graph.FetchParentAccess;
|
|||
import org.hibernate.sql.results.graph.Fetchable;
|
||||
import org.hibernate.sql.results.graph.FetchableContainer;
|
||||
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
|
||||
import org.hibernate.sql.results.graph.entity.EntityFetch;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
|
||||
/**
|
||||
|
@ -56,14 +58,51 @@ public class EagerCollectionFetch extends CollectionFetch implements FetchParent
|
|||
final NavigablePath parentPath = fetchedPath.getParent();
|
||||
final TableGroup parentTableGroup = parentPath == null ? null : fromClauseAccess.findTableGroup( parentPath );
|
||||
|
||||
|
||||
// NOTE :
|
||||
// `keyContainerResult` = fk target-side
|
||||
// `keyCollectionResult` = fk key-side
|
||||
|
||||
// 3 cases:
|
||||
//
|
||||
// #1 - one-to-many : Teacher#students
|
||||
// teacher( id ), student( id, teacher_fk )
|
||||
//
|
||||
// student.teacher_fk -> teacher.id
|
||||
//
|
||||
// referring : student.teacher_fk
|
||||
// target : teacher.id
|
||||
//
|
||||
// #2 - many-to-many : Teacher#skills
|
||||
// teacher( id ), skill( id ), teacher_skill( teacher_fk, skill_id )
|
||||
//
|
||||
// teacher_skill.skill_id -> skill.id
|
||||
// teacher_skill.teacher_fk -> teacher.id
|
||||
//
|
||||
// referring : teacher_skill.teacher_fk
|
||||
// target : teacher.id
|
||||
//
|
||||
// #3 - element-collection : Teacher#nickNames
|
||||
// teacher( id ), teacher_nicks( teacher_fk, nick )
|
||||
//
|
||||
// teacher_nicks.teacher_fk -> teacher.id
|
||||
//
|
||||
// referring : teacher_nicks.teacher_fk
|
||||
// target : teacher.id
|
||||
|
||||
final ForeignKeyDescriptor keyDescriptor = fetchedAttribute.getKeyDescriptor();
|
||||
if ( parentTableGroup != null ) {
|
||||
// join fetch
|
||||
keyContainerResult = keyDescriptor.createCollectionFetchDomainResult( fetchedPath, parentTableGroup, creationState );
|
||||
keyCollectionResult = keyDescriptor.createDomainResult( fetchedPath, collectionTableGroup, creationState );
|
||||
|
||||
// target-side
|
||||
keyContainerResult = keyDescriptor.createTargetDomainResult( fetchedPath, parentTableGroup, creationState );
|
||||
|
||||
// referring(key)-side
|
||||
keyCollectionResult = keyDescriptor.createKeyDomainResult( fetchedPath, collectionTableGroup, creationState );
|
||||
}
|
||||
else {
|
||||
// select fetch
|
||||
|
||||
// todo (6.0) : we could potentially leverage batch fetching for performance
|
||||
keyContainerResult = keyDescriptor.createCollectionFetchDomainResult( fetchedPath, collectionTableGroup, creationState );
|
||||
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* 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.orm.test.query.hql;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.dialect.DerbyDialect;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.FailureExpected;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@DomainModel( annotatedClasses = CastFunctionTest.MyEntity.class )
|
||||
@SessionFactory
|
||||
@FailureExpected( reason = "requires https://github.com/hibernate/hibernate-orm/pull/3912" )
|
||||
public class CastFunctionTest {
|
||||
@Test
|
||||
public void testStringCasting(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final Dialect dialect = session.getFactory().getJdbcServices().getDialect();
|
||||
if ( DerbyDialect.class.isInstance( dialect ) ) {
|
||||
|
||||
// the conversion from DOUBLE to VARCHAR is not supported by Derby,
|
||||
// using the short name
|
||||
session.createQuery( "select cast(char(e.theLostNumber) as string) from MyEntity e" ).list();
|
||||
// using the java class name
|
||||
session.createQuery( "select cast(char(e.theLostNumber) as java.lang.String) from MyEntity e" ).list();
|
||||
// using the fqn Hibernate Type name
|
||||
session.createQuery( "select cast(char(e.theLostNumber) as org.hibernate.type.StringType) from MyEntity e" )
|
||||
.list();
|
||||
}
|
||||
else {
|
||||
// using the short name
|
||||
session.createQuery( "select cast(e.theLostNumber as string) from MyEntity e" ).list();
|
||||
// using the java class name
|
||||
session.createQuery( "select cast(e.theLostNumber as java.lang.String) from MyEntity e" ).list();
|
||||
// using the fqn Hibernate Type name
|
||||
session.createQuery( "select cast(e.theLostNumber as org.hibernate.type.StringType) from MyEntity e" )
|
||||
.list();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntegerCasting(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
// using the short name
|
||||
session.createQuery( "select cast(e.theLostNumber as integer) from MyEntity e" ).list();
|
||||
// using the java class name (primitive)
|
||||
session.createQuery( "select cast(e.theLostNumber as int) from MyEntity e" ).list();
|
||||
// using the java class name
|
||||
session.createQuery( "select cast(e.theLostNumber as java.lang.Integer) from MyEntity e" ).list();
|
||||
// using the fqn Hibernate Type name
|
||||
session.createQuery( "select cast(e.theLostNumber as org.hibernate.type.IntegerType) from MyEntity e" ).list();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLongCasting(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
// using the short name (also the primitive name)
|
||||
session.createQuery( "select cast(e.theLostNumber as long) from MyEntity e" ).list();
|
||||
// using the java class name
|
||||
session.createQuery( "select cast(e.theLostNumber as java.lang.Long) from MyEntity e" ).list();
|
||||
// using the fqn Hibernate Type name
|
||||
session.createQuery( "select cast(e.theLostNumber as org.hibernate.type.LongType) from MyEntity e" ).list();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFloatCasting(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
// using the short name (also the primitive name)
|
||||
session.createQuery( "select cast(e.theLostNumber as float) from MyEntity e" ).list();
|
||||
// using the java class name
|
||||
session.createQuery( "select cast(e.theLostNumber as java.lang.Float) from MyEntity e" ).list();
|
||||
// using the fqn Hibernate Type name
|
||||
session.createQuery( "select cast(e.theLostNumber as org.hibernate.type.FloatType) from MyEntity e" ).list();
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Entity( name="MyEntity" )
|
||||
public static class MyEntity {
|
||||
@Id
|
||||
private Integer id;
|
||||
private String name;
|
||||
private Double theLostNumber;
|
||||
}
|
||||
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
|
||||
package org.hibernate.test.hql.size;
|
||||
package org.hibernate.orm.test.query.hql.size;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -18,25 +18,27 @@ import javax.persistence.ManyToMany;
|
|||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.dialect.DerbyDialect;
|
||||
|
||||
import org.hibernate.testing.SkipForDialect;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.SkipForDialect;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||
|
||||
@TestForIssue( jiraKey = "HHH-13619" )
|
||||
public class ManyToManySizeTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
@DomainModel( annotatedClasses = { ManyToManySizeTest.Company.class, ManyToManySizeTest.Customer.class } )
|
||||
@SessionFactory
|
||||
public class ManyToManySizeTest {
|
||||
|
||||
@Test
|
||||
public void testSizeAsRestriction() {
|
||||
doInHibernate(
|
||||
this::sessionFactory,
|
||||
session -> {
|
||||
public void testSizeAsRestriction(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final List results = session.createQuery(
|
||||
"select c.id from Company c where size( c.customers ) = 0"
|
||||
).list();
|
||||
|
@ -47,11 +49,10 @@ public class ManyToManySizeTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent")
|
||||
public void testSizeAsCompoundSelectExpression() {
|
||||
doInHibernate(
|
||||
this::sessionFactory,
|
||||
session -> {
|
||||
@SkipForDialect( dialectClass = DerbyDialect.class, reason = "Derby doesn't see that the subquery is functionally dependent" )
|
||||
public void testSizeAsCompoundSelectExpression(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final List results = session.createQuery(
|
||||
"select c.id, c.name, size( c.customers )" +
|
||||
" from Company c" +
|
||||
|
@ -76,13 +77,12 @@ public class ManyToManySizeTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent")
|
||||
public void testSizeAsCtorSelectExpression() {
|
||||
doInHibernate(
|
||||
this::sessionFactory,
|
||||
session -> {
|
||||
@SkipForDialect( dialectClass = DerbyDialect.class, reason = "Derby doesn't see that the subquery is functionally dependent" )
|
||||
public void testSizeAsCtorSelectExpression(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final List results = session.createQuery(
|
||||
"select new org.hibernate.test.hql.size.ManyToManySizeTest$CompanyDto(" +
|
||||
"select new org.hibernate.orm.test.query.hql.size.ManyToManySizeTest$CompanyDto(" +
|
||||
" c.id, c.name, size( c.customers ) )" +
|
||||
" from Company c" +
|
||||
" group by c.id, c.name" +
|
||||
|
@ -106,13 +106,12 @@ public class ManyToManySizeTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent")
|
||||
public void testSizeAsSelectExpressionWithLeftJoin() {
|
||||
doInHibernate(
|
||||
this::sessionFactory,
|
||||
session -> {
|
||||
@SkipForDialect( dialectClass = DerbyDialect.class, reason = "Derby doesn't see that the subquery is functionally dependent" )
|
||||
public void testSizeAsSelectExpressionWithLeftJoin(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final List results = session.createQuery(
|
||||
"select new org.hibernate.test.hql.size.ManyToManySizeTest$CompanyDto(" +
|
||||
"select new org.hibernate.orm.test.query.hql.size.ManyToManySizeTest$CompanyDto(" +
|
||||
" c.id, c.name, size( c.customers ) )" +
|
||||
" from Company c left join c.customers cu" +
|
||||
" group by c.id, c.name" +
|
||||
|
@ -136,13 +135,12 @@ public class ManyToManySizeTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent")
|
||||
public void testSizeAsSelectExpressionWithInnerJoin() {
|
||||
doInHibernate(
|
||||
this::sessionFactory,
|
||||
session -> {
|
||||
@SkipForDialect( dialectClass = DerbyDialect.class, reason = "Derby doesn't see that the subquery is functionally dependent" )
|
||||
public void testSizeAsSelectExpressionWithInnerJoin(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final List results = session.createQuery(
|
||||
"select new org.hibernate.test.hql.size.ManyToManySizeTest$CompanyDto(" +
|
||||
"select new org.hibernate.orm.test.query.hql.size.ManyToManySizeTest$CompanyDto(" +
|
||||
" c.id, c.name, size( c.customers ) )" +
|
||||
" from Company c inner join c.customers cu" +
|
||||
" group by c.id, c.name" +
|
||||
|
@ -162,13 +160,12 @@ public class ManyToManySizeTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent")
|
||||
public void testSizeAsSelectExpressionOfAliasWithInnerJoin() {
|
||||
doInHibernate(
|
||||
this::sessionFactory,
|
||||
session -> {
|
||||
@SkipForDialect( dialectClass = DerbyDialect.class, reason = "Derby doesn't see that the subquery is functionally dependent" )
|
||||
public void testSizeAsSelectExpressionOfAliasWithInnerJoin(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final List results = session.createQuery(
|
||||
"select new org.hibernate.test.hql.size.ManyToManySizeTest$CompanyDto(" +
|
||||
"select new org.hibernate.orm.test.query.hql.size.ManyToManySizeTest$CompanyDto(" +
|
||||
" c.id, c.name, size( cu ) )" +
|
||||
" from Company c inner join c.customers cu" +
|
||||
" group by c.id, c.name" +
|
||||
|
@ -188,13 +185,12 @@ public class ManyToManySizeTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent")
|
||||
public void testSizeAsSelectExpressionExcludeEmptyCollection() {
|
||||
doInHibernate(
|
||||
this::sessionFactory,
|
||||
@SkipForDialect( dialectClass = DerbyDialect.class, reason = "Derby doesn't see that the subquery is functionally dependent" )
|
||||
public void testSizeAsSelectExpressionExcludeEmptyCollection(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final List results = session.createQuery(
|
||||
"select new org.hibernate.test.hql.size.ManyToManySizeTest$CompanyDto(" +
|
||||
"select new org.hibernate.orm.test.query.hql.size.ManyToManySizeTest$CompanyDto(" +
|
||||
" c.id, c.name, size( c.customers ) )" +
|
||||
" from Company c" +
|
||||
" where c.id != 0" +
|
||||
|
@ -214,10 +210,9 @@ public class ManyToManySizeTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSizeAsConditionalExpressionExcludeEmptyCollection() {
|
||||
doInHibernate(
|
||||
this::sessionFactory,
|
||||
session -> {
|
||||
public void testSizeAsConditionalExpressionExcludeEmptyCollection(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final List<Company> results = session.createQuery(
|
||||
"from Company c" +
|
||||
" where size( c.customers ) > 0" +
|
||||
|
@ -240,10 +235,9 @@ public class ManyToManySizeTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSizeAsConditionalExpressionIncludeEmptyCollection() {
|
||||
doInHibernate(
|
||||
this::sessionFactory,
|
||||
session -> {
|
||||
public void testSizeAsConditionalExpressionIncludeEmptyCollection(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final List<Company> results = session.createQuery(
|
||||
"from Company c" +
|
||||
" where size( c.customers ) > -1" +
|
||||
|
@ -270,12 +264,10 @@ public class ManyToManySizeTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void initDataBase(){
|
||||
|
||||
doInHibernate(
|
||||
this::sessionFactory,
|
||||
session -> {
|
||||
@BeforeEach
|
||||
public void createTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
// Add a company with no customers
|
||||
final Company companyWithNoCustomers = new Company( 0 );
|
||||
companyWithNoCustomers.name = "Company 0";
|
||||
|
@ -297,11 +289,10 @@ public class ManyToManySizeTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanupDatabase() {
|
||||
doInHibernate(
|
||||
this::sessionFactory,
|
||||
session -> {
|
||||
@AfterEach
|
||||
public void dropTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
for ( Company company : session.createQuery( "from Company", Company.class ).list() ) {
|
||||
session.delete( company );
|
||||
}
|
||||
|
@ -309,11 +300,6 @@ public class ManyToManySizeTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class[] getAnnotatedClasses() {
|
||||
return new Class[] { Company.class, Customer.class };
|
||||
}
|
||||
|
||||
@Entity(name ="Company")
|
||||
public static class Company {
|
||||
|
|
@ -0,0 +1,495 @@
|
|||
/*
|
||||
* 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.orm.test.query.hql.size;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.jdbc.SQLStatementInspector;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.FailureExpected;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hibernate.testing.jdbc.SQLStatementInspector.extractFromSession;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@TestForIssue(jiraKey = "HHH-13944")
|
||||
//@ServiceRegistry(
|
||||
// settings = @Setting( name = AvailableSettings.STATEMENT_INSPECTOR, value = "org.hibernate.testing.jdbc.SQLStatementInspector" )
|
||||
//)
|
||||
@DomainModel( annotatedClasses = { Skill.class, Teacher.class, Student.class } )
|
||||
@SessionFactory
|
||||
public class ManyToManySizeTest2 {
|
||||
|
||||
@Test
|
||||
public void testSize(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
List<Teacher> teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher join teacher.skills skills where size(skills) > 2",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 0, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher join teacher.skills skills where size(skills) > 1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 1, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher join teacher.skills skills where size(skills) > 0",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
|
||||
// Using "join" (instead of "left join") removes the teacher with no skills from the results.
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher join teacher.skills skills where size(skills) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
|
||||
// Using "left join" includes the teacher with no skills in the results.
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join teacher.skills skills where size(skills) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 3, teachers.size() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeAddFetch(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
List<Teacher> teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.skills join teacher.skills skills where size(skills) > 2",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 0, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.skills join teacher.skills skills where size(skills) > 1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 1, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getSkills() ) );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.skills join teacher.skills skills where size(skills) > 0",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getSkills() ) );
|
||||
|
||||
|
||||
// Using "join" (instead of "left join") removes the teacher with no skills from the results.
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.skills join teacher.skills skills where size(skills) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getSkills() ) );
|
||||
|
||||
// Using "left join" includes the teacher with no skills in the results.
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.skills left join teacher.skills skills where size(skills) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 3, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 2 ).getSkills() ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithoutNestedPath(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
List<Teacher> teachers = session.createQuery(
|
||||
"select teacher from Teacher teacher where size(teacher.skills) > 2",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 0, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select teacher from Teacher teacher where size(teacher.skills) > 1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 1, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select teacher from Teacher teacher where size(teacher.skills) > 0",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
|
||||
// If there is no "join", then results include the teacher with no skills
|
||||
teachers = session.createQuery(
|
||||
"select teacher from Teacher teacher where size(teacher.skills) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 3, teachers.size() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithoutNestedPathAddFetchDistinct(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final SQLStatementInspector statementInspector = extractFromSession( session );
|
||||
statementInspector.clear();
|
||||
|
||||
List<Teacher> teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.skills where size(teacher.skills) > 2",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
statementInspector.assertNumberOfJoins( 0, 2 );
|
||||
assertEquals( 0, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.skills where size(teacher.skills) > 1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 1, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getSkills() ) );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.skills where size(teacher.skills) > 0",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getSkills() ) );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.skills where size(teacher.skills) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 3, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 2 ).getSkills() ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
// @FailureExpected( reason = "org.hibernate.PropertyAccessException : Student#teacher (I think its a stack-overflow)" )
|
||||
public void testSizeWithNestedPathAndImplicitJoin(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final SQLStatementInspector statementInspector = extractFromSession( session );
|
||||
statementInspector.clear();
|
||||
|
||||
List<Student> students = session.createQuery(
|
||||
"select student from Student student where size(student.teacher.skills) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0, students.size() );
|
||||
statementInspector.assertNumberOfJoins( 0, 1 );
|
||||
|
||||
students = session.createQuery(
|
||||
"select student from Student student where size(student.teacher.skills) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 1, students.size() );
|
||||
|
||||
students = session.createQuery(
|
||||
"select student from Student student where size(student.teacher.skills) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2, students.size() );
|
||||
|
||||
// If there is no "join" in the query, then results include the student with the teacher with no skills
|
||||
students = session.createQuery(
|
||||
"select student from Student student where size(student.teacher.skills) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3, students.size() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithNestedPathAndImplicitJoinAddFetchDistinct(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final SQLStatementInspector statementInspector = extractFromSession( session );
|
||||
statementInspector.clear();
|
||||
|
||||
List<Student> students = session.createQuery(
|
||||
"select distinct student " +
|
||||
"from Student student " +
|
||||
" left join fetch student.teacher t " +
|
||||
" left join fetch t.skills " +
|
||||
"where size(student.teacher.skills) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0, students.size() );
|
||||
statementInspector.assertNumberOfJoins( 0, 4 );
|
||||
|
||||
students = session.createQuery(
|
||||
"select distinct student from Student student left join fetch student.teacher t left join fetch t.skills where size(student.teacher.skills) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 1, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||
|
||||
students = session.createQuery(
|
||||
"select distinct student from Student student left join fetch student.teacher t left join fetch t.skills where size(student.teacher.skills) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getSkills() ) );
|
||||
|
||||
students = session.createQuery(
|
||||
"select distinct student from Student student left join fetch student.teacher t left join fetch t.skills where size(student.teacher.skills) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getSkills() ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailureExpected( reason = "org.hibernate.PropertyAccessException : Student#teacher (I think its a stack-overflow)" )
|
||||
public void testSizeWithNestedPath(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
{
|
||||
List<Student> students = session.createQuery(
|
||||
"select student from Student student join student.teacher t join t.skills",
|
||||
Student.class
|
||||
).getResultList();
|
||||
|
||||
}
|
||||
|
||||
List<Student> students = session.createQuery(
|
||||
"select student from Student student join student.teacher t where size(student.teacher.skills) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3L, students.size() );
|
||||
extractFromSession( session ).assertNumberOfJoins( 0, 1 );
|
||||
|
||||
students = session.createQuery(
|
||||
"select student from Student student join student.teacher t where size(student.teacher.skills) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2L, students.size() );
|
||||
|
||||
students = session.createQuery(
|
||||
"select student from Student student join student.teacher t where size(student.teacher.skills) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 1L, students.size() );
|
||||
|
||||
students = session.createQuery(
|
||||
"select student from Student student join student.teacher t where size(student.teacher.skills) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0L, students.size() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailureExpected( reason = "org.hibernate.PropertyAccessException : Student#teacher (I think its a stack-overflow)" )
|
||||
public void testSizeWithNestedPathAddFetchDistinct(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
List<Student> students = session.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tjoin left join fetch tjoin.skills where size(student.teacher.skills) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
// NOTE: An INNER JOIN is done on Teacher twice, which results in 4 joins.
|
||||
// A possible optimization would be to reuse this INNER JOIN.
|
||||
extractFromSession( session ).assertNumberOfJoins( 0, 4 );
|
||||
assertEquals( 3L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getSkills() ) );
|
||||
|
||||
students = session.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tjoin left join fetch tjoin.skills where size(student.teacher.skills) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getSkills() ) );
|
||||
|
||||
students = session.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tjoin left join fetch tjoin.skills where size(student.teacher.skills) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 1L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||
|
||||
students = session.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tjoin left join fetch tjoin.skills where size(student.teacher.skills) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0L, students.size() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
// @FailureExpected( reason = "org.hibernate.PropertyAccessException : Student#teacher (I think its a stack-overflow)" )
|
||||
public void testSizeWithNestedPath2(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
List<Student> students = session.createQuery(
|
||||
"select student from Student student join student.teacher t where size(t.skills) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3, students.size() );
|
||||
extractFromSession( session ).assertNumberOfJoins( 0, 1 );
|
||||
|
||||
students = session.createQuery(
|
||||
"select student from Student student join student.teacher t where size(t.skills) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2, students.size() );
|
||||
|
||||
students = session.createQuery(
|
||||
"select student from Student student join student.teacher t where size(t.skills) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 1, students.size() );
|
||||
|
||||
students = session.createQuery(
|
||||
"select student from Student student join student.teacher t where size(t.skills) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0, students.size() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailureExpected( reason = "org.hibernate.PropertyAccessException : Student#teacher (I think its a stack-overflow)" )
|
||||
public void testSizeWithNestedPath2AddFetchDistinct(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
List<Student> students = session.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch left join fetch tfetch.skills where size(t.skills) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
// NOTE: An INNER JOIN is done on Teacher twice, which results in 4 joins.
|
||||
// A possible optimization would be to reuse this INNER JOIN.
|
||||
extractFromSession( session ).assertNumberOfJoins( 0, 4 );
|
||||
assertEquals( 3L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getSkills() ) );
|
||||
|
||||
students = session.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch left join fetch tfetch.skills where size(t.skills) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getSkills() ) );
|
||||
|
||||
students = session.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch left join fetch tfetch.skills where size(t.skills) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 1L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||
|
||||
students = session.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch left join fetch tfetch.skills where size(t.skills) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0L, students.size() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void prepareTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
Skill mathSkill = new Skill();
|
||||
Skill frenchSkill = new Skill();
|
||||
|
||||
session.persist( mathSkill );
|
||||
session.persist( frenchSkill );
|
||||
|
||||
Teacher teacherWithNoSkills = new Teacher();
|
||||
|
||||
Teacher teacherWithOneSkill = new Teacher();
|
||||
teacherWithOneSkill.addSkill( mathSkill );
|
||||
|
||||
Teacher teacherWithTwoSkills = new Teacher();
|
||||
teacherWithTwoSkills.addSkill( mathSkill );
|
||||
teacherWithTwoSkills.addSkill( frenchSkill );
|
||||
|
||||
session.persist( teacherWithNoSkills );
|
||||
session.persist( teacherWithOneSkill );
|
||||
session.persist( teacherWithTwoSkills );
|
||||
|
||||
Student studentWithTeacherWithNoSkills = new Student();
|
||||
studentWithTeacherWithNoSkills.setTeacher( teacherWithNoSkills );
|
||||
session.persist( studentWithTeacherWithNoSkills );
|
||||
|
||||
Student studentWithTeacherWithOneSkill = new Student();
|
||||
studentWithTeacherWithOneSkill.setTeacher( teacherWithOneSkill );
|
||||
session.persist( studentWithTeacherWithOneSkill );
|
||||
|
||||
Student studentWithTeacherWithTwoSkills = new Student();
|
||||
studentWithTeacherWithTwoSkills.setTeacher( teacherWithTwoSkills );
|
||||
session.persist( studentWithTeacherWithTwoSkills );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void dropTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
session.createQuery( "delete from Student" ).executeUpdate();
|
||||
session.createQuery( "delete from Teacher" ).executeUpdate();
|
||||
session.createQuery( "delete from Skill" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
|
||||
package org.hibernate.test.hql.size;
|
||||
package org.hibernate.orm.test.query.hql.size;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -19,28 +19,30 @@ import javax.persistence.OneToMany;
|
|||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.dialect.DerbyDialect;
|
||||
|
||||
import org.hibernate.testing.SkipForDialect;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.SkipForDialect;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||
|
||||
@TestForIssue( jiraKey = "HHH-13619" )
|
||||
public class OneToManySizeTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
@DomainModel( annotatedClasses = { OneToManySizeTest.Company.class, OneToManySizeTest.Customer.class } )
|
||||
@SessionFactory
|
||||
public class OneToManySizeTest {
|
||||
|
||||
@Test
|
||||
@SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent")
|
||||
public void testSizeAsSelectExpression() {
|
||||
doInHibernate(
|
||||
this::sessionFactory,
|
||||
session -> {
|
||||
@SkipForDialect( dialectClass = DerbyDialect.class, reason = "Derby doesn't see that the subquery is functionally dependent" )
|
||||
public void testSizeAsSelectExpression(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final List results = session.createQuery(
|
||||
"select new org.hibernate.test.hql.size.OneToManySizeTest$CompanyDto(" +
|
||||
"select new org.hibernate.orm.test.query.hql.size.OneToManySizeTest$CompanyDto(" +
|
||||
" c.id, c.name, size( c.customers ) )" +
|
||||
" from Company c" +
|
||||
" group by c.id, c.name" +
|
||||
|
@ -64,13 +66,12 @@ public class OneToManySizeTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent")
|
||||
public void testSizeAsSelectExpressionWithLeftJoin() {
|
||||
doInHibernate(
|
||||
this::sessionFactory,
|
||||
session -> {
|
||||
@SkipForDialect( dialectClass = DerbyDialect.class, reason = "Derby doesn't see that the subquery is functionally dependent" )
|
||||
public void testSizeAsSelectExpressionWithLeftJoin(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final List results = session.createQuery(
|
||||
"select new org.hibernate.test.hql.size.OneToManySizeTest$CompanyDto(" +
|
||||
"select new org.hibernate.orm.test.query.hql.size.OneToManySizeTest$CompanyDto(" +
|
||||
" c.id, c.name, size( c.customers ) )" +
|
||||
" from Company c left join c.customers cu" +
|
||||
" group by c.id, c.name" +
|
||||
|
@ -94,13 +95,12 @@ public class OneToManySizeTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent")
|
||||
public void testSizeAsSelectExpressionWithInnerJoin() {
|
||||
doInHibernate(
|
||||
this::sessionFactory,
|
||||
session -> {
|
||||
@SkipForDialect( dialectClass = DerbyDialect.class, reason = "Derby doesn't see that the subquery is functionally dependent" )
|
||||
public void testSizeAsSelectExpressionWithInnerJoin(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final List results = session.createQuery(
|
||||
"select new org.hibernate.test.hql.size.OneToManySizeTest$CompanyDto(" +
|
||||
"select new org.hibernate.orm.test.query.hql.size.OneToManySizeTest$CompanyDto(" +
|
||||
" c.id, c.name, size( c.customers ) )" +
|
||||
" from Company c inner join c.customers cu" +
|
||||
" group by c.id, c.name" +
|
||||
|
@ -120,13 +120,12 @@ public class OneToManySizeTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent")
|
||||
public void testSizeAsSelectExpressionOfAliasWithInnerJoin() {
|
||||
doInHibernate(
|
||||
this::sessionFactory,
|
||||
session -> {
|
||||
@SkipForDialect( dialectClass = DerbyDialect.class, reason = "Derby doesn't see that the subquery is functionally dependent" )
|
||||
public void testSizeAsSelectExpressionOfAliasWithInnerJoin(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final List results = session.createQuery(
|
||||
"select new org.hibernate.test.hql.size.OneToManySizeTest$CompanyDto(" +
|
||||
"select new org.hibernate.orm.test.query.hql.size.OneToManySizeTest$CompanyDto(" +
|
||||
" c.id, c.name, size( cu ) )" +
|
||||
" from Company c inner join c.customers cu" +
|
||||
" group by c.id, c.name" +
|
||||
|
@ -146,13 +145,12 @@ public class OneToManySizeTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent")
|
||||
public void testSizeAsSelectExpressionExcludeEmptyCollection() {
|
||||
doInHibernate(
|
||||
this::sessionFactory,
|
||||
session -> {
|
||||
@SkipForDialect( dialectClass = DerbyDialect.class, reason = "Derby doesn't see that the subquery is functionally dependent" )
|
||||
public void testSizeAsSelectExpressionExcludeEmptyCollection(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final List results = session.createQuery(
|
||||
"select new org.hibernate.test.hql.size.OneToManySizeTest$CompanyDto(" +
|
||||
"select new org.hibernate.orm.test.query.hql.size.OneToManySizeTest$CompanyDto(" +
|
||||
" c.id, c.name, size( c.customers ) )" +
|
||||
" from Company c" +
|
||||
" where c.id != 0" +
|
||||
|
@ -172,10 +170,9 @@ public class OneToManySizeTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSizeAsConditionalExpressionExcludeEmptyCollection() {
|
||||
doInHibernate(
|
||||
this::sessionFactory,
|
||||
session -> {
|
||||
public void testSizeAsConditionalExpressionExcludeEmptyCollection(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final List<Company> results = session.createQuery(
|
||||
"from Company c" +
|
||||
" where size( c.customers ) > 0" +
|
||||
|
@ -198,10 +195,9 @@ public class OneToManySizeTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSizeAsConditionalExpressionIncludeEmptyCollection() {
|
||||
doInHibernate(
|
||||
this::sessionFactory,
|
||||
session -> {
|
||||
public void testSizeAsConditionalExpressionIncludeEmptyCollection(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final List<Company> results = session.createQuery(
|
||||
"from Company c" +
|
||||
" where size( c.customers ) > -1" +
|
||||
|
@ -228,12 +224,10 @@ public class OneToManySizeTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void initDataBase(){
|
||||
|
||||
doInHibernate(
|
||||
this::sessionFactory,
|
||||
session -> {
|
||||
@BeforeEach
|
||||
public void createTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
// Add a company with no customers
|
||||
final Company companyWithNoCustomers = new Company( 0 );
|
||||
companyWithNoCustomers.name = "Company 0";
|
||||
|
@ -255,11 +249,10 @@ public class OneToManySizeTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanupDatabase() {
|
||||
doInHibernate(
|
||||
this::sessionFactory,
|
||||
session -> {
|
||||
@AfterEach
|
||||
public void dropTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
for ( Company company : session.createQuery( "from Company", Company.class ).list() ) {
|
||||
session.delete( company );
|
||||
}
|
||||
|
@ -267,11 +260,6 @@ public class OneToManySizeTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class[] getAnnotatedClasses() {
|
||||
return new Class[] { Company.class, Customer.class };
|
||||
}
|
||||
|
||||
@Entity(name ="Company")
|
||||
public static class Company {
|
||||
|
|
@ -0,0 +1,613 @@
|
|||
/*
|
||||
* 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.orm.test.query.hql.size;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.jdbc.SQLStatementInspector;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hibernate.testing.jdbc.SQLStatementInspector.extractFromSession;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@TestForIssue(jiraKey = "HHH-13944")
|
||||
@ServiceRegistry(
|
||||
settings = @Setting( name = AvailableSettings.STATEMENT_INSPECTOR, value = "org.hibernate.testing.jdbc.SQLStatementInspector" )
|
||||
)
|
||||
@DomainModel(
|
||||
annotatedClasses = { Skill.class, Teacher.class, Student.class }
|
||||
)
|
||||
@SessionFactory
|
||||
public class OneToManySizeTest2 {
|
||||
@Test
|
||||
public void testSize(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
List<Teacher> teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher join teacher.students students where size(students) > 2",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 0, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher join teacher.students students where size(students) > 1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 1, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher join teacher.students students where size(students) > 0",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher join teacher.students students where size(students) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
|
||||
// Using "left join" includes the teacher with no students in the results.
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join teacher.students students where size(students) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 3, teachers.size() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeAddFetch(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
List<Teacher> teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.students join teacher.students students where size(students) > 2",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 0, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.students join teacher.students students where size(students) > 1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 1, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getStudents() ) );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.students join teacher.students students where size(students) > 0",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getStudents() ) );
|
||||
|
||||
// Using "join" (instead of "left join") removes the teacher with no students from the results.
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.students join teacher.students students where size(students) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getStudents() ) );
|
||||
|
||||
// Using "left join" includes the teacher with no students in the results.
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.students left join teacher.students students where size(students) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 3, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 2 ).getStudents() ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithoutNestedPath(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
List<Teacher> teachers = session.createQuery(
|
||||
"select teacher from Teacher teacher where size(teacher.students) > 2",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 0, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select teacher from Teacher teacher where size(teacher.students) > 1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 1, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select teacher from Teacher teacher where size(teacher.students) > 0",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
|
||||
// If there is no "join", then results include the teacher with no students
|
||||
teachers = session.createQuery(
|
||||
"select teacher from Teacher teacher where size(teacher.students) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 3, teachers.size() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithoutNestedPathAddFetchDistinct(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final SQLStatementInspector statementInspector = extractFromSession( session );
|
||||
statementInspector.clear();
|
||||
|
||||
List<Teacher> teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.students where size(teacher.students) > 2",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( countNumberOfJoins( statementInspector.getSqlQueries().get( 0 ) ), 1 );
|
||||
assertEquals( 0, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.students where size(teacher.students) > 1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 1, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getStudents() ) );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.students where size(teacher.students) > 0",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getStudents() ) );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.students where size(teacher.students) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 3, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 2 ).getStudents() ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithNestedPathAndImplicitJoin(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final SQLStatementInspector statementInspector = extractFromSession( session );
|
||||
statementInspector.clear();
|
||||
|
||||
List<Student> students = session.createQuery(
|
||||
"select student from Student student where size(student.teacher.students) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0, students.size() );
|
||||
assertEquals( countNumberOfJoins( statementInspector.getSqlQueries().get( 0 ) ), 1 );
|
||||
|
||||
students = session.createQuery(
|
||||
"select student from Student student where size(student.teacher.students) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2, students.size() );
|
||||
|
||||
students = session.createQuery(
|
||||
"select student from Student student where size(student.teacher.students) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3, students.size() );
|
||||
|
||||
students = session.createQuery(
|
||||
"select student from Student student where size(student.teacher.students) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3, students.size() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCollectionFetchBaseline(SessionFactoryScope scope) {
|
||||
// tests various "normal" ways to use a fetch
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
|
||||
final List list = session.createQuery( "from Teacher t join fetch t.students" ).list();
|
||||
assertThat( list.size(), is( 2 ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSizeWithNestedPathAndImplicitJoinAddFetchDistinct(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final SQLStatementInspector statementInspector = extractFromSession( session );
|
||||
statementInspector.clear();
|
||||
|
||||
List<Student> students = session.createQuery(
|
||||
"select distinct student " +
|
||||
"from Student student " +
|
||||
" left join fetch student.teacher t " +
|
||||
" left join fetch t.students " +
|
||||
"where size(student.teacher.students) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3, countNumberOfJoins( statementInspector.getSqlQueries().get( 0 ) ) );
|
||||
assertEquals( 0, students.size() );
|
||||
|
||||
students = session.createQuery(
|
||||
"select distinct student " +
|
||||
"from Student student " +
|
||||
" left join fetch student.teacher t " +
|
||||
" left join fetch t.students " +
|
||||
"where size(student.teacher.students) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||
|
||||
students = session.createQuery(
|
||||
"select distinct student " +
|
||||
"from Student student " +
|
||||
" left join fetch student.teacher t " +
|
||||
" left join fetch t.students " +
|
||||
"where size(student.teacher.students) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getStudents() ) );
|
||||
|
||||
students = session.createQuery(
|
||||
"select distinct student " +
|
||||
"from Student student " +
|
||||
" left join fetch student.teacher t " +
|
||||
" left join fetch t.students " +
|
||||
"where size(student.teacher.students) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getStudents() ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithNestedPathBase(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final SQLStatementInspector statementInterceptor = extractFromSession( session );
|
||||
statementInterceptor.clear();
|
||||
|
||||
List<Student> students = null;
|
||||
|
||||
students = session.createQuery(
|
||||
"select student from Student student join student.teacher t",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3L, students.size() );
|
||||
assertEquals( 1, countNumberOfJoins( statementInterceptor.getSqlQueries().get( 0 ) ) );
|
||||
|
||||
statementInterceptor.clear();
|
||||
|
||||
students = session.createQuery(
|
||||
"select student from Student student join student.teacher t where size( t.students ) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 1, countNumberOfJoins( statementInterceptor.getSqlQueries().get( 0 ) ) );
|
||||
|
||||
statementInterceptor.clear();
|
||||
|
||||
students = session.createQuery(
|
||||
"select student from Student student join student.teacher where size( student.teacher.students ) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
statementInterceptor.assertNumberOfJoins( 0, 2 );
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithNestedPath(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final SQLStatementInspector statementInspector = extractFromSession( session );
|
||||
statementInspector.clear();
|
||||
|
||||
List<Student> students = session.createQuery(
|
||||
"select student from Student student join student.teacher t where size(student.teacher.students) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3L, students.size() );
|
||||
assertEquals( 2, countNumberOfJoins( statementInspector.getSqlQueries().get( 0 ) ) );
|
||||
|
||||
students = session.createQuery(
|
||||
"select student from Student student join student.teacher t where size(student.teacher.students) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3L, students.size() );
|
||||
|
||||
students = session.createQuery(
|
||||
"select student from Student student join student.teacher t where size(student.teacher.students) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2L, students.size() );
|
||||
|
||||
students = session.createQuery(
|
||||
"select student from Student student join student.teacher t where size(student.teacher.students) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0L, students.size() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithNestedPathAddFetchDistinctBase(SessionFactoryScope scope) {
|
||||
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final SQLStatementInspector statementInspector = extractFromSession( session );
|
||||
statementInspector.clear();
|
||||
|
||||
{
|
||||
List<Student> students = session.createQuery(
|
||||
"select distinct student " +
|
||||
"from Student student " +
|
||||
" join student.teacher t " +
|
||||
" join fetch student.teacher tjoin " +
|
||||
" left join fetch tjoin.students " +
|
||||
"where size(tjoin.students) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3, countNumberOfJoins( statementInspector.getSqlQueries().get( 0 ) ) );
|
||||
assertEquals( 3L, students.size() );
|
||||
}
|
||||
|
||||
{
|
||||
List<Student> students = session.createQuery(
|
||||
"select distinct student " +
|
||||
"from Student student " +
|
||||
" join student.teacher t " +
|
||||
" join fetch student.teacher tjoin " +
|
||||
" left join fetch tjoin.students " +
|
||||
"where size(t.students) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3, countNumberOfJoins( statementInspector.getSqlQueries().get( 0 ) ) );
|
||||
assertEquals( 3L, students.size() );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithNestedPathAddFetchDistinct(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final SQLStatementInspector statementInspector = extractFromSession( session );
|
||||
statementInspector.clear();
|
||||
|
||||
List<Student> students;
|
||||
students = session.createQuery(
|
||||
"select distinct student " +
|
||||
"from Student student " +
|
||||
" join student.teacher t " +
|
||||
" join fetch student.teacher tjoin " +
|
||||
" left join fetch tjoin.students " +
|
||||
"where size(student.teacher.students) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 4, countNumberOfJoins( statementInspector.getSqlQueries().get( 0 ) ) );
|
||||
assertEquals( 3L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getStudents() ) );
|
||||
|
||||
students = session.createQuery(
|
||||
"select distinct student " +
|
||||
"from Student student " +
|
||||
" join student.teacher t " +
|
||||
" join fetch student.teacher tjoin " +
|
||||
" left join fetch tjoin.students " +
|
||||
"where size(student.teacher.students) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 4, countNumberOfJoins( statementInspector.getSqlQueries().get( 0 ) ) );
|
||||
assertEquals( 3L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getStudents() ) );
|
||||
|
||||
students = session.createQuery(
|
||||
"select distinct student " +
|
||||
"from Student student " +
|
||||
" join student.teacher t " +
|
||||
" join fetch student.teacher tjoin " +
|
||||
" left join fetch tjoin.students " +
|
||||
"where size(student.teacher.students) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 4, countNumberOfJoins( statementInspector.getSqlQueries().get( 0 ) ) );
|
||||
assertEquals( 2L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||
|
||||
students = session.createQuery(
|
||||
"select distinct student " +
|
||||
"from Student student " +
|
||||
" join student.teacher t " +
|
||||
" join fetch student.teacher tjoin " +
|
||||
" left join fetch tjoin.students " +
|
||||
"where size(student.teacher.students) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0L, students.size() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithNestedPath2(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final SQLStatementInspector statementInspector = extractFromSession( session );
|
||||
statementInspector.clear();
|
||||
|
||||
List<Student> students = session.createQuery(
|
||||
"select student from Student student join student.teacher t where size(t.students) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3L, students.size() );
|
||||
assertEquals( countNumberOfJoins( statementInspector.getSqlQueries().get( 0 ) ), 1 );
|
||||
|
||||
students = session.createQuery(
|
||||
"select student from Student student join student.teacher t where size(t.students) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3L, students.size() );
|
||||
|
||||
students = session.createQuery(
|
||||
"select student from Student student join student.teacher t where size(t.students) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2L, students.size() );
|
||||
|
||||
students = session.createQuery(
|
||||
"select student from Student student join student.teacher t where size(t.students) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0L, students.size() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithNestedPath2AddFetchDistinct(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final SQLStatementInspector statementInspector = extractFromSession( session );
|
||||
statementInspector.clear();
|
||||
|
||||
List<Student> students = session.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch join fetch tfetch.students where size(t.students) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
// NOTE: An INNER JOIN is done on Teacher twice, which results in 4 joins.
|
||||
// A possible optimization would be to reuse this INNER JOIN.
|
||||
assertEquals( countNumberOfJoins( statementInspector.getSqlQueries().get( 0 ) ), 3 );
|
||||
assertEquals( 3L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getStudents() ) );
|
||||
|
||||
students = session.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch join fetch tfetch.students where size(t.students) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getStudents() ) );
|
||||
|
||||
students = session.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch join fetch tfetch.students where size(t.students) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||
|
||||
students = session.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch join fetch tfetch.students where size(t.students) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0L, students.size() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void prepareTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
Student mathStudent = new Student();
|
||||
Student frenchStudent = new Student();
|
||||
Student scienceStudent = new Student();
|
||||
|
||||
Teacher teacherWithNoStudents = new Teacher();
|
||||
Teacher teacherWithOneStudent = new Teacher();
|
||||
Teacher teacherWithTwoStudents = new Teacher();
|
||||
|
||||
session.persist( teacherWithNoStudents );
|
||||
session.persist( teacherWithOneStudent );
|
||||
session.persist( teacherWithTwoStudents );
|
||||
|
||||
mathStudent.setTeacher( teacherWithOneStudent );
|
||||
teacherWithOneStudent.addStudent( mathStudent );
|
||||
|
||||
frenchStudent.setTeacher( teacherWithTwoStudents );
|
||||
teacherWithTwoStudents.addStudent( frenchStudent );
|
||||
|
||||
scienceStudent.setTeacher( teacherWithTwoStudents );
|
||||
teacherWithTwoStudents.addStudent( scienceStudent );
|
||||
|
||||
session.persist( mathStudent );
|
||||
session.persist( frenchStudent );
|
||||
session.persist( scienceStudent );
|
||||
|
||||
final SQLStatementInspector statementInspector = extractFromSession( session );
|
||||
statementInspector.clear();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void dropTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
session.createQuery( "delete from Student" ).executeUpdate();
|
||||
session.createQuery( "delete from Teacher" ).executeUpdate();
|
||||
session.createQuery( "delete from Skill" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private static int countNumberOfJoins(String query) {
|
||||
String fromPart = query.toLowerCase( Locale.ROOT ).split( " from " )[1].split( " where " )[0];
|
||||
return fromPart.split( "(\\sjoin\\s|,\\s)", -1 ).length - 1;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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.orm.test.query.hql.size;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToMany;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Entity
|
||||
public class Skill {
|
||||
|
||||
private Integer id;
|
||||
|
||||
private Set<Teacher> teachers = new HashSet<>();
|
||||
|
||||
@Id
|
||||
@Column(name = "skill_id")
|
||||
@GeneratedValue
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@ManyToMany(mappedBy = "skills")
|
||||
public Set<Teacher> getTeachers() {
|
||||
return teachers;
|
||||
}
|
||||
|
||||
public void setTeachers(Set<Teacher> teachers) {
|
||||
this.teachers = teachers;
|
||||
}
|
||||
|
||||
public void addTeacher(Teacher teacher) {
|
||||
teachers.add( teacher );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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.orm.test.query.hql.size;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Entity
|
||||
public class Student {
|
||||
|
||||
private Integer id;
|
||||
|
||||
private Teacher teacher;
|
||||
|
||||
@Id
|
||||
@Column(name = "student_id")
|
||||
@GeneratedValue
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumn(name = "teacher_fk_id")
|
||||
public Teacher getTeacher() {
|
||||
return teacher;
|
||||
}
|
||||
|
||||
public void setTeacher(Teacher teacher) {
|
||||
this.teacher = teacher;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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.orm.test.query.hql.size;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.OneToMany;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Entity
|
||||
public class Teacher {
|
||||
|
||||
private Integer id;
|
||||
|
||||
private Set<Student> students = new HashSet<>();
|
||||
|
||||
private Set<Skill> skills = new HashSet<>();
|
||||
|
||||
@Id
|
||||
@Column(name = "teacher_id")
|
||||
@GeneratedValue
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@OneToMany(mappedBy = "teacher")
|
||||
public Set<Student> getStudents() {
|
||||
return students;
|
||||
}
|
||||
|
||||
public void setStudents(Set<Student> students) {
|
||||
this.students = students;
|
||||
}
|
||||
|
||||
public void addStudent(Student student) {
|
||||
students.add( student );
|
||||
}
|
||||
|
||||
@ManyToMany
|
||||
public Set<Skill> getSkills() {
|
||||
return skills;
|
||||
}
|
||||
|
||||
public void addSkill(Skill skill) {
|
||||
skills.add( skill );
|
||||
skill.addTeacher( this );
|
||||
}
|
||||
|
||||
public void setSkills(Set<Skill> skills) {
|
||||
this.skills = skills;
|
||||
}
|
||||
}
|
|
@ -1,113 +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.test.hql;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.dialect.DerbyDialect;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class CastFunctionTest extends BaseCoreFunctionalTestCase {
|
||||
@Entity( name="MyEntity" )
|
||||
public static class MyEntity {
|
||||
@Id
|
||||
private Integer id;
|
||||
private String name;
|
||||
private Double theLostNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] { MyEntity.class };
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringCasting() {
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
|
||||
if ( getDialect() instanceof DerbyDialect ) {
|
||||
// the conversion from DOUBLE to VARCHAR is not supported by Derby,
|
||||
// using the short name
|
||||
s.createQuery( "select cast(char(e.theLostNumber) as string) from MyEntity e" ).list();
|
||||
// using the java class name
|
||||
s.createQuery( "select cast(char(e.theLostNumber) as java.lang.String) from MyEntity e" ).list();
|
||||
// using the fqn Hibernate Type name
|
||||
s.createQuery( "select cast(char(e.theLostNumber) as org.hibernate.type.StringType) from MyEntity e" )
|
||||
.list();
|
||||
}
|
||||
else {
|
||||
// using the short name
|
||||
s.createQuery( "select cast(e.theLostNumber as string) from MyEntity e" ).list();
|
||||
// using the java class name
|
||||
s.createQuery( "select cast(e.theLostNumber as java.lang.String) from MyEntity e" ).list();
|
||||
// using the fqn Hibernate Type name
|
||||
s.createQuery( "select cast(e.theLostNumber as org.hibernate.type.StringType) from MyEntity e" )
|
||||
.list();
|
||||
}
|
||||
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntegerCasting() {
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
|
||||
// using the short name
|
||||
s.createQuery( "select cast(e.theLostNumber as integer) from MyEntity e" ).list();
|
||||
// using the java class name (primitive)
|
||||
s.createQuery( "select cast(e.theLostNumber as int) from MyEntity e" ).list();
|
||||
// using the java class name
|
||||
s.createQuery( "select cast(e.theLostNumber as java.lang.Integer) from MyEntity e" ).list();
|
||||
// using the fqn Hibernate Type name
|
||||
s.createQuery( "select cast(e.theLostNumber as org.hibernate.type.IntegerType) from MyEntity e" ).list();
|
||||
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLongCasting() {
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
|
||||
// using the short name (also the primitive name)
|
||||
s.createQuery( "select cast(e.theLostNumber as long) from MyEntity e" ).list();
|
||||
// using the java class name
|
||||
s.createQuery( "select cast(e.theLostNumber as java.lang.Long) from MyEntity e" ).list();
|
||||
// using the fqn Hibernate Type name
|
||||
s.createQuery( "select cast(e.theLostNumber as org.hibernate.type.LongType) from MyEntity e" ).list();
|
||||
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFloatCasting() {
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
|
||||
// using the short name (also the primitive name)
|
||||
s.createQuery( "select cast(e.theLostNumber as float) from MyEntity e" ).list();
|
||||
// using the java class name
|
||||
s.createQuery( "select cast(e.theLostNumber as java.lang.Float) from MyEntity e" ).list();
|
||||
// using the fqn Hibernate Type name
|
||||
s.createQuery( "select cast(e.theLostNumber as org.hibernate.type.FloatType) from MyEntity e" ).list();
|
||||
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
}
|
|
@ -1,563 +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.test.hql.size;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.boot.SessionFactoryBuilder;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@TestForIssue(jiraKey = "HHH-13944")
|
||||
public class ManyToManySizeTest2 extends BaseNonConfigCoreFunctionalTestCase {
|
||||
|
||||
private SQLStatementInterceptor sqlStatementInterceptor;
|
||||
|
||||
@Override
|
||||
protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) {
|
||||
sqlStatementInterceptor = new SQLStatementInterceptor( sfb );
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
inTransaction( session -> {
|
||||
Skill mathSkill = new Skill();
|
||||
Skill frenchSkill = new Skill();
|
||||
|
||||
session.persist( mathSkill );
|
||||
session.persist( frenchSkill );
|
||||
|
||||
Teacher teacherWithNoSkills = new Teacher();
|
||||
|
||||
Teacher teacherWithOneSkill = new Teacher();
|
||||
teacherWithOneSkill.addSkill( mathSkill );
|
||||
|
||||
Teacher teacherWithTwoSkills = new Teacher();
|
||||
teacherWithTwoSkills.addSkill( mathSkill );
|
||||
teacherWithTwoSkills.addSkill( frenchSkill );
|
||||
|
||||
session.persist( teacherWithNoSkills );
|
||||
session.persist( teacherWithOneSkill );
|
||||
session.persist( teacherWithTwoSkills );
|
||||
|
||||
Student studentWithTeacherWithNoSkills = new Student();
|
||||
studentWithTeacherWithNoSkills.setTeacher( teacherWithNoSkills );
|
||||
session.persist( studentWithTeacherWithNoSkills );
|
||||
|
||||
Student studentWithTeacherWithOneSkill = new Student();
|
||||
studentWithTeacherWithOneSkill.setTeacher( teacherWithOneSkill );
|
||||
session.persist( studentWithTeacherWithOneSkill );
|
||||
|
||||
Student studentWithTeacherWithTwoSkills = new Student();
|
||||
studentWithTeacherWithTwoSkills.setTeacher( teacherWithTwoSkills );
|
||||
session.persist( studentWithTeacherWithTwoSkills );
|
||||
} );
|
||||
sqlStatementInterceptor.clear();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
inTransaction( session -> {
|
||||
session.createQuery( "delete from Student" ).executeUpdate();
|
||||
session.createQuery( "delete from Teacher" ).executeUpdate();
|
||||
session.createQuery( "delete from Skill" ).executeUpdate();
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSize() {
|
||||
inTransaction( session -> {
|
||||
List<Teacher> teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher join teacher.skills skills where size(skills) > 2",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 0, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher join teacher.skills skills where size(skills) > 1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 1, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher join teacher.skills skills where size(skills) > 0",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
|
||||
// Using "join" (instead of "left join") removes the teacher with no skills from the results.
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher join teacher.skills skills where size(skills) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
|
||||
// Using "left join" includes the teacher with no skills in the results.
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join teacher.skills skills where size(skills) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 3, teachers.size() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeAddFetch() {
|
||||
inTransaction( session -> {
|
||||
List<Teacher> teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.skills join teacher.skills skills where size(skills) > 2",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 0, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.skills join teacher.skills skills where size(skills) > 1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 1, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getSkills() ) );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.skills join teacher.skills skills where size(skills) > 0",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getSkills() ) );
|
||||
|
||||
|
||||
// Using "join" (instead of "left join") removes the teacher with no skills from the results.
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.skills join teacher.skills skills where size(skills) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getSkills() ) );
|
||||
|
||||
// Using "left join" includes the teacher with no skills in the results.
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.skills left join teacher.skills skills where size(skills) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 3, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 2 ).getSkills() ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithoutNestedPath() {
|
||||
inTransaction( session -> {
|
||||
List<Teacher> teachers = session.createQuery(
|
||||
"select teacher from Teacher teacher where size(teacher.skills) > 2",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 0, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select teacher from Teacher teacher where size(teacher.skills) > 1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 1, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select teacher from Teacher teacher where size(teacher.skills) > 0",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
|
||||
// If there is no "join", then results include the teacher with no skills
|
||||
teachers = session.createQuery(
|
||||
"select teacher from Teacher teacher where size(teacher.skills) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 3, teachers.size() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithoutNestedPathAddFetchDistinct() {
|
||||
inTransaction( session -> {
|
||||
List<Teacher> teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.skills where size(teacher.skills) > 2",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 2 );
|
||||
assertEquals( 0, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.skills where size(teacher.skills) > 1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 1, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getSkills() ) );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.skills where size(teacher.skills) > 0",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getSkills() ) );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.skills where size(teacher.skills) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 3, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 2 ).getSkills() ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithNestedPathAndImplicitJoin() {
|
||||
doInJPA( this::sessionFactory, em -> {
|
||||
List<Student> students = em.createQuery(
|
||||
"select student from Student student where size(student.teacher.skills) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0, students.size() );
|
||||
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 1 );
|
||||
|
||||
students = em.createQuery(
|
||||
"select student from Student student where size(student.teacher.skills) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 1, students.size() );
|
||||
|
||||
students = em.createQuery(
|
||||
"select student from Student student where size(student.teacher.skills) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2, students.size() );
|
||||
|
||||
// If there is no "join" in the query, then results include the student with the teacher with no skills
|
||||
students = em.createQuery(
|
||||
"select student from Student student where size(student.teacher.skills) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3, students.size() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithNestedPathAndImplicitJoinAddFetchDistinct() {
|
||||
doInJPA( this::sessionFactory, em -> {
|
||||
List<Student> students = em.createQuery(
|
||||
"select distinct student from Student student left join fetch student.teacher t left join fetch t.skills where size(student.teacher.skills) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0, students.size() );
|
||||
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 3 );
|
||||
|
||||
students = em.createQuery(
|
||||
"select distinct student from Student student left join fetch student.teacher t left join fetch t.skills where size(student.teacher.skills) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 1, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||
|
||||
students = em.createQuery(
|
||||
"select distinct student from Student student left join fetch student.teacher t left join fetch t.skills where size(student.teacher.skills) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getSkills() ) );
|
||||
|
||||
students = em.createQuery(
|
||||
"select distinct student from Student student left join fetch student.teacher t left join fetch t.skills where size(student.teacher.skills) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getSkills() ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithNestedPath() {
|
||||
doInJPA( this::sessionFactory, em -> {
|
||||
List<Student> students = em.createQuery(
|
||||
"select student from Student student join student.teacher t where size(student.teacher.skills) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3L, students.size() );
|
||||
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 1 );
|
||||
|
||||
students = em.createQuery(
|
||||
"select student from Student student join student.teacher t where size(student.teacher.skills) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2L, students.size() );
|
||||
|
||||
students = em.createQuery(
|
||||
"select student from Student student join student.teacher t where size(student.teacher.skills) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 1L, students.size() );
|
||||
|
||||
students = em.createQuery(
|
||||
"select student from Student student join student.teacher t where size(student.teacher.skills) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0L, students.size() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithNestedPathAddFetchDistinct() {
|
||||
doInJPA( this::sessionFactory, em -> {
|
||||
List<Student> students = em.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tjoin left join fetch tjoin.skills where size(student.teacher.skills) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
// NOTE: An INNER JOIN is done on Teacher twice, which results in 4 joins.
|
||||
// A possible optimization would be to reuse this INNER JOIN.
|
||||
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 4 );
|
||||
assertEquals( 3L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getSkills() ) );
|
||||
|
||||
students = em.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tjoin left join fetch tjoin.skills where size(student.teacher.skills) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getSkills() ) );
|
||||
|
||||
students = em.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tjoin left join fetch tjoin.skills where size(student.teacher.skills) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 1L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||
|
||||
students = em.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tjoin left join fetch tjoin.skills where size(student.teacher.skills) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0L, students.size() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithNestedPath2() {
|
||||
doInJPA( this::sessionFactory, em -> {
|
||||
List<Student> students = em.createQuery(
|
||||
"select student from Student student join student.teacher t where size(t.skills) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3L, students.size() );
|
||||
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 1 );
|
||||
|
||||
students = em.createQuery(
|
||||
"select student from Student student join student.teacher t where size(t.skills) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2L, students.size() );
|
||||
|
||||
students = em.createQuery(
|
||||
"select student from Student student join student.teacher t where size(t.skills) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 1L, students.size() );
|
||||
|
||||
students = em.createQuery(
|
||||
"select student from Student student join student.teacher t where size(t.skills) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0L, students.size() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithNestedPath2AddFetchDistinct() {
|
||||
doInJPA( this::sessionFactory, em -> {
|
||||
List<Student> students = em.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch left join fetch tfetch.skills where size(t.skills) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
// NOTE: An INNER JOIN is done on Teacher twice, which results in 4 joins.
|
||||
// A possible optimization would be to reuse this INNER JOIN.
|
||||
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 4 );
|
||||
assertEquals( 3L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getSkills() ) );
|
||||
|
||||
students = em.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch left join fetch tfetch.skills where size(t.skills) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getSkills() ) );
|
||||
|
||||
students = em.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch left join fetch tfetch.skills where size(t.skills) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 1L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||
|
||||
students = em.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch left join fetch tfetch.skills where size(t.skills) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0L, students.size() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] { Skill.class, Teacher.class, Student.class };
|
||||
}
|
||||
|
||||
@Entity(name = "Skill")
|
||||
public static class Skill {
|
||||
|
||||
private Integer id;
|
||||
|
||||
private Set<Teacher> teachers = new HashSet<>();
|
||||
|
||||
@Id
|
||||
@Column(name = "skill_id")
|
||||
@GeneratedValue
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@ManyToMany(mappedBy = "skills")
|
||||
public Set<Teacher> getTeachers() {
|
||||
return teachers;
|
||||
}
|
||||
|
||||
public void setTeachers(Set<Teacher> teachers) {
|
||||
this.teachers = teachers;
|
||||
}
|
||||
|
||||
public void addTeacher(Teacher teacher) {
|
||||
teachers.add( teacher );
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Teacher")
|
||||
public static class Teacher {
|
||||
|
||||
private Integer id;
|
||||
|
||||
private Set<Student> students = new HashSet<>();
|
||||
|
||||
private Set<Skill> skills = new HashSet<>();
|
||||
|
||||
@Id
|
||||
@Column(name = "teacher_id")
|
||||
@GeneratedValue
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@OneToMany(mappedBy = "teacher")
|
||||
public Set<Student> getStudents() {
|
||||
return students;
|
||||
}
|
||||
|
||||
public void setStudents(Set<Student> students) {
|
||||
this.students = students;
|
||||
}
|
||||
|
||||
public void addStudent(Student student) {
|
||||
students.add( student );
|
||||
}
|
||||
|
||||
@ManyToMany
|
||||
public Set<Skill> getSkills() {
|
||||
return skills;
|
||||
}
|
||||
|
||||
public void addSkill(Skill skill) {
|
||||
skills.add( skill );
|
||||
skill.addTeacher( this );
|
||||
}
|
||||
|
||||
public void setSkills(Set<Skill> skills) {
|
||||
this.skills = skills;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Student")
|
||||
public static class Student {
|
||||
|
||||
private Integer id;
|
||||
|
||||
private Teacher teacher;
|
||||
|
||||
@Id
|
||||
@Column(name = "student_id")
|
||||
@GeneratedValue
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumn(name = "teacher_fk_id")
|
||||
public Teacher getTeacher() {
|
||||
return teacher;
|
||||
}
|
||||
|
||||
public void setTeacher(Teacher teacher) {
|
||||
this.teacher = teacher;
|
||||
teacher.addStudent( this );
|
||||
}
|
||||
}
|
||||
|
||||
private static int countNumberOfJoins(String query) {
|
||||
String fromPart = query.toLowerCase( Locale.ROOT ).split( " from " )[1].split( " where " )[0];
|
||||
return fromPart.split( "(\\sjoin\\s|,\\s)", -1 ).length - 1;
|
||||
}
|
||||
}
|
|
@ -1,560 +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.test.hql.size;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.boot.SessionFactoryBuilder;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@TestForIssue(jiraKey = "HHH-13944")
|
||||
public class OneToManySizeTest2 extends BaseNonConfigCoreFunctionalTestCase {
|
||||
|
||||
private SQLStatementInterceptor sqlStatementInterceptor;
|
||||
|
||||
@Override
|
||||
protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) {
|
||||
sqlStatementInterceptor = new SQLStatementInterceptor( sfb );
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
inTransaction( session -> {
|
||||
Student mathStudent = new Student();
|
||||
Student frenchStudent = new Student();
|
||||
Student scienceStudent = new Student();
|
||||
|
||||
Teacher teacherWithNoStudents = new Teacher();
|
||||
Teacher teacherWithOneStudent = new Teacher();
|
||||
Teacher teacherWithTwoStudents = new Teacher();
|
||||
|
||||
session.persist( teacherWithNoStudents );
|
||||
session.persist( teacherWithOneStudent );
|
||||
session.persist( teacherWithTwoStudents );
|
||||
|
||||
mathStudent.setTeacher( teacherWithOneStudent );
|
||||
teacherWithOneStudent.addStudent( mathStudent );
|
||||
|
||||
frenchStudent.setTeacher( teacherWithTwoStudents );
|
||||
teacherWithTwoStudents.addStudent( frenchStudent );
|
||||
|
||||
scienceStudent.setTeacher( teacherWithTwoStudents );
|
||||
teacherWithTwoStudents.addStudent( scienceStudent );
|
||||
|
||||
session.persist( mathStudent );
|
||||
session.persist( frenchStudent );
|
||||
session.persist( scienceStudent );
|
||||
} );
|
||||
sqlStatementInterceptor.clear();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
inTransaction( session -> {
|
||||
session.createQuery( "delete from Student" ).executeUpdate();
|
||||
session.createQuery( "delete from Teacher" ).executeUpdate();
|
||||
session.createQuery( "delete from Skill" ).executeUpdate();
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSize() {
|
||||
inTransaction( session -> {
|
||||
List<Teacher> teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher join teacher.students students where size(students) > 2",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 0, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher join teacher.students students where size(students) > 1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 1, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher join teacher.students students where size(students) > 0",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher join teacher.students students where size(students) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
|
||||
// Using "left join" includes the teacher with no students in the results.
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join teacher.students students where size(students) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 3, teachers.size() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeAddFetch() {
|
||||
inTransaction( session -> {
|
||||
List<Teacher> teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.students join teacher.students students where size(students) > 2",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 0, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.students join teacher.students students where size(students) > 1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 1, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getStudents() ) );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.students join teacher.students students where size(students) > 0",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getStudents() ) );
|
||||
|
||||
|
||||
// Using "join" (instead of "left join") removes the teacher with no students from the results.
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.students join teacher.students students where size(students) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getStudents() ) );
|
||||
|
||||
// Using "left join" includes the teacher with no students in the results.
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.students left join teacher.students students where size(students) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 3, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 2 ).getStudents() ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithoutNestedPath() {
|
||||
inTransaction( session -> {
|
||||
List<Teacher> teachers = session.createQuery(
|
||||
"select teacher from Teacher teacher where size(teacher.students) > 2",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 0, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select teacher from Teacher teacher where size(teacher.students) > 1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 1, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select teacher from Teacher teacher where size(teacher.students) > 0",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
|
||||
// If there is no "join", then results include the teacher with no students
|
||||
teachers = session.createQuery(
|
||||
"select teacher from Teacher teacher where size(teacher.students) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 3, teachers.size() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithoutNestedPathAddFetchDistinct() {
|
||||
inTransaction( session -> {
|
||||
List<Teacher> teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.students where size(teacher.students) > 2",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 1 );
|
||||
assertEquals( 0, teachers.size() );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.students where size(teacher.students) > 1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 1, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getStudents() ) );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.students where size(teacher.students) > 0",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 2, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getStudents() ) );
|
||||
|
||||
teachers = session.createQuery(
|
||||
"select distinct teacher from Teacher teacher left join fetch teacher.students where size(teacher.students) > -1",
|
||||
Teacher.class
|
||||
).getResultList();
|
||||
assertEquals( 3, teachers.size() );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( teachers.get( 2 ).getStudents() ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithNestedPathAndImplicitJoin() {
|
||||
doInJPA( this::sessionFactory, em -> {
|
||||
List<Student> students = em.createQuery(
|
||||
"select student from Student student where size(student.teacher.students) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0, students.size() );
|
||||
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 1 );
|
||||
|
||||
students = em.createQuery(
|
||||
"select student from Student student where size(student.teacher.students) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2, students.size() );
|
||||
|
||||
students = em.createQuery(
|
||||
"select student from Student student where size(student.teacher.students) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3, students.size() );
|
||||
|
||||
students = em.createQuery(
|
||||
"select student from Student student where size(student.teacher.students) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3, students.size() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithNestedPathAndImplicitJoinAddFetchDistinct() {
|
||||
doInJPA( this::sessionFactory, em -> {
|
||||
List<Student> students = em.createQuery(
|
||||
"select distinct student from Student student left join fetch student.teacher t left join fetch t.students where size(student.teacher.students) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0, students.size() );
|
||||
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 2 );
|
||||
|
||||
students = em.createQuery(
|
||||
"select distinct student from Student student left join fetch student.teacher t left join fetch t.students where size(student.teacher.students) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||
|
||||
students = em.createQuery(
|
||||
"select distinct student from Student student left join fetch student.teacher t left join fetch t.students where size(student.teacher.students) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getStudents() ) );
|
||||
|
||||
students = em.createQuery(
|
||||
"select distinct student from Student student left join fetch student.teacher t left join fetch t.students where size(student.teacher.students) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getStudents() ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithNestedPath() {
|
||||
doInJPA( this::sessionFactory, em -> {
|
||||
List<Student> students = em.createQuery(
|
||||
"select student from Student student join student.teacher t where size(student.teacher.students) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3L, students.size() );
|
||||
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 1 );
|
||||
|
||||
students = em.createQuery(
|
||||
"select student from Student student join student.teacher t where size(student.teacher.students) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3L, students.size() );
|
||||
|
||||
students = em.createQuery(
|
||||
"select student from Student student join student.teacher t where size(student.teacher.students) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2L, students.size() );
|
||||
|
||||
students = em.createQuery(
|
||||
"select student from Student student join student.teacher t where size(student.teacher.students) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0L, students.size() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithNestedPathAddFetchDistinct() {
|
||||
doInJPA( this::sessionFactory, em -> {
|
||||
List<Student> students = em.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tjoin left join fetch tjoin.students where size(student.teacher.students) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
// NOTE: An INNER JOIN is done on Teacher twice, which results in 4 joins.
|
||||
// A possible optimization would be to reuse this INNER JOIN.
|
||||
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 3 );
|
||||
assertEquals( 3L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getStudents() ) );
|
||||
|
||||
students = em.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tjoin left join fetch tjoin.students where size(student.teacher.students) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getStudents() ) );
|
||||
|
||||
students = em.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tjoin left join fetch tjoin.students where size(student.teacher.students) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||
|
||||
students = em.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tjoin left join fetch tjoin.students where size(student.teacher.students) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0L, students.size() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithNestedPath2() {
|
||||
doInJPA( this::sessionFactory, em -> {
|
||||
List<Student> students = em.createQuery(
|
||||
"select student from Student student join student.teacher t where size(t.students) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3L, students.size() );
|
||||
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 1 );
|
||||
|
||||
students = em.createQuery(
|
||||
"select student from Student student join student.teacher t where size(t.students) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3L, students.size() );
|
||||
|
||||
students = em.createQuery(
|
||||
"select student from Student student join student.teacher t where size(t.students) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2L, students.size() );
|
||||
|
||||
students = em.createQuery(
|
||||
"select student from Student student join student.teacher t where size(t.students) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0L, students.size() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeWithNestedPath2AddFetchDistinct() {
|
||||
doInJPA( this::sessionFactory, em -> {
|
||||
List<Student> students = em.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch join fetch tfetch.students where size(t.students) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
// NOTE: An INNER JOIN is done on Teacher twice, which results in 4 joins.
|
||||
// A possible optimization would be to reuse this INNER JOIN.
|
||||
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 3 );
|
||||
assertEquals( 3L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getStudents() ) );
|
||||
|
||||
students = em.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch join fetch tfetch.students where size(t.students) > 0",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 3L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getStudents() ) );
|
||||
|
||||
students = em.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch join fetch tfetch.students where size(t.students) > 1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 2L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||
|
||||
students = em.createQuery(
|
||||
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch join fetch tfetch.students where size(t.students) > 2",
|
||||
Student.class
|
||||
).getResultList();
|
||||
assertEquals( 0L, students.size() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] { Skill.class, Teacher.class, Student.class };
|
||||
}
|
||||
|
||||
@Entity(name = "Skill")
|
||||
public static class Skill {
|
||||
|
||||
private Integer id;
|
||||
|
||||
private Set<Teacher> teachers = new HashSet<>();
|
||||
|
||||
@Id
|
||||
@Column(name = "skill_id")
|
||||
@GeneratedValue
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@ManyToMany(mappedBy = "skills")
|
||||
public Set<Teacher> getTeachers() {
|
||||
return teachers;
|
||||
}
|
||||
|
||||
public void setTeachers(Set<Teacher> teachers) {
|
||||
this.teachers = teachers;
|
||||
}
|
||||
|
||||
public void addTeacher(Teacher teacher) {
|
||||
teachers.add( teacher );
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Teacher")
|
||||
public static class Teacher {
|
||||
|
||||
private Integer id;
|
||||
|
||||
private Set<Student> students = new HashSet<>();
|
||||
|
||||
private Set<Skill> skills = new HashSet<>();
|
||||
|
||||
@Id
|
||||
@Column(name = "teacher_id")
|
||||
@GeneratedValue
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@OneToMany(mappedBy = "teacher")
|
||||
public Set<Student> getStudents() {
|
||||
return students;
|
||||
}
|
||||
|
||||
public void setStudents(Set<Student> students) {
|
||||
this.students = students;
|
||||
}
|
||||
|
||||
public void addStudent(Student student) {
|
||||
students.add( student );
|
||||
}
|
||||
|
||||
@ManyToMany
|
||||
public Set<Skill> getSkills() {
|
||||
return skills;
|
||||
}
|
||||
|
||||
public void addSkill(Skill skill) {
|
||||
skills.add( skill );
|
||||
skill.addTeacher( this );
|
||||
}
|
||||
|
||||
public void setSkills(Set<Skill> skills) {
|
||||
this.skills = skills;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Student")
|
||||
public static class Student {
|
||||
|
||||
private Integer id;
|
||||
|
||||
private Teacher teacher;
|
||||
|
||||
@Id
|
||||
@Column(name = "student_id")
|
||||
@GeneratedValue
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumn(name = "teacher_fk_id")
|
||||
public Teacher getTeacher() {
|
||||
return teacher;
|
||||
}
|
||||
|
||||
public void setTeacher(Teacher teacher) {
|
||||
this.teacher = teacher;
|
||||
}
|
||||
}
|
||||
|
||||
private static int countNumberOfJoins(String query) {
|
||||
String fromPart = query.toLowerCase( Locale.ROOT ).split( " from " )[1].split( " where " )[0];
|
||||
return fromPart.split( "(\\sjoin\\s|,\\s)", -1 ).length - 1;
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import java.util.LinkedList;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
|
@ -40,10 +41,20 @@ public class SQLStatementInspector implements StatementInspector {
|
|||
sqlQueries.clear();
|
||||
}
|
||||
|
||||
public int getNumberOfJoins(int position) {
|
||||
final String sql = sqlQueries.get( position );
|
||||
String fromPart = sql.toLowerCase( Locale.ROOT ).split( " from " )[1].split( " where " )[0];
|
||||
return fromPart.split( "(\\sjoin\\s|,\\s)", -1 ).length - 1;
|
||||
}
|
||||
|
||||
public void assertExecuted(String expected) {
|
||||
assertTrue( sqlQueries.contains( expected ) );
|
||||
}
|
||||
|
||||
public void assertNumberOfJoins(int queryNumber, int expectedNumberOfJoins) {
|
||||
assertNumberOfOccurrenceInQuery( queryNumber, "join", expectedNumberOfJoins );
|
||||
}
|
||||
|
||||
public void assertExecutedCount(int expected) {
|
||||
assertEquals( "Number of executed statements ",expected, sqlQueries.size() );
|
||||
}
|
||||
|
@ -51,7 +62,7 @@ public class SQLStatementInspector implements StatementInspector {
|
|||
public void assertNumberOfOccurrenceInQuery(int queryNumber, String toCheck, int expectedNumberOfOccurrences) {
|
||||
String query = sqlQueries.get( queryNumber );
|
||||
int actual = query.split( " " + toCheck + " ", -1 ).length - 1;
|
||||
assertThat( "number of " + toCheck,actual, is( expectedNumberOfOccurrences ) );
|
||||
assertThat( "number of " + toCheck, actual, is( expectedNumberOfOccurrences ) );
|
||||
}
|
||||
|
||||
public void assertIsSelect(int queryNumber) {
|
||||
|
@ -68,4 +79,8 @@ public class SQLStatementInspector implements StatementInspector {
|
|||
String query = sqlQueries.get( queryNumber );
|
||||
assertTrue( query.toLowerCase( Locale.ROOT ).startsWith( "update" ) );
|
||||
}
|
||||
|
||||
public static SQLStatementInspector extractFromSession(SessionImplementor session) {
|
||||
return (SQLStatementInspector) session.getJdbcSessionContext().getStatementInspector();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
|
|||
import org.hibernate.boot.registry.StandardServiceInitiator;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.integrator.spi.Integrator;
|
||||
import org.hibernate.service.spi.ServiceContributor;
|
||||
|
||||
|
@ -96,6 +97,8 @@ public class ServiceRegistryExtension
|
|||
throw new RuntimeException( "Unable to determine how to handle given ExtensionContext : " + context.getDisplayName() );
|
||||
}
|
||||
|
||||
// set some baseline test settings
|
||||
ssrb.applySetting( AvailableSettings.STATEMENT_INSPECTOR, org.hibernate.testing.jdbc.SQLStatementInspector.class );
|
||||
|
||||
final Optional<ServiceRegistry> ssrAnnWrapper = AnnotationSupport.findAnnotation(
|
||||
context.getElement().get(),
|
||||
|
|
Loading…
Reference in New Issue