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:
Steve Ebersole 2021-04-14 15:58:03 -05:00
parent c4445fbf5c
commit 3958ee2360
20 changed files with 1663 additions and 1379 deletions

View File

@ -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;
}

View File

@ -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,

View File

@ -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,

View File

@ -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(

View File

@ -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;

View File

@ -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();

View File

@ -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 );

View File

@ -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;
}
}

View File

@ -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 {

View File

@ -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();
}
);
}
}

View File

@ -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 {

View File

@ -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;
}
}

View File

@ -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 );
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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(),