Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@
import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.SqmStatement;
import org.hibernate.query.sqm.tree.cte.SqmCteStatement;
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.expression.ValueBindJpaCriteriaParameter;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.from.SqmFrom;
import org.hibernate.query.sqm.tree.from.SqmFromClause;
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
import org.hibernate.query.sqm.tree.from.SqmRoot;
Expand Down Expand Up @@ -528,8 +531,9 @@ public SqmSelectStatement<Long> createCountQuery() {
// in 'select' list (we don't even need to hit the database to
// know they return exactly one row)
if ( queryPart.isSimpleQueryPart()
&& !( querySpec = (SqmQuerySpec<?>) queryPart ).isDistinct()
&& querySpec.getGroupingExpressions().isEmpty() ) {
&& !( querySpec = queryPart.getFirstQuerySpec() ).isDistinct()
&& querySpec.getGroupingExpressions().isEmpty()
&& !selectsEntityValuedPaths( querySpec ) ) {
for ( SqmRoot<?> root : querySpec.getRootList() ) {
root.removeLeftFetchJoins();
}
Expand All @@ -553,6 +557,31 @@ public SqmSelectStatement<Long> createCountQuery() {
}
}

private static boolean selectsEntityValuedPaths(SqmQuerySpec<?> querySpec) {
for ( SqmSelectableNode<?> selection : querySpec.getSelectClause().getSelectionItems() ) {
if ( selection instanceof SqmEntityValuedSimplePath<?> entityPath ) {
final SqmPath<?> lhs = entityPath.getLhs();
if ( lhs instanceof SqmFrom<?, ?> from ) {
// force an explicit inner join for this implicit entity valued path
from.join( entityPath.getNavigablePath().getLocalName() );
}
else {
return true;
}
}
else if ( selection instanceof SqmPath<?> path ) {
SqmPath<?> lhs = path.getLhs();
while ( lhs != null ) {
if ( lhs instanceof SqmEntityValuedSimplePath<?> ) {
return true;
}
lhs = lhs.getLhs();
}
}
}
return false;
}

private <S> void aliasSelections(SqmQueryPart<S> queryPart) {
if ( queryPart.isSimpleQueryPart() ) {
final SqmQuerySpec<S> querySpec = queryPart.getFirstQuerySpec();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.ParameterExpression;
import jakarta.persistence.criteria.Root;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.Jira;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import java.util.List;
Expand All @@ -28,7 +32,6 @@
public class CountTest {

@Test void testCount(SessionFactoryScope scope) {
scope.inTransaction(session -> session.createMutationQuery("delete Book").executeUpdate());
scope.inTransaction(session -> {
session.persist(new Book("9781932394153", "Hibernate in Action"));
session.persist(new Book("9781617290459", "Java Persistence with Hibernate"));
Expand Down Expand Up @@ -64,7 +67,6 @@ public class CountTest {
}

@Test void testCountNative(SessionFactoryScope scope) {
scope.inTransaction(session -> session.createMutationQuery("delete Book").executeUpdate());
scope.inTransaction(session -> {
session.persist(new Book("9781932394153", "Hibernate in Action"));
session.persist(new Book("9781617290459", "Java Persistence with Hibernate"));
Expand All @@ -86,7 +88,6 @@ public class CountTest {
}

@Test void testCountCriteria(SessionFactoryScope scope) {
scope.inTransaction(session -> session.createMutationQuery("delete Book").executeUpdate());
scope.inTransaction(session -> {
session.persist(new Book("9781932394153", "Hibernate in Action"));
session.persist(new Book("9781617290459", "Java Persistence with Hibernate"));
Expand Down Expand Up @@ -120,6 +121,50 @@ public class CountTest {
});
}

@Test
@Jira( "https://hibernate.atlassian.net/browse/HHH-19065" )
public void testJoins(SessionFactoryScope scope) {
scope.inTransaction( session -> {
Publisher p = new Publisher( 1L, "Manning" );
session.persist( p );
final Book book = new Book( "9781932394153", "Hibernate in Action" );
book.publisher = p;
session.persist( book );
session.persist( new Book( "9781617290459", "Java Persistence with Hibernate" ) );
} );
scope.inSession( session -> {
// explicit inner join
assertCount( 1, "select p from Book b join b.publisher p", Publisher.class, session );
assertCount( 1, "select p.name from Book b join b.publisher p", String.class, session );
// explicit left join
assertCount( 2, "select p from Book b left join b.publisher p", Publisher.class, session );
assertCount( 2, "select p.name from Book b left join b.publisher p", String.class, session );
// implicit join
assertCount( 1, "select b.publisher from Book b", Publisher.class, session );
assertCount( 1, "select b.publisher from Book b join b.publisher", Publisher.class, session );
assertCount( 1, "select b.publisher.name from Book b", String.class, session );
assertCount( 1, "select publisher.name from Book b left join b.publisher", String.class, session );
assertCount( 1,
"select publisher.name from Book b left join b.publisher where publisher.name is null or length(publisher.name) > 0",
String.class, session );
// selecting only the id does not create an explicit join
assertCount( 2, "select b.publisher.id from Book b", Long.class, session );
} );
}

private <T> void assertCount(int expected, String hql, Class<T> resultClass, SessionImplementor session) {
final QueryImplementor<T> query = session.createQuery( hql, resultClass );
final List<T> resultList = query.getResultList();
final long resultCount = query.getResultCount();
assertEquals( expected, resultList.size() );
assertEquals( expected, resultCount );
}

@AfterEach
public void tearDown(SessionFactoryScope scope) {
scope.getSessionFactory().getSchemaManager().truncateMappedObjects();
}

@Entity(name="Book")
@Table(name = "books")
static class Book {
Expand Down Expand Up @@ -151,6 +196,15 @@ static class Author {
@Entity(name="Publisher")
@Table(name = "pubs")
static class Publisher {
@Id String name;
@Id Long id;
String name;

Publisher() {
}

Publisher(Long id, String name) {
this.id = id;
this.name = name;
}
}
}