HHH-16182 - Converted boolean values not always properly handled in predicates
This commit is contained in:
parent
cb2af521a5
commit
b8d500ec41
|
@ -7,17 +7,25 @@
|
|||
package org.hibernate.userguide.mapping.basic;
|
||||
|
||||
import java.sql.Types;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
|
||||
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
|
||||
import org.hibernate.query.criteria.JpaCriteriaQuery;
|
||||
import org.hibernate.query.criteria.JpaPath;
|
||||
import org.hibernate.query.criteria.JpaRoot;
|
||||
import org.hibernate.type.internal.ConvertedBasicTypeImpl;
|
||||
|
||||
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.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.Basic;
|
||||
|
@ -28,6 +36,7 @@ import jakarta.persistence.Table;
|
|||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.isOneOf;
|
||||
|
||||
|
@ -97,22 +106,211 @@ public class BooleanMappingTests {
|
|||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void createTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (session) -> {
|
||||
final EntityOfBooleans entity = new EntityOfBooleans();
|
||||
entity.id = 1;
|
||||
assert !entity.convertedYesNo;
|
||||
assert !entity.convertedTrueFalse;
|
||||
assert !entity.convertedNumeric;
|
||||
session.persist( entity );
|
||||
} );
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void dropTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (session) -> {
|
||||
session.createMutationQuery( "delete EntityOfBooleans" ).executeUpdate();
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@Jira( "https://hibernate.atlassian.net/browse/HHH-16182" )
|
||||
public void testQueryLiteralUsage(SessionFactoryScope scope) {
|
||||
public void testComparisonLiteralHandling(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (session) -> {
|
||||
session.createSelectionQuery( "from EntityOfBooleans where convertedYesNo = true" ).list();
|
||||
session.createSelectionQuery( "from EntityOfBooleans where convertedTrueFalse = true" ).list();
|
||||
session.createSelectionQuery( "from EntityOfBooleans where convertedNumeric = true" ).list();
|
||||
|
||||
session.createMutationQuery( "delete EntityOfBooleans where convertedYesNo = true" ).executeUpdate();
|
||||
session.createMutationQuery( "delete EntityOfBooleans where convertedTrueFalse = true" ).executeUpdate();
|
||||
session.createMutationQuery( "delete EntityOfBooleans where convertedNumeric = true" ).executeUpdate();
|
||||
|
||||
session.createMutationQuery( "update EntityOfBooleans set convertedYesNo = true" ).executeUpdate();
|
||||
session.createMutationQuery( "update EntityOfBooleans set convertedTrueFalse = true" ).executeUpdate();
|
||||
session.createMutationQuery( "update EntityOfBooleans set convertedNumeric = true" ).executeUpdate();
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where convertedYesNo = true" ).list(),
|
||||
hasSize( 0 )
|
||||
);
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where convertedTrueFalse = true" ).list(),
|
||||
hasSize( 0 )
|
||||
);
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where convertedNumeric = true" ).list(),
|
||||
hasSize( 0 )
|
||||
);
|
||||
} );
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where convertedYesNo = false" ).list(),
|
||||
hasSize( 1 )
|
||||
);
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where convertedTrueFalse = false" ).list(),
|
||||
hasSize( 1 )
|
||||
);
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where convertedNumeric = false" ).list(),
|
||||
hasSize( 1 )
|
||||
);
|
||||
} );
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where convertedYesNo != true" ).list(),
|
||||
hasSize( 1 )
|
||||
);
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where convertedTrueFalse != true" ).list(),
|
||||
hasSize( 1 )
|
||||
);
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where convertedNumeric != true" ).list(),
|
||||
hasSize( 1 )
|
||||
);
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@Jira( "https://hibernate.atlassian.net/browse/HHH-16182" )
|
||||
public void testExpressionAsPredicateUsage(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (session) -> {
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where convertedYesNo" ).list(),
|
||||
hasSize( 0 )
|
||||
);
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where convertedTrueFalse" ).list(),
|
||||
hasSize( 0 )
|
||||
);
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where convertedNumeric" ).list(),
|
||||
hasSize( 0 )
|
||||
);
|
||||
} );
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where (convertedYesNo)" ).list(),
|
||||
hasSize( 0 )
|
||||
);
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where (convertedTrueFalse)" ).list(),
|
||||
hasSize( 0 )
|
||||
);
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where (convertedNumeric)" ).list(),
|
||||
hasSize( 0 )
|
||||
);
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@Jira( "https://hibernate.atlassian.net/browse/HHH-16182" )
|
||||
public void testNegatedExpressionAsPredicateUsage(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (session) -> {
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where not convertedYesNo" ).list(),
|
||||
hasSize( 1 )
|
||||
);
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where not convertedTrueFalse" ).list(),
|
||||
hasSize( 1 )
|
||||
);
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where not convertedNumeric" ).list(),
|
||||
hasSize( 1 )
|
||||
);
|
||||
} );
|
||||
scope.inTransaction( (session) -> {
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where not (convertedYesNo)" ).list(),
|
||||
hasSize( 1 )
|
||||
);
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where not (convertedTrueFalse)" ).list(),
|
||||
hasSize( 1 )
|
||||
);
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where not (convertedNumeric)" ).list(),
|
||||
hasSize( 1 )
|
||||
);
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@Jira( "https://hibernate.atlassian.net/browse/HHH-16182" )
|
||||
public void testSetClauseUsage(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (session) -> {
|
||||
assertThat(
|
||||
session.createMutationQuery( "update EntityOfBooleans set convertedYesNo = true" ).executeUpdate(),
|
||||
equalTo( 1 )
|
||||
);
|
||||
assertThat(
|
||||
session.createMutationQuery( "update EntityOfBooleans set convertedTrueFalse = true" ).executeUpdate(),
|
||||
equalTo( 1 )
|
||||
);
|
||||
assertThat(
|
||||
session.createMutationQuery( "update EntityOfBooleans set convertedNumeric = true" ).executeUpdate(),
|
||||
equalTo( 1 )
|
||||
);
|
||||
} );
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where convertedYesNo" ).list(),
|
||||
hasSize( 1 )
|
||||
);
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where convertedTrueFalse" ).list(),
|
||||
hasSize( 1 )
|
||||
);
|
||||
assertThat(
|
||||
session.createSelectionQuery( "from EntityOfBooleans where convertedNumeric" ).list(),
|
||||
hasSize( 1 )
|
||||
);
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@Jira( "https://hibernate.atlassian.net/browse/HHH-16182" )
|
||||
public void testCriteriaUsage(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (session) -> {
|
||||
assertThat( countByCriteria( "convertedYesNo", true, session ), equalTo( 0 ) );
|
||||
assertThat( countByCriteria( "convertedTrueFalse", true, session ), equalTo( 0 ) );
|
||||
assertThat( countByCriteria( "convertedNumeric", true, session ), equalTo( 0 ) );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@Jira( "https://hibernate.atlassian.net/browse/HHH-16182" )
|
||||
public void testNegatedCriteriaUsage(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (session) -> {
|
||||
assertThat( countByCriteria( "convertedYesNo", false, session ), equalTo( 1 ) );
|
||||
assertThat( countByCriteria( "convertedTrueFalse", false, session ), equalTo( 1 ) );
|
||||
assertThat( countByCriteria( "convertedNumeric", false, session ), equalTo( 1 ) );
|
||||
} );
|
||||
}
|
||||
|
||||
private int countByCriteria(String attributeName, boolean matchValue, SessionImplementor session) {
|
||||
final HibernateCriteriaBuilder builder = session.getCriteriaBuilder();
|
||||
final JpaCriteriaQuery<Long> criteria = builder.createQuery( Long.class );
|
||||
criteria.select( builder.count( builder.literal( 1 ) ) );
|
||||
final JpaRoot<EntityOfBooleans> root = criteria.from( EntityOfBooleans.class );
|
||||
final JpaPath<Boolean> convertedYesNo = root.get( attributeName );
|
||||
if ( matchValue ) {
|
||||
criteria.where( convertedYesNo );
|
||||
}
|
||||
else {
|
||||
criteria.where( builder.not( convertedYesNo ) );
|
||||
}
|
||||
|
||||
final Long result = session.createQuery( criteria ).uniqueResult();
|
||||
return result.intValue();
|
||||
}
|
||||
|
||||
@Entity(name = "EntityOfBooleans")
|
||||
|
|
|
@ -88,16 +88,12 @@ import org.hibernate.metamodel.mapping.internal.OneToManyCollectionPart;
|
|||
import org.hibernate.metamodel.mapping.internal.SqlTypedMappingImpl;
|
||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.ordering.OrderByFragment;
|
||||
import org.hibernate.type.descriptor.converter.internal.OrdinalEnumValueConverter;
|
||||
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
|
||||
import org.hibernate.metamodel.model.domain.BasicDomainType;
|
||||
import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
|
||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
|
||||
import org.hibernate.metamodel.model.domain.internal.AnyDiscriminatorSqmPath;
|
||||
import org.hibernate.metamodel.model.domain.internal.AnyDiscriminatorSqmPathSource;
|
||||
import org.hibernate.query.derived.AnonymousTupleTableGroupProducer;
|
||||
import org.hibernate.query.derived.AnonymousTupleType;
|
||||
import org.hibernate.metamodel.model.domain.internal.BasicSqmPathSource;
|
||||
import org.hibernate.metamodel.model.domain.internal.CompositeSqmPathSource;
|
||||
import org.hibernate.metamodel.model.domain.internal.DiscriminatorSqmPath;
|
||||
|
@ -115,6 +111,8 @@ import org.hibernate.query.criteria.JpaCteCriteriaAttribute;
|
|||
import org.hibernate.query.criteria.JpaPath;
|
||||
import org.hibernate.query.criteria.JpaSearchOrder;
|
||||
import org.hibernate.query.derived.AnonymousTupleEntityValuedModelPart;
|
||||
import org.hibernate.query.derived.AnonymousTupleTableGroupProducer;
|
||||
import org.hibernate.query.derived.AnonymousTupleType;
|
||||
import org.hibernate.query.spi.QueryEngine;
|
||||
import org.hibernate.query.spi.QueryOptions;
|
||||
import org.hibernate.query.spi.QueryParameterBinding;
|
||||
|
@ -392,6 +390,8 @@ import org.hibernate.type.CustomType;
|
|||
import org.hibernate.type.EnumType;
|
||||
import org.hibernate.type.JavaObjectType;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.descriptor.converter.internal.OrdinalEnumValueConverter;
|
||||
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
|
||||
import org.hibernate.type.descriptor.java.EnumJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.java.TemporalJavaType;
|
||||
|
@ -501,6 +501,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
private boolean negativeAdjustment;
|
||||
|
||||
private final Set<AssociationKey> visitedAssociationKeys = new HashSet<>();
|
||||
private final MappingMetamodel domainModel;
|
||||
|
||||
public BaseSqmToSqlAstConverter(
|
||||
SqlAstCreationContext creationContext,
|
||||
|
@ -569,8 +570,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
this.loadQueryInfluencers = loadQueryInfluencers;
|
||||
this.domainParameterXref = domainParameterXref;
|
||||
this.domainParameterBindings = domainParameterBindings;
|
||||
this.jpaCriteriaParamResolutions = domainParameterXref.getParameterResolutions()
|
||||
.getJpaCriteriaParamResolutions();
|
||||
this.jpaCriteriaParamResolutions = domainParameterXref.getParameterResolutions().getJpaCriteriaParamResolutions();
|
||||
this.domainModel = creationContext.getSessionFactory().getRuntimeMetamodels().getMappingMetamodel();
|
||||
}
|
||||
|
||||
private static Boolean stackMatchHelper(SqlAstProcessingState processingState, SqlAstProcessingState c) {
|
||||
|
@ -4813,14 +4814,11 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
// A case wrapper for non-basic paths is not possible,
|
||||
// because a case expression must return a scalar value,
|
||||
// so we instead add the type restriction predicate as conjunct
|
||||
final MappingMetamodel domainModel = creationContext.getSessionFactory()
|
||||
.getRuntimeMetamodels()
|
||||
.getMappingMetamodel();
|
||||
final EntityPersister entityDescriptor = domainModel.findEntityDescriptor(
|
||||
treatedPath.getTreatTarget().getHibernateEntityName()
|
||||
);
|
||||
conjunctTreatUsages.computeIfAbsent( wrappedPath, p -> new HashSet<>( 1 ) )
|
||||
.addAll( entityDescriptor.getSubclassEntityNames() );
|
||||
final String treatedName = treatedPath.getTreatTarget().getHibernateEntityName();
|
||||
final EntityPersister entityDescriptor = domainModel.findEntityDescriptor( treatedName );
|
||||
conjunctTreatUsages
|
||||
.computeIfAbsent( wrappedPath, p -> new HashSet<>( 1 ) )
|
||||
.addAll( entityDescriptor.getSubclassEntityNames() );
|
||||
return expression;
|
||||
}
|
||||
if ( wrappedPath instanceof DiscriminatorSqmPath ) {
|
||||
|
@ -4869,18 +4867,12 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
}
|
||||
|
||||
private Predicate createTreatTypeRestriction(SqmPath<?> lhs, EntityDomainType<?> treatTarget) {
|
||||
final MappingMetamodel domainModel = creationContext.getSessionFactory()
|
||||
.getRuntimeMetamodels()
|
||||
.getMappingMetamodel();
|
||||
final EntityPersister entityDescriptor = domainModel.findEntityDescriptor( treatTarget.getHibernateEntityName() );
|
||||
final Set<String> subclassEntityNames = entityDescriptor.getSubclassEntityNames();
|
||||
return createTreatTypeRestriction( lhs, subclassEntityNames );
|
||||
}
|
||||
|
||||
private Predicate createTreatTypeRestriction(SqmPath<?> lhs, Set<String> subclassEntityNames) {
|
||||
final MappingMetamodel domainModel = creationContext.getSessionFactory()
|
||||
.getRuntimeMetamodels()
|
||||
.getMappingMetamodel();
|
||||
// Do what visitSelfInterpretingSqmPath does, except for calling preparingReusablePath
|
||||
// as that would register a type usage for the table group that we don't want here
|
||||
final DiscriminatorSqmPath discriminatorSqmPath = (DiscriminatorSqmPath) lhs.type();
|
||||
|
@ -5264,25 +5256,31 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
if ( sqmExpression instanceof SqmParameter ) {
|
||||
return determineValueMapping( (SqmParameter<?>) sqmExpression );
|
||||
}
|
||||
else if ( sqmExpression instanceof SqmPath ) {
|
||||
|
||||
if ( sqmExpression instanceof SqmPath ) {
|
||||
log.debugf( "Determining mapping-model type for SqmPath : %s ", sqmExpression );
|
||||
final MappingMetamodel domainModel = creationContext.getSessionFactory()
|
||||
.getRuntimeMetamodels()
|
||||
.getMappingMetamodel();
|
||||
|
||||
return SqmMappingModelHelper.resolveMappingModelExpressible(
|
||||
sqmExpression,
|
||||
domainModel,
|
||||
fromClauseIndex::findTableGroup
|
||||
);
|
||||
}
|
||||
|
||||
if ( sqmExpression instanceof SqmBooleanExpressionPredicate ) {
|
||||
final SqmBooleanExpressionPredicate expressionPredicate = (SqmBooleanExpressionPredicate) sqmExpression;
|
||||
return determineValueMapping( expressionPredicate.getBooleanExpression(), fromClauseIndex );
|
||||
}
|
||||
|
||||
// The model type of an enum literal is always inferred
|
||||
else if ( sqmExpression instanceof SqmEnumLiteral<?> ) {
|
||||
if ( sqmExpression instanceof SqmEnumLiteral<?> ) {
|
||||
final MappingModelExpressible<?> mappingModelExpressible = resolveInferredType();
|
||||
if ( mappingModelExpressible != null ) {
|
||||
return mappingModelExpressible;
|
||||
}
|
||||
}
|
||||
else if ( sqmExpression instanceof SqmSubQuery<?> ) {
|
||||
|
||||
if ( sqmExpression instanceof SqmSubQuery<?> ) {
|
||||
final SqmSubQuery<?> subQuery = (SqmSubQuery<?>) sqmExpression;
|
||||
final SqmSelectClause selectClause = subQuery.getQuerySpec().getSelectClause();
|
||||
if ( selectClause.getSelections().size() == 1 ) {
|
||||
|
@ -5293,9 +5291,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
}
|
||||
final SqmExpressible<?> selectionNodeType = subQuerySelection.getNodeType();
|
||||
if ( selectionNodeType != null ) {
|
||||
final MappingMetamodel domainModel = creationContext.getSessionFactory()
|
||||
.getRuntimeMetamodels()
|
||||
.getMappingMetamodel();
|
||||
final MappingModelExpressible<?> expressible = domainModel.resolveMappingExpressible(selectionNodeType, this::findTableGroupByPath );
|
||||
|
||||
if ( expressible != null ) {
|
||||
|
@ -5321,6 +5316,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
// We can't determine the type of the expression
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( nodeType instanceof EmbeddedSqmPathSource<?> ) {
|
||||
if ( sqmExpression instanceof SqmBinaryArithmetic<?> ) {
|
||||
final SqmBinaryArithmetic<?> binaryArithmetic = (SqmBinaryArithmetic<?>) sqmExpression;
|
||||
|
@ -5332,9 +5328,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
}
|
||||
}
|
||||
}
|
||||
final MappingMetamodel domainModel = creationContext.getSessionFactory()
|
||||
.getRuntimeMetamodels()
|
||||
.getMappingMetamodel();
|
||||
|
||||
|
||||
final MappingModelExpressible<?> valueMapping = domainModel.resolveMappingExpressible(
|
||||
nodeType,
|
||||
fromClauseIndex::getTableGroup
|
||||
|
@ -5348,7 +5343,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
}
|
||||
|
||||
if ( valueMapping == null ) {
|
||||
// For literals it is totally possible that we can't figure out a mapping type
|
||||
// For literals, it is totally possible that we can't figure out a mapping type
|
||||
if ( sqmExpression instanceof SqmLiteral<?> ) {
|
||||
return null;
|
||||
}
|
||||
|
@ -6785,10 +6780,10 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
finally {
|
||||
inferrableTypeAccessStack.pop();
|
||||
}
|
||||
ComparisonOperator sqmOperator = predicate.getSqmOperator();
|
||||
if ( predicate.isNegated() ) {
|
||||
sqmOperator = sqmOperator.negated();
|
||||
}
|
||||
|
||||
final ComparisonOperator sqmOperator = predicate.isNegated()
|
||||
? predicate.getSqmOperator().negated()
|
||||
: predicate.getSqmOperator();
|
||||
return new ComparisonPredicate( lhs, sqmOperator, rhs, getBooleanType() );
|
||||
}
|
||||
|
||||
|
@ -7099,20 +7094,21 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
@Override
|
||||
public Object visitBooleanExpressionPredicate(SqmBooleanExpressionPredicate predicate) {
|
||||
final Expression booleanExpression = (Expression) predicate.getBooleanExpression().accept( this );
|
||||
if ( booleanExpression instanceof SelfRenderingExpression ) {
|
||||
final Predicate sqlPredicate = new SelfRenderingPredicate( (SelfRenderingExpression) booleanExpression );
|
||||
if ( predicate.isNegated() ) {
|
||||
return new NegatedPredicate( sqlPredicate );
|
||||
}
|
||||
return sqlPredicate;
|
||||
}
|
||||
else {
|
||||
return new BooleanExpressionPredicate(
|
||||
final JdbcMapping jdbcMapping = booleanExpression.getExpressionType().getJdbcMapping( 0 );
|
||||
if ( jdbcMapping.getValueConverter() != null ) {
|
||||
// handle converted booleans (yes-no, etc)
|
||||
return new ComparisonPredicate(
|
||||
booleanExpression,
|
||||
predicate.isNegated(),
|
||||
getBooleanType()
|
||||
ComparisonOperator.EQUAL,
|
||||
new JdbcLiteral<>( jdbcMapping.convertToRelationalValue( !predicate.isNegated() ), jdbcMapping )
|
||||
);
|
||||
}
|
||||
|
||||
return new BooleanExpressionPredicate(
|
||||
booleanExpression,
|
||||
predicate.isNegated(),
|
||||
getBooleanType()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.query.sqm.tree.predicate;
|
||||
|
||||
import org.hibernate.query.sqm.NodeBuilder;
|
||||
import org.hibernate.query.sqm.SqmExpressible;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
|
@ -19,7 +20,11 @@ public abstract class AbstractNegatableSqmPredicate extends AbstractSqmPredicate
|
|||
}
|
||||
|
||||
public AbstractNegatableSqmPredicate(boolean negated, NodeBuilder nodeBuilder) {
|
||||
super( nodeBuilder.getBooleanType(), nodeBuilder );
|
||||
this( nodeBuilder.getBooleanType(), negated, nodeBuilder );
|
||||
}
|
||||
|
||||
public AbstractNegatableSqmPredicate(SqmExpressible<Boolean> type, boolean negated, NodeBuilder nodeBuilder) {
|
||||
super( type, nodeBuilder );
|
||||
this.negated = negated;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import jakarta.persistence.criteria.Expression;
|
|||
|
||||
/**
|
||||
* Represents an expression whose type is boolean, and can therefore be used as a predicate.
|
||||
* E.g. {@code `from Employee e where e.isActive`}
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
|
@ -34,7 +35,7 @@ public class SqmBooleanExpressionPredicate extends AbstractNegatableSqmPredicate
|
|||
SqmExpression<Boolean> booleanExpression,
|
||||
boolean negated,
|
||||
NodeBuilder nodeBuilder) {
|
||||
super( negated, nodeBuilder );
|
||||
super( booleanExpression.getExpressible(), negated, nodeBuilder );
|
||||
|
||||
assert booleanExpression.getNodeType() != null;
|
||||
final Class<?> expressionJavaType = booleanExpression.getNodeType().getExpressibleJavaType().getJavaTypeClass();
|
||||
|
@ -86,4 +87,14 @@ public class SqmBooleanExpressionPredicate extends AbstractNegatableSqmPredicate
|
|||
protected SqmNegatablePredicate createNegatedNode() {
|
||||
return new SqmBooleanExpressionPredicate( booleanExpression, !isNegated(), nodeBuilder() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if ( isNegated() ) {
|
||||
return "SqmBooleanExpressionPredicate( (not) " + booleanExpression + " )";
|
||||
}
|
||||
else {
|
||||
return "SqmBooleanExpressionPredicate( " + booleanExpression + " )";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue