HHH-17967
- Add test for issue (already fixed on main) - Backport the minimal necessary bits of HHH-16931 and https://github.com/hibernate/hibernate-orm/pull/7883 to fix the NPE Signed-off-by: Jan Schatteman <jschatte@redhat.com>
This commit is contained in:
parent
c843573007
commit
03e589ef0d
|
@ -218,7 +218,7 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
|
||||||
if ( queryOptions.getTupleTransformer() != null ) {
|
if ( queryOptions.getTupleTransformer() != null ) {
|
||||||
return makeRowTransformerTupleTransformerAdapter( sqm, queryOptions );
|
return makeRowTransformerTupleTransformerAdapter( sqm, queryOptions );
|
||||||
}
|
}
|
||||||
else if ( resultType == null ) {
|
else if ( resultType == null || resultType == Object.class ) {
|
||||||
return RowTransformerStandardImpl.instance();
|
return RowTransformerStandardImpl.instance();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -33,7 +33,7 @@ public abstract class AbstractSqmAttributeJoin<O,T>
|
||||||
extends AbstractSqmQualifiedJoin<O,T>
|
extends AbstractSqmQualifiedJoin<O,T>
|
||||||
implements SqmAttributeJoin<O,T> {
|
implements SqmAttributeJoin<O,T> {
|
||||||
|
|
||||||
private final boolean fetched;
|
private boolean fetched;
|
||||||
|
|
||||||
public AbstractSqmAttributeJoin(
|
public AbstractSqmAttributeJoin(
|
||||||
SqmFrom<?,O> lhs,
|
SqmFrom<?,O> lhs,
|
||||||
|
@ -88,6 +88,10 @@ public abstract class AbstractSqmAttributeJoin<O,T>
|
||||||
return fetched;
|
return fetched;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void clearFetched() {
|
||||||
|
fetched = false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <X> X accept(SemanticQueryWalker<X> walker) {
|
public <X> X accept(SemanticQueryWalker<X> walker) {
|
||||||
return walker.visitQualifiedAttributeJoin( this );
|
return walker.visitQualifiedAttributeJoin( this );
|
||||||
|
|
|
@ -15,6 +15,7 @@ import java.util.Set;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.hibernate.Internal;
|
||||||
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||||
import org.hibernate.metamodel.model.domain.BagPersistentAttribute;
|
import org.hibernate.metamodel.model.domain.BagPersistentAttribute;
|
||||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||||
|
@ -254,6 +255,29 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
|
||||||
findRoot().addOrderedJoin( join );
|
findRoot().addOrderedJoin( join );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
public void removeLeftFetchJoins() {
|
||||||
|
if ( joins != null ) {
|
||||||
|
for ( SqmJoin<T, ?> join : new ArrayList<>(joins) ) {
|
||||||
|
if ( join instanceof AbstractSqmAttributeJoin ) {
|
||||||
|
final AbstractSqmAttributeJoin<T, ?> attributeJoin = (AbstractSqmAttributeJoin<T, ?>) join;
|
||||||
|
if ( attributeJoin.isFetched() ) {
|
||||||
|
if ( join.getSqmJoinType() == SqmJoinType.LEFT ) {
|
||||||
|
joins.remove( join );
|
||||||
|
final List<SqmJoin<?, ?>> orderedJoins = findRoot().getOrderedJoins();
|
||||||
|
if (orderedJoins != null) {
|
||||||
|
orderedJoins.remove( join );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
attributeJoin.clearFetched();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitSqmJoins(Consumer<SqmJoin<T, ?>> consumer) {
|
public void visitSqmJoins(Consumer<SqmJoin<T, ?>> consumer) {
|
||||||
if ( joins != null ) {
|
if ( joins != null ) {
|
||||||
|
|
|
@ -45,7 +45,7 @@ public abstract class AbstractSqmSelectQuery<T>
|
||||||
implements SqmSelectQuery<T> {
|
implements SqmSelectQuery<T> {
|
||||||
private final Map<String, SqmCteStatement<?>> cteStatements;
|
private final Map<String, SqmCteStatement<?>> cteStatements;
|
||||||
private SqmQueryPart<T> sqmQueryPart;
|
private SqmQueryPart<T> sqmQueryPart;
|
||||||
private Class<T> resultType;
|
private final Class<T> resultType;
|
||||||
|
|
||||||
public AbstractSqmSelectQuery(Class<T> resultType, NodeBuilder builder) {
|
public AbstractSqmSelectQuery(Class<T> resultType, NodeBuilder builder) {
|
||||||
this( new SqmQuerySpec<>( builder ), resultType, builder );
|
this( new SqmQuerySpec<>( builder ), resultType, builder );
|
||||||
|
@ -202,8 +202,11 @@ public abstract class AbstractSqmSelectQuery<T>
|
||||||
return resultType;
|
return resultType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Don't use this method. It has no effect.
|
||||||
|
*/
|
||||||
protected void setResultType(Class<T> resultType) {
|
protected void setResultType(Class<T> resultType) {
|
||||||
this.resultType = resultType;
|
// No-op
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -410,7 +413,6 @@ public abstract class AbstractSqmSelectQuery<T>
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
setResultType( (Class<T>) Object[].class );
|
|
||||||
resultSelection = ( Selection<? extends T> ) nodeBuilder().array( selections );
|
resultSelection = ( Selection<? extends T> ) nodeBuilder().array( selections );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,9 @@ import org.hibernate.query.sqm.tree.expression.SqmStar;
|
||||||
import org.hibernate.query.sqm.tree.expression.ValueBindJpaCriteriaParameter;
|
import org.hibernate.query.sqm.tree.expression.ValueBindJpaCriteriaParameter;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||||
import org.hibernate.query.sqm.tree.from.SqmFromClause;
|
import org.hibernate.query.sqm.tree.from.SqmFromClause;
|
||||||
|
import org.hibernate.query.sqm.tree.from.SqmRoot;
|
||||||
|
|
||||||
|
import static org.hibernate.query.sqm.tree.SqmCopyContext.noParamCopyContext;
|
||||||
import static org.hibernate.query.sqm.tree.jpa.ParameterCollector.collectParameters;
|
import static org.hibernate.query.sqm.tree.jpa.ParameterCollector.collectParameters;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -119,6 +121,10 @@ public class SqmSelectStatement<T> extends AbstractSqmSelectQuery<T> implements
|
||||||
if ( existing != null ) {
|
if ( existing != null ) {
|
||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
|
return createCopy( context, getResultType() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private <X> SqmSelectStatement<X> createCopy(SqmCopyContext context, Class<X> resultType) {
|
||||||
final Set<SqmParameter<?>> parameters;
|
final Set<SqmParameter<?>> parameters;
|
||||||
if ( this.parameters == null ) {
|
if ( this.parameters == null ) {
|
||||||
parameters = null;
|
parameters = null;
|
||||||
|
@ -129,17 +135,19 @@ public class SqmSelectStatement<T> extends AbstractSqmSelectQuery<T> implements
|
||||||
parameters.add( parameter.copy( context ) );
|
parameters.add( parameter.copy( context ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final SqmSelectStatement<T> statement = context.registerCopy(
|
//noinspection unchecked
|
||||||
|
final SqmSelectStatement<X> statement = (SqmSelectStatement<X>) context.registerCopy(
|
||||||
this,
|
this,
|
||||||
new SqmSelectStatement<>(
|
new SqmSelectStatement<>(
|
||||||
nodeBuilder(),
|
nodeBuilder(),
|
||||||
copyCteStatements( context ),
|
copyCteStatements( context ),
|
||||||
getResultType(),
|
resultType,
|
||||||
getQuerySource(),
|
getQuerySource(),
|
||||||
parameters
|
parameters
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
statement.setQueryPart( getQueryPart().copy( context ) );
|
//noinspection unchecked
|
||||||
|
statement.setQueryPart( (SqmQueryPart<X>) getQueryPart().copy( context ) );
|
||||||
return statement;
|
return statement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,9 +274,6 @@ public class SqmSelectStatement<T> extends AbstractSqmSelectQuery<T> implements
|
||||||
checkSelectionIsJpaCompliant( selection );
|
checkSelectionIsJpaCompliant( selection );
|
||||||
}
|
}
|
||||||
getQuerySpec().setSelection( (JpaSelection<T>) selection );
|
getQuerySpec().setSelection( (JpaSelection<T>) selection );
|
||||||
if ( getResultType() == Object.class ) {
|
|
||||||
setResultType( (Class<T>) selection.getJavaType() );
|
|
||||||
}
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,7 +314,6 @@ public class SqmSelectStatement<T> extends AbstractSqmSelectQuery<T> implements
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
setResultType( (Class<T>) Object[].class );
|
|
||||||
resultSelection = ( Selection<? extends T> ) nodeBuilder().array( selections );
|
resultSelection = ( Selection<? extends T> ) nodeBuilder().array( selections );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -460,49 +464,43 @@ public class SqmSelectStatement<T> extends AbstractSqmSelectQuery<T> implements
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JpaCriteriaQuery<Long> createCountQuery() {
|
public SqmSelectStatement<Long> createCountQuery() {
|
||||||
final SqmCopyContext context = new NoParamSqmCopyContext() {
|
final SqmSelectStatement<?> copy = createCopy( noParamCopyContext(), Object.class );
|
||||||
@Override
|
final SqmQueryPart<?> queryPart = copy.getQueryPart();
|
||||||
public boolean copyFetchedFlag() {
|
final SqmQuerySpec<?> querySpec;
|
||||||
return false;
|
//TODO: detect queries with no 'group by', but aggregate functions
|
||||||
|
// 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() ) {
|
||||||
|
for ( SqmRoot<?> root : querySpec.getRootList() ) {
|
||||||
|
root.removeLeftFetchJoins();
|
||||||
}
|
}
|
||||||
};
|
querySpec.getSelectClause().setSelection( nodeBuilder().count( new SqmStar( nodeBuilder() )) );
|
||||||
final NodeBuilder nodeBuilder = nodeBuilder();
|
if ( querySpec.getFetch() == null && querySpec.getOffset() == null ) {
|
||||||
final Set<SqmParameter<?>> parameters;
|
querySpec.setOrderByClause( null );
|
||||||
if ( this.parameters == null ) {
|
}
|
||||||
parameters = null;
|
|
||||||
|
return (SqmSelectStatement<Long>) copy;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
parameters = new LinkedHashSet<>( this.parameters.size() );
|
final JpaSelection<?> selection = queryPart.getFirstQuerySpec().getSelection();
|
||||||
for ( SqmParameter<?> parameter : this.parameters ) {
|
if ( selection.isCompoundSelection() ) {
|
||||||
parameters.add( parameter.copy( context ) );
|
char c = 'a';
|
||||||
|
for ( JpaSelection<?> item : selection.getSelectionItems() ) {
|
||||||
|
item.alias( Character.toString( ++c ) + '_' );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
selection.alias( "a_" );
|
||||||
|
}
|
||||||
|
final SqmSubQuery<?> subquery = new SqmSubQuery<>( copy, queryPart, null, nodeBuilder() );
|
||||||
|
final SqmSelectStatement<Long> query = nodeBuilder().createQuery( Long.class );
|
||||||
|
query.from( subquery );
|
||||||
|
query.select( nodeBuilder().count( new SqmStar(nodeBuilder())) );
|
||||||
|
return query;
|
||||||
}
|
}
|
||||||
final SqmSelectStatement<Long> selectStatement = new SqmSelectStatement<>(
|
|
||||||
nodeBuilder,
|
|
||||||
copyCteStatements( context ),
|
|
||||||
Long.class,
|
|
||||||
SqmQuerySource.CRITERIA,
|
|
||||||
parameters
|
|
||||||
);
|
|
||||||
final SqmQuerySpec<Long> querySpec = new SqmQuerySpec<>( nodeBuilder );
|
|
||||||
|
|
||||||
final SqmSubQuery<Tuple> subquery = new SqmSubQuery<>( selectStatement, Tuple.class, nodeBuilder );
|
|
||||||
final SqmQueryPart<T> queryPart = getQueryPart().copy( context );
|
|
||||||
resetSelections( queryPart );
|
|
||||||
// Reset the
|
|
||||||
if ( queryPart.getFetch() == null && queryPart.getOffset() == null ) {
|
|
||||||
queryPart.setOrderByClause( null );
|
|
||||||
}
|
|
||||||
//noinspection unchecked
|
|
||||||
subquery.setQueryPart( (SqmQueryPart<Tuple>) queryPart );
|
|
||||||
|
|
||||||
querySpec.setFromClause( new SqmFromClause( 1 ) );
|
|
||||||
querySpec.setSelectClause( new SqmSelectClause( false, 1, nodeBuilder ) );
|
|
||||||
selectStatement.setQueryPart( querySpec );
|
|
||||||
selectStatement.select( nodeBuilder.count( new SqmStar( nodeBuilder ) ) );
|
|
||||||
selectStatement.from( subquery );
|
|
||||||
return selectStatement;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetSelections(SqmQueryPart<?> queryPart) {
|
private void resetSelections(SqmQueryPart<?> queryPart) {
|
||||||
|
|
|
@ -258,7 +258,6 @@ public class SqmSubQuery<T> extends AbstractSqmSelectQuery<T> implements SqmSele
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
setResultType( (Class<T>) Object[].class );
|
|
||||||
resultSelection = ( Selection<? extends T> ) nodeBuilder().array( selections );
|
resultSelection = ( Selection<? extends T> ) nodeBuilder().array( selections );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -609,9 +608,6 @@ public class SqmSubQuery<T> extends AbstractSqmSelectQuery<T> implements SqmSele
|
||||||
public void applyInferableType(SqmExpressible<?> type) {
|
public void applyInferableType(SqmExpressible<?> type) {
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
expressibleType = (SqmExpressible<T>) type;
|
expressibleType = (SqmExpressible<T>) type;
|
||||||
if ( expressibleType != null && expressibleType.getExpressibleJavaType() != null ) {
|
|
||||||
setResultType( expressibleType.getExpressibleJavaType().getJavaTypeClass() );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyInferableType(Class<T> type) {
|
private void applyInferableType(Class<T> type) {
|
||||||
|
|
|
@ -28,19 +28,49 @@ import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Inheritance;
|
||||||
|
import jakarta.persistence.InheritanceType;
|
||||||
|
import jakarta.persistence.MappedSuperclass;
|
||||||
|
import jakarta.persistence.SequenceGenerator;
|
||||||
import jakarta.persistence.Tuple;
|
import jakarta.persistence.Tuple;
|
||||||
|
import jakarta.persistence.TypedQuery;
|
||||||
|
import jakarta.persistence.criteria.Root;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Christian Beikov
|
* @author Christian Beikov
|
||||||
*/
|
*/
|
||||||
@DomainModel(standardModels = StandardDomainModel.CONTACTS)
|
@DomainModel(
|
||||||
|
standardModels = StandardDomainModel.CONTACTS,
|
||||||
|
annotatedClasses = {CountQueryTests.LogSupport.class, CountQueryTests.Contract.class}
|
||||||
|
)
|
||||||
@SessionFactory
|
@SessionFactory
|
||||||
@JiraKey("HHH-17410")
|
|
||||||
public class CountQueryTests {
|
public class CountQueryTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@JiraKey( "HHH-17967" )
|
||||||
|
public void testForHHH17967(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
HibernateCriteriaBuilder cb = session.getCriteriaBuilder();
|
||||||
|
JpaCriteriaQuery<Contract> cq = cb.createQuery( Contract.class );
|
||||||
|
Root<Contract> root = cq.from( Contract.class );
|
||||||
|
cq.select( root );
|
||||||
|
TypedQuery<Long> query = session.createQuery( cq.createCountQuery() );
|
||||||
|
query.getSingleResult();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@JiraKey("HHH-17410")
|
||||||
public void testBasic(SessionFactoryScope scope) {
|
public void testBasic(SessionFactoryScope scope) {
|
||||||
scope.inTransaction(
|
scope.inTransaction(
|
||||||
session -> {
|
session -> {
|
||||||
|
@ -58,6 +88,7 @@ public class CountQueryTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@JiraKey("HHH-17410")
|
||||||
public void testFetches(SessionFactoryScope scope) {
|
public void testFetches(SessionFactoryScope scope) {
|
||||||
scope.inTransaction(
|
scope.inTransaction(
|
||||||
session -> {
|
session -> {
|
||||||
|
@ -66,7 +97,7 @@ public class CountQueryTests {
|
||||||
"select e from Contact e join fetch e.alternativeContact",
|
"select e from Contact e join fetch e.alternativeContact",
|
||||||
Contact.class
|
Contact.class
|
||||||
) );
|
) );
|
||||||
verifyCollectionCount( session, cb.createQuery(
|
verifyCount( session, cb.createQuery(
|
||||||
"select e from Contact e left join fetch e.addresses",
|
"select e from Contact e left join fetch e.addresses",
|
||||||
Contact.class
|
Contact.class
|
||||||
) );
|
) );
|
||||||
|
@ -75,6 +106,7 @@ public class CountQueryTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@JiraKey("HHH-17410")
|
||||||
public void testConstructor(SessionFactoryScope scope) {
|
public void testConstructor(SessionFactoryScope scope) {
|
||||||
scope.inTransaction(
|
scope.inTransaction(
|
||||||
session -> {
|
session -> {
|
||||||
|
@ -89,6 +121,7 @@ public class CountQueryTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsRecursiveCtes.class)
|
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsRecursiveCtes.class)
|
||||||
|
@JiraKey("HHH-17410")
|
||||||
public void testCte(SessionFactoryScope scope) {
|
public void testCte(SessionFactoryScope scope) {
|
||||||
scope.inTransaction(
|
scope.inTransaction(
|
||||||
session -> {
|
session -> {
|
||||||
|
@ -107,6 +140,7 @@ public class CountQueryTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@JiraKey("HHH-17410")
|
||||||
public void testValues(SessionFactoryScope scope) {
|
public void testValues(SessionFactoryScope scope) {
|
||||||
scope.inTransaction(
|
scope.inTransaction(
|
||||||
session -> {
|
session -> {
|
||||||
|
@ -126,6 +160,7 @@ public class CountQueryTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@JiraKey("HHH-17410")
|
||||||
public void testParameters(SessionFactoryScope scope) {
|
public void testParameters(SessionFactoryScope scope) {
|
||||||
scope.inTransaction(
|
scope.inTransaction(
|
||||||
session -> {
|
session -> {
|
||||||
|
@ -134,7 +169,7 @@ public class CountQueryTests {
|
||||||
final JpaCriteriaQuery<Tuple> cq = cb.createTupleQuery();
|
final JpaCriteriaQuery<Tuple> cq = cb.createTupleQuery();
|
||||||
final JpaRoot<Contact> root = cq.from( Contact.class );
|
final JpaRoot<Contact> root = cq.from( Contact.class );
|
||||||
final JpaParameterExpression<Contact.Gender> parameter = cb.parameter( Contact.Gender.class );
|
final JpaParameterExpression<Contact.Gender> parameter = cb.parameter( Contact.Gender.class );
|
||||||
|
|
||||||
cq.multiselect( root.get( "id" ), root.get( "name" ) );
|
cq.multiselect( root.get( "id" ), root.get( "name" ) );
|
||||||
cq.where( root.get( "gender" ).equalTo( parameter ) );
|
cq.where( root.get( "gender" ).equalTo( parameter ) );
|
||||||
final Long count = session.createQuery( cq.createCountQuery() )
|
final Long count = session.createQuery( cq.createCountQuery() )
|
||||||
|
@ -232,19 +267,6 @@ public class CountQueryTests {
|
||||||
assertEquals( resultList.size(), count.intValue() );
|
assertEquals( resultList.size(), count.intValue() );
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> void verifyCollectionCount(SessionImplementor session, JpaCriteriaQuery<Contact> query) {
|
|
||||||
final List<Contact> resultList = session.createQuery( query ).getResultList();
|
|
||||||
final Long count = session.createQuery( query.createCountQuery() ).getSingleResult();
|
|
||||||
int ormSize = 0;
|
|
||||||
for ( Contact contact : resultList ) {
|
|
||||||
ormSize++;
|
|
||||||
ormSize += Math.max( contact.getAddresses().size() - 1, 0 );
|
|
||||||
ormSize += Math.max( contact.getPhoneNumbers().size() - 1, 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals( ormSize, count.intValue() );
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
public void dropTestData(SessionFactoryScope scope) {
|
public void dropTestData(SessionFactoryScope scope) {
|
||||||
scope.inTransaction( (session) -> {
|
scope.inTransaction( (session) -> {
|
||||||
|
@ -252,4 +274,23 @@ public class CountQueryTests {
|
||||||
session.createMutationQuery( "delete Contact" ).executeUpdate();
|
session.createMutationQuery( "delete Contact" ).executeUpdate();
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MappedSuperclass
|
||||||
|
public static abstract class LogSupport {
|
||||||
|
@Column(name = "SOMESTRING")
|
||||||
|
private String s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
|
||||||
|
public static class Contract extends LogSupport {
|
||||||
|
@Id
|
||||||
|
@Column(name = "PK")
|
||||||
|
@SequenceGenerator(name = "CONTRACT_GENERATOR", sequenceName = "CONTRACT_SEQ", allocationSize = 1)
|
||||||
|
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CONTRACT_GENERATOR")
|
||||||
|
private Long syntheticId;
|
||||||
|
@Column(name = "CUSTOMER_NAME")
|
||||||
|
private String customerName;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue