The JPA TCK tests expect very specific things regarding a negated predicate node.

This commit is contained in:
Steve Ebersole 2021-11-11 03:10:39 -06:00
parent 7ce1c673ff
commit 3f7536a94e
20 changed files with 268 additions and 16 deletions

View File

@ -25,6 +25,7 @@ import org.hibernate.engine.query.spi.NativeQueryInterpreter;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.jpa.spi.JpaCompliance;
import org.hibernate.metamodel.model.domain.JpaMetamodel;
import org.hibernate.query.criteria.ValueHandlingMode;
import org.hibernate.query.hql.HqlTranslator;
@ -57,10 +58,7 @@ import org.jboss.logging.Logger;
public class QueryEngine {
private static final Logger LOG_HQL_FUNCTIONS = CoreLogging.logger( "org.hibernate.LOG_HQL_FUNCTIONS" );
public static QueryEngine from(
SessionFactoryImplementor sessionFactory,
MetadataImplementor metadata) {
final SqmCreationContext sqmCreationContext = sessionFactory;
public static QueryEngine from(SessionFactoryImplementor sessionFactory, MetadataImplementor metadata) {
final QueryEngineOptions queryEngineOptions = sessionFactory.getSessionFactoryOptions();
final SqmCreationOptions sqmCreationOptions = new SqmCreationOptionsStandard( sessionFactory );
@ -68,7 +66,7 @@ public class QueryEngine {
final HqlTranslator hqlTranslator = resolveHqlTranslator(
queryEngineOptions,
dialect,
sqmCreationContext,
sessionFactory,
sqmCreationOptions
);
@ -91,6 +89,7 @@ public class QueryEngine {
return new QueryEngine(
sessionFactory.getUuid(),
sessionFactory.getName(),
sessionFactory.getSessionFactoryOptions().getJpaCompliance(),
() -> sessionFactory.getRuntimeMetamodels().getJpaMetamodel(),
sessionFactory.getSessionFactoryOptions().getCriteriaValueHandlingMode(),
sessionFactory.getSessionFactoryOptions().getPreferredSqlTypeCodeForBoolean(),
@ -119,6 +118,7 @@ public class QueryEngine {
public QueryEngine(
String uuid,
String name,
JpaCompliance jpaCompliance,
Supplier<JpaMetamodel> jpaMetamodelAccess,
ValueHandlingMode criteriaValueHandlingMode,
int preferredSqlTypeCodeForBoolean,
@ -140,6 +140,7 @@ public class QueryEngine {
this.criteriaBuilder = new SqmCriteriaNodeBuilder(
uuid,
name,
jpaCompliance.isJpaQueryComplianceEnabled(),
this,
jpaMetamodelAccess,
serviceRegistry,
@ -212,6 +213,7 @@ public class QueryEngine {
this.criteriaBuilder = new SqmCriteriaNodeBuilder(
uuid,
name,
false,
this,
() -> jpaMetamodel,
serviceRegistry,

View File

@ -78,6 +78,8 @@ public interface NodeBuilder extends HibernateCriteriaBuilder {
return getDomainModel().getTypeConfiguration();
}
boolean isJpaQueryComplianceEnabled();
ServiceRegistry getServiceRegistry();
QueryEngine getQueryEngine();

View File

@ -150,6 +150,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
return new SqmCriteriaNodeBuilder(
sf.getUuid(),
sf.getName(),
sf.getSessionFactoryOptions().getJpaCompliance().isJpaQueryComplianceEnabled(),
sf.getQueryEngine(),
() -> sf.getRuntimeMetamodels().getJpaMetamodel(),
sf.getServiceRegistry(),
@ -159,6 +160,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
private final String uuid;
private final String name;
private final transient boolean jpaComplianceEnabled;
private final transient QueryEngine queryEngine;
private final transient Supplier<JpaMetamodel> domainModelAccess;
private final transient ServiceRegistry serviceRegistry;
@ -169,12 +171,14 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
public SqmCriteriaNodeBuilder(
String uuid,
String name,
boolean jpaComplianceEnabled,
QueryEngine queryEngine,
Supplier<JpaMetamodel> domainModelAccess,
ServiceRegistry serviceRegistry,
ValueHandlingMode criteriaValueHandlingMode) {
this.uuid = uuid;
this.name = name;
this.jpaComplianceEnabled = jpaComplianceEnabled;
this.queryEngine = queryEngine;
this.domainModelAccess = domainModelAccess;
this.serviceRegistry = serviceRegistry;
@ -186,6 +190,11 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
return domainModelAccess.get();
}
@Override
public boolean isJpaQueryComplianceEnabled() {
return jpaComplianceEnabled;
}
@Override
public BasicType<Boolean> getBooleanType() {
final BasicType<Boolean> booleanType = this.booleanType;

View File

@ -16,6 +16,7 @@ import org.hibernate.query.criteria.JpaSelection;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SqmExpressable;
import org.hibernate.query.sqm.SqmTreeCreationLogger;
import org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder;
import org.hibernate.query.sqm.tree.jpa.AbstractJpaSelection;
import org.hibernate.query.sqm.tree.predicate.SqmInPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
@ -32,6 +33,11 @@ public abstract class AbstractSqmExpression<T> extends AbstractJpaSelection<T> i
super( type, criteriaBuilder );
}
@Override
public SqmCriteriaNodeBuilder nodeBuilder() {
return (SqmCriteriaNodeBuilder) super.nodeBuilder();
}
@Override
public void applyInferableType(SqmExpressable<?> type) {
// if ( type == null ) {

View File

@ -33,10 +33,16 @@ public abstract class AbstractNegatableSqmPredicate extends AbstractSqmPredicate
this.negated = !this.negated;
}
protected abstract SqmNegatablePredicate createNegatedNode();
@Override
public SqmNegatablePredicate not() {
// in certain cases JPA required that this always return
// a new instance. we may need to allow for that here (compliance?)
// a new instance.
if ( nodeBuilder().isJpaQueryComplianceEnabled() ) {
return createNegatedNode();
}
negate();
return this;
}

View File

@ -8,12 +8,12 @@ package org.hibernate.query.sqm.tree.predicate;
import java.util.Collections;
import java.util.List;
import jakarta.persistence.criteria.Expression;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SqmExpressable;
import org.hibernate.query.sqm.tree.expression.AbstractSqmExpression;
import org.hibernate.type.StandardBasicTypes;
import jakarta.persistence.criteria.Expression;
/**
* @author Steve Ebersole

View File

@ -8,8 +8,8 @@ package org.hibernate.query.sqm.tree.predicate;
import org.hibernate.query.internal.QueryHelper;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SqmExpressable;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.SqmExpressable;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
/**
@ -70,4 +70,9 @@ public class SqmBetweenPredicate extends AbstractNegatableSqmPredicate {
sb.append( " and " );
upperBound.appendHqlString( sb );
}
@Override
protected SqmNegatablePredicate createNegatedNode() {
return new SqmBetweenPredicate( expression, lowerBound, upperBound, ! isNegated(), nodeBuilder() );
}
}

View File

@ -22,8 +22,17 @@ import org.hibernate.query.sqm.tree.expression.SqmExpression;
public class SqmBooleanExpressionPredicate extends AbstractNegatableSqmPredicate {
private final SqmExpression<Boolean> booleanExpression;
public SqmBooleanExpressionPredicate(SqmExpression<Boolean> booleanExpression, NodeBuilder nodeBuilder) {
super( nodeBuilder );
public SqmBooleanExpressionPredicate(
SqmExpression<Boolean> booleanExpression,
NodeBuilder nodeBuilder) {
this( booleanExpression, false, nodeBuilder );
}
public SqmBooleanExpressionPredicate(
SqmExpression<Boolean> booleanExpression,
boolean negated,
NodeBuilder nodeBuilder) {
super( negated, nodeBuilder );
assert booleanExpression.getNodeType() != null;
final Class<?> expressionJavaType = booleanExpression.getNodeType().getExpressableJavaTypeDescriptor().getJavaTypeClass();
@ -50,4 +59,9 @@ public class SqmBooleanExpressionPredicate extends AbstractNegatableSqmPredicate
public void appendHqlString(StringBuilder sb) {
booleanExpression.appendHqlString( sb );
}
@Override
protected SqmNegatablePredicate createNegatedNode() {
return new SqmBooleanExpressionPredicate( booleanExpression, !isNegated(), nodeBuilder() );
}
}

View File

@ -40,6 +40,13 @@ public class SqmComparisonPredicate extends AbstractNegatableSqmPredicate {
rightHandExpression.applyInferableType( expressableType );
}
private SqmComparisonPredicate(SqmComparisonPredicate affirmativeForm) {
super( true, affirmativeForm.nodeBuilder() );
this.leftHandExpression = affirmativeForm.leftHandExpression;
this.rightHandExpression = affirmativeForm.rightHandExpression;
this.operator = affirmativeForm.operator;
}
public SqmExpression<?> getLeftHandExpression() {
return leftHandExpression;
}
@ -53,13 +60,13 @@ public class SqmComparisonPredicate extends AbstractNegatableSqmPredicate {
}
@Override
public boolean isNegated() {
return false;
public void negate() {
this.operator = this.operator.negated();
}
@Override
public void negate() {
this.operator = this.operator.negated();
protected SqmNegatablePredicate createNegatedNode() {
return new SqmComparisonPredicate( this );
}
@Override

View File

@ -43,4 +43,9 @@ public class SqmEmptinessPredicate extends AbstractNegatableSqmPredicate {
sb.append( " is empty" );
}
}
@Override
protected SqmNegatablePredicate createNegatedNode() {
return new SqmEmptinessPredicate( pluralPath, !isNegated(), nodeBuilder() );
}
}

View File

@ -19,7 +19,14 @@ public class SqmExistsPredicate extends AbstractNegatableSqmPredicate {
public SqmExistsPredicate(
SqmExpression<?> expression,
NodeBuilder nodeBuilder) {
super( nodeBuilder );
this( expression, false, nodeBuilder );
}
public SqmExistsPredicate(
SqmExpression<?> expression,
boolean negated,
NodeBuilder nodeBuilder) {
super( negated, nodeBuilder );
this.expression = expression;
expression.applyInferableType( expression.getNodeType() );
@ -44,4 +51,9 @@ public class SqmExistsPredicate extends AbstractNegatableSqmPredicate {
}
expression.appendHqlString( sb );
}
@Override
protected SqmNegatablePredicate createNegatedNode() {
return new SqmExistsPredicate( expression, !isNegated(), nodeBuilder() );
}
}

View File

@ -139,4 +139,9 @@ public class SqmInListPredicate<T> extends AbstractNegatableSqmPredicate impleme
}
sb.append( ')' );
}
@Override
protected SqmNegatablePredicate createNegatedNode() {
return new SqmInListPredicate<>( testExpression, listExpressions, !isNegated(), nodeBuilder() );
}
}

View File

@ -92,4 +92,14 @@ public class SqmInSubQueryPredicate<T> extends AbstractNegatableSqmPredicate imp
sb.append( " in " );
subQueryExpression.appendHqlString( sb );
}
@Override
protected SqmNegatablePredicate createNegatedNode() {
return new SqmInSubQueryPredicate<>(
testExpression,
subQueryExpression,
!isNegated(),
nodeBuilder()
);
}
}

View File

@ -100,4 +100,9 @@ public class SqmLikePredicate extends AbstractNegatableSqmPredicate {
escapeCharacter.appendHqlString( sb );
}
}
@Override
protected SqmNegatablePredicate createNegatedNode() {
return new SqmLikePredicate( matchExpression, pattern, escapeCharacter, !isNegated(), nodeBuilder() );
}
}

View File

@ -56,4 +56,9 @@ public class SqmMemberOfPredicate extends AbstractNegatableSqmPredicate {
sb.append( " member of " );
pluralPath.appendHqlString( sb );
}
@Override
protected SqmNegatablePredicate createNegatedNode() {
return new SqmMemberOfPredicate( leftHandExpression, pluralPath, !isNegated(), nodeBuilder() );
}
}

View File

@ -44,4 +44,9 @@ public class SqmNegatedPredicate extends AbstractNegatableSqmPredicate {
wrappedPredicate.appendHqlString( sb );
sb.append( ')' );
}
@Override
protected SqmNegatablePredicate createNegatedNode() {
return new SqmNegatedPredicate( this, nodeBuilder() );
}
}

View File

@ -44,4 +44,9 @@ public class SqmNullnessPredicate extends AbstractNegatableSqmPredicate {
sb.append( " is null" );
}
}
@Override
protected SqmNegatablePredicate createNegatedNode() {
return new SqmNullnessPredicate( expression, !isNegated(), nodeBuilder() );
}
}

View File

@ -0,0 +1,74 @@
/*
* 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.criteria;
import java.util.List;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.JpaCriteriaQuery;
import org.hibernate.query.criteria.JpaRoot;
import org.hibernate.testing.orm.domain.gambit.EntityOfBasics;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Steve Ebersole
*/
@DomainModel( annotatedClasses = EntityOfBasics.class )
@SessionFactory
public class LocateTests {
@Test
public void simpleLocateTest(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
final HibernateCriteriaBuilder nodeBuilder = session.getFactory().getCriteriaBuilder();
final JpaCriteriaQuery<Integer> criteria = nodeBuilder.createQuery( Integer.class );
final JpaRoot<EntityOfBasics> root = criteria.from( EntityOfBasics.class );
criteria.select( root.get( "id" ) );
criteria.where(
nodeBuilder.greaterThan(
nodeBuilder.locate(
root.get( "theString" ),
nodeBuilder.literal("def")
),
0
)
);
final List<Integer> list = session.createQuery( criteria ).list();
assertThat( list ).hasSize( 1 );
assertThat( list.get( 0 ) ).isEqualTo( 2 );
} );
}
@BeforeEach
public void createTestData(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
final EntityOfBasics entity1 = new EntityOfBasics( 1 );
entity1.setTheString( "abc" );
session.persist( entity1 );
final EntityOfBasics entity2 = new EntityOfBasics( 2 );
entity2.setTheString( "def" );
session.persist( entity2 );
} );
}
@BeforeEach
public void dropTestData(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
session.createQuery( "delete EntityOfBasics" ).executeUpdate();
} );
}
}

View File

@ -217,6 +217,26 @@ public class MultiSelectTests {
} );
}
@Test
public void tupleSelectionArrayTest(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
final CriteriaBuilder nodeBuilder = session.getFactory().getNodeBuilder();
final CriteriaQuery<Tuple> criteria = nodeBuilder.createTupleQuery();
final Root<BasicEntity> root = criteria.from( BasicEntity.class );
Selection<?>[] s = { root.get("id"), root.get("data") };
criteria.select(nodeBuilder.tuple(s));
final List<Tuple> results = session.createQuery( criteria ).list();
assertThat( results ).hasSize( 1 );
final Tuple firstResult = results.get( 0 );
assertThat( firstResult.getElements() ).hasSize( 2 );
assertThat( firstResult.get(0) ).isEqualTo( 1 );
assertThat( firstResult.get(1) ).isEqualTo( "abc" );
} );
}
@BeforeEach
public void createTestData(SessionFactoryScope scope) {
scope.inTransaction( (session) -> session.persist( new BasicEntity( 1, "abc" ) ) );

View File

@ -0,0 +1,55 @@
/*
* 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.criteria;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.JpaPredicate;
import org.hibernate.testing.orm.domain.gambit.BasicEntity;
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.assertj.core.api.Assertions.assertThat;
/**
* @author Steve Ebersole
*/
@ServiceRegistry(
settings = @Setting( name = AvailableSettings.JPA_QUERY_COMPLIANCE, value = "true" )
)
@DomainModel( annotatedClasses = BasicEntity.class )
@SessionFactory
public class NegationTests {
@Test
public void simpleTest(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
final HibernateCriteriaBuilder nodeBuilder = session.getFactory().getCriteriaBuilder();
final JpaPredicate equality = nodeBuilder.equal( nodeBuilder.literal( 1 ), nodeBuilder.literal( 1 ) );
final JpaPredicate inequality = equality.not();
assertThat( equality ).isNotSameAs( inequality );
assertThat( equality.isNegated() ).isFalse();
assertThat( inequality.isNegated() ).isTrue();
} );
}
@BeforeEach
public void createTestData(SessionFactoryScope scope) {
scope.inTransaction( (session) -> session.persist( new BasicEntity( 1, "abc" ) ) );
}
@AfterEach
public void dropTestData(SessionFactoryScope scope) {
scope.inTransaction( (session) -> session.createQuery( "delete BasicEntity" ).executeUpdate() );
}
}