Fix failures with inline value handling mode

This commit is contained in:
Andrea Boriero 2022-01-22 19:54:37 +01:00 committed by Steve Ebersole
parent c8a4546331
commit be49444822
24 changed files with 2203 additions and 288 deletions

View File

@ -29,7 +29,19 @@ public interface EntityIdentifierMapping extends ValueMapping, ModelPart {
*/
IdentifierValue getUnsavedStrategy();
/**
*
*
* @return the entity identifier value
*
* @deprecated Use {@link #getIdentifier(Object)}
*/
@Deprecated
Object getIdentifier(Object entity, SharedSessionContractImplementor session);
Object getIdentifier(Object entity);
void setIdentifier(Object entity, Object id, SharedSessionContractImplementor session);
Object instantiate();
}

View File

@ -124,6 +124,14 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa
return propertyAccess.getGetter().get( entity );
}
@Override
public Object getIdentifier(Object entity) {
if ( entity instanceof HibernateProxy ) {
return ( (HibernateProxy) entity ).getHibernateLazyInitializer().getIdentifier();
}
return propertyAccess.getGetter().get( entity );
}
@Override
public void setIdentifier(Object entity, Object id, SharedSessionContractImplementor session) {
propertyAccess.getSetter().set( entity, id );

View File

@ -200,7 +200,7 @@ public class DiscriminatedAssociationAttributeMapping
final Object discriminator = discriminatorMapping
.getModelPart()
.resolveDiscriminatorForEntityType( concreteMappingType );
final Object identifier = identifierMapping.getIdentifier( value, session );
final Object identifier = identifierMapping.getIdentifier( value );
return new Object[] {
discriminatorMapping.getDiscriminatorPart().disassemble( discriminator, session ),
@ -271,7 +271,7 @@ public class DiscriminatedAssociationAttributeMapping
);
final EntityIdentifierMapping identifierMapping = concreteMappingType.getIdentifierMapping();
final Object identifier = identifierMapping.getIdentifier( value, session );
final Object identifier = identifierMapping.getIdentifier( value );
final Object disassembledKey = discriminatorMapping.getKeyPart().disassemble( identifier, session );
valuesConsumer.consume(
offset + 1,
@ -295,7 +295,7 @@ public class DiscriminatedAssociationAttributeMapping
valueConsumer.consume( disassembledDiscriminator, discriminatorMapping.getDiscriminatorPart() );
final EntityIdentifierMapping identifierMapping = concreteMappingType.getIdentifierMapping();
final Object identifier = identifierMapping.getIdentifier( domainValue, session );
final Object identifier = identifierMapping.getIdentifier( domainValue );
final Object disassembledKey = discriminatorMapping.getKeyPart().disassemble( identifier, session );
valueConsumer.consume( disassembledKey, discriminatorMapping.getKeyPart() );
}

View File

@ -589,10 +589,10 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
}
// If the mapping type has an identifier type, that identifier is the key
if ( modelPart instanceof SingleAttributeIdentifierMapping ) {
return ( (SingleAttributeIdentifierMapping) modelPart ).getIdentifier( targetObject, session );
return ( (SingleAttributeIdentifierMapping) modelPart ).getIdentifier( targetObject );
}
else if ( modelPart instanceof CompositeIdentifierMapping ) {
return ( (CompositeIdentifierMapping) modelPart ).getIdentifier( targetObject, session );
return ( (CompositeIdentifierMapping) modelPart ).getIdentifier( targetObject );
}
// Otherwise, this is a key based on the target object i.e. without id-class
return targetObject;

View File

@ -85,6 +85,14 @@ public class EmbeddedIdentifierMappingImpl
return propertyAccess.getGetter().get( entity );
}
@Override
public Object getIdentifier(Object entity) {
if ( entity instanceof HibernateProxy ) {
return ( (HibernateProxy) entity ).getHibernateLazyInitializer().getIdentifier();
}
return propertyAccess.getGetter().get( entity );
}
@Override
public void setIdentifier(Object entity, Object id, SharedSessionContractImplementor session) {
propertyAccess.getSetter().set( entity, id );

View File

@ -173,7 +173,7 @@ public class IdClassEmbeddable extends AbstractEmbeddableMapping implements Iden
toOneAttributeMapping.getSideNature().inverse()
);
if ( targetPart instanceof EntityIdentifierMapping ) {
propertyValues[i] = ( (EntityIdentifierMapping) targetPart ).getIdentifier( o, session );
propertyValues[i] = ( (EntityIdentifierMapping) targetPart ).getIdentifier( o );
}
else {
propertyValues[i] = o;

View File

@ -202,6 +202,11 @@ public class NonAggregatedIdentifierMappingImpl extends AbstractCompositeIdentif
@Override
public Object getIdentifier(Object entity, SharedSessionContractImplementor session) {
return getIdentifier( entity );
}
@Override
public Object getIdentifier(Object entity) {
if ( hasContainingClass() ) {
final Object id = identifierValueMapper.getRepresentationStrategy().getInstantiator().instantiate(
null,
@ -230,7 +235,7 @@ public class NonAggregatedIdentifierMappingImpl extends AbstractCompositeIdentif
toOneAttributeMapping.getSideNature().inverse()
);
if ( targetPart instanceof EntityIdentifierMapping ) {
propertyValues[i] = ( (EntityIdentifierMapping) targetPart ).getIdentifier( o, session );
propertyValues[i] = ( (EntityIdentifierMapping) targetPart ).getIdentifier( o );
}
else {
propertyValues[i] = o;

View File

@ -5046,7 +5046,7 @@ public abstract class AbstractEntityPersister
@Override
public Object getIdentifier(Object entity, SharedSessionContractImplementor session) {
return identifierMapping.getIdentifier( entity, session );
return identifierMapping.getIdentifier( entity );
}
@Override
@ -6508,7 +6508,7 @@ public abstract class AbstractEntityPersister
return null;
}
final EntityIdentifierMapping identifierMapping = getIdentifierMapping();
final Object identifier = identifierMapping.getIdentifier( value, session );
final Object identifier = identifierMapping.getIdentifier( value );
return identifierMapping.disassemble( identifier, session );
}
@ -6541,7 +6541,7 @@ public abstract class AbstractEntityPersister
identifier = null;
}
else {
identifier = identifierMapping.disassemble( identifierMapping.getIdentifier( value, session ), session );
identifier = identifierMapping.disassemble( identifierMapping.getIdentifier( value ), session );
}
return identifierMapping.forEachDisassembledJdbcValue(
identifier,

View File

@ -365,7 +365,7 @@ public class SqmUtil {
final EntityIdentifierMapping identifierMapping = (EntityIdentifierMapping) parameterType;
final EntityMappingType entityMapping = identifierMapping.findContainingEntityMapping();
if ( entityMapping.getRepresentationStrategy().getInstantiator().isInstance( bindValue, session.getFactory() ) ) {
bindValue = identifierMapping.getIdentifier( bindValue, session );
bindValue = identifierMapping.getIdentifier( bindValue );
}
}
else if ( parameterType instanceof EntityMappingType ) {
@ -373,7 +373,7 @@ public class SqmUtil {
final EntityMappingType entityMapping = identifierMapping.findContainingEntityMapping();
parameterType = identifierMapping;
if ( entityMapping.getRepresentationStrategy().getInstantiator().isInstance( bindValue, session.getFactory() ) ) {
bindValue = identifierMapping.getIdentifier( bindValue, session );
bindValue = identifierMapping.getIdentifier( bindValue );
}
}
else if ( parameterType instanceof ToOneAttributeMapping ) {

View File

@ -87,8 +87,10 @@ import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
import org.hibernate.metamodel.model.domain.BasicDomainType;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.metamodel.model.domain.internal.BasicSqmPathSource;
import org.hibernate.metamodel.model.domain.internal.CompositeSqmPathSource;
import org.hibernate.metamodel.model.domain.internal.DiscriminatorSqmPath;
import org.hibernate.metamodel.model.domain.internal.EntityTypeImpl;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable;
@ -3934,31 +3936,12 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
if ( inferableExpressible instanceof ConvertibleModelPart ) {
final ConvertibleModelPart convertibleModelPart = (ConvertibleModelPart) inferableExpressible;
final BasicValueConverter valueConverter = convertibleModelPart.getValueConverter();
if ( valueConverter != null ) {
final Object literalValue = literal.getLiteralValue();
final Object sqlLiteralValue;
if ( valueConverter.getDomainJavaType().getJavaTypeClass().isInstance( literalValue ) ) {
sqlLiteralValue = valueConverter.toRelationalValue( literalValue );
}
else {
if ( !valueConverter.getRelationalJavaType().getJavaTypeClass().isInstance( literalValue ) ) {
throw new SqlTreeCreationException(
String.format(
Locale.ROOT,
"QueryLiteral type [`%s`] did not match domain Java-type [`%s`] nor JDBC Java-type [`%s`]",
literalValue.getClass(),
valueConverter.getDomainJavaType().getJavaTypeClass().getName(),
valueConverter.getRelationalJavaType().getJavaTypeClass().getName()
)
);
}
sqlLiteralValue = literalValue;
}
return new QueryLiteral<>( sqlLiteralValue, convertibleModelPart );
if ( convertibleModelPart.getValueConverter() != null ) {
return new QueryLiteral<>(
literal.getLiteralValue(),
convertibleModelPart
);
}
}
// Special case for when we create an entity literal through the JPA CriteriaBuilder.literal API
@ -3998,6 +3981,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return new EntityTypeLiteral( mappingDescriptor );
}
final MappingModelExpressible<?> expressible;
final MappingModelExpressible<?> localExpressible = SqmMappingModelHelper.resolveMappingModelExpressible(
literal,
@ -4021,6 +4005,13 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
}
if ( expressible instanceof EntityIdentifierMapping && literal.getNodeType() instanceof EntityTypeImpl ) {
return new QueryLiteral<>(
( (EntityIdentifierMapping) expressible ).getIdentifier( literal.getLiteralValue() ),
(BasicValuedMapping) expressible
);
}
if ( expressible instanceof BasicValuedMapping ) {
return new QueryLiteral<>(
literal.getLiteralValue(),
@ -4028,7 +4019,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
);
}
// Handling other values might seem unnecessary, but with JPA Criteria it is totally possible to have such literals
else if ( expressible instanceof EmbeddableValuedModelPart ) {
if ( expressible instanceof EmbeddableValuedModelPart ) {
final EmbeddableValuedModelPart embeddableValuedModelPart = (EmbeddableValuedModelPart) expressible;
final List<Expression> list = new ArrayList<>( embeddableValuedModelPart.getJdbcTypeCount() );
embeddableValuedModelPart.forEachJdbcValue(
@ -4042,46 +4033,41 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
else if ( expressible instanceof EntityValuedModelPart ) {
final EntityValuedModelPart entityValuedModelPart = (EntityValuedModelPart) expressible;
final Object associationKey;
final ModelPart associationKeyPart;
if ( entityValuedModelPart instanceof Association ) {
final Association association = (Association) entityValuedModelPart;
final ForeignKeyDescriptor foreignKeyDescriptor = association.getForeignKeyDescriptor();
associationKey = foreignKeyDescriptor.getAssociationKeyFromSide(
literal.getLiteralValue(),
association.getSideNature(),
null
);
associationKeyPart = foreignKeyDescriptor.getPart( association.getSideNature() );
}
else {
final EntityIdentifierMapping identifierMapping = entityValuedModelPart.getEntityMappingType()
.getIdentifierMapping();
associationKeyPart = identifierMapping;
associationKey = identifierMapping.getIdentifier(
literal.getLiteralValue(),
null
);
}
if ( associationKeyPart instanceof BasicValuedMapping ) {
final EntityIdentifierMapping identifierMapping = entityValuedModelPart.getEntityMappingType()
.getIdentifierMapping();
final Object associationKey = identifierMapping.getIdentifier( literal.getLiteralValue() );
if ( identifierMapping instanceof BasicValuedMapping ) {
return new QueryLiteral<>(
associationKey,
(BasicValuedMapping) associationKeyPart
(BasicValuedMapping) identifierMapping
);
}
else {
final List<Expression> list = new ArrayList<>( associationKeyPart.getJdbcTypeCount() );
associationKeyPart.forEachJdbcValue(
final List<Expression> list = new ArrayList<>( identifierMapping.getJdbcTypeCount() );
identifierMapping.forEachJdbcValue(
associationKey,
null,
(selectionIndex, value, jdbcMapping)
-> list.add( new QueryLiteral<>( value, (BasicValuedMapping) jdbcMapping ) ),
null
);
return new SqlTuple( list, associationKeyPart );
return new SqlTuple( list, identifierMapping );
}
}
else {
if ( literal instanceof SqmLiteral ) {
return new QueryLiteral<>(
literal.getLiteralValue(),
creationContext.getSessionFactory()
.getTypeConfiguration()
.getBasicTypeRegistry()
.getRegisteredType(
( (BasicSqmPathSource) literal.getNodeType() ).getSqmPathType()
.getJavaType()
.getName()
)
);
}
throw new NotYetImplementedFor6Exception(
expressible == null ? literal.getLiteralValue().getClass() : expressible.getClass()
);

View File

@ -8,12 +8,15 @@ package org.hibernate.sql.ast.tree.expression;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Locale;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.ConvertibleModelPart;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.SqlTreeCreationException;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.exec.spi.ExecutionContext;
@ -35,8 +38,43 @@ public class QueryLiteral<T> implements Literal, DomainResultProducer<T> {
private final BasicValuedMapping type;
public QueryLiteral(T value, BasicValuedMapping type) {
this.value = value;
this.type = type;
if ( type instanceof ConvertibleModelPart ) {
final ConvertibleModelPart convertibleModelPart = (ConvertibleModelPart) type;
final BasicValueConverter valueConverter = convertibleModelPart.getValueConverter();
if ( valueConverter != null ) {
final Object literalValue = value;
final Object sqlLiteralValue;
if ( valueConverter.getDomainJavaType().getJavaTypeClass().isInstance( literalValue ) ) {
sqlLiteralValue = valueConverter.toRelationalValue( literalValue );
}
else {
if ( !valueConverter.getRelationalJavaType().getJavaTypeClass().isInstance( literalValue ) ) {
throw new SqlTreeCreationException(
String.format(
Locale.ROOT,
"QueryLiteral type [`%s`] did not match domain Java-type [`%s`] nor JDBC Java-type [`%s`]",
literalValue.getClass(),
valueConverter.getDomainJavaType().getJavaTypeClass().getName(),
valueConverter.getRelationalJavaType().getJavaTypeClass().getName()
)
);
}
sqlLiteralValue = literalValue;
}
this.value = (T) sqlLiteralValue;
this.type = convertibleModelPart;
}
else {
this.value = value;
this.type = type;
}
}
else {
this.value = value;
this.type = type;
}
}
@Override

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.type.descriptor;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
@ -196,7 +197,15 @@ public final class DateTimeUtils {
}
public static void appendAsTimestampWithMicros(SqlAppender appender, Date date, TimeZone jdbcTimeZone) {
final SimpleDateFormat simpleDateFormat = TIMESTAMP_WITH_MICROS_FORMAT.get();
final SimpleDateFormat simpleDateFormat;
if ( date instanceof Timestamp ) {
// java.sql.Timestamp supports micro sec
simpleDateFormat = TIMESTAMP_WITH_MICROS_FORMAT.get();
}
else {
// java.util.Date supports only milli sec
simpleDateFormat = TIMESTAMP_WITH_MILLIS_FORMAT.get();
}
final TimeZone originalTimeZone = simpleDateFormat.getTimeZone();
try {
simpleDateFormat.setTimeZone( jdbcTimeZone );
@ -231,7 +240,8 @@ public final class DateTimeUtils {
}
public static void appendAsTimestampWithMicros(SqlAppender appender, Calendar calendar, TimeZone jdbcTimeZone) {
final SimpleDateFormat simpleDateFormat = TIMESTAMP_WITH_MICROS_FORMAT.get();
// it is possible to use micro sec resolution with java.util.Date
final SimpleDateFormat simpleDateFormat = TIMESTAMP_WITH_MILLIS_FORMAT.get();
final TimeZone originalTimeZone = simpleDateFormat.getTimeZone();
try {
simpleDateFormat.setTimeZone( jdbcTimeZone );

View File

@ -6,7 +6,6 @@
*/
package org.hibernate.type.descriptor.jdbc.spi;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaType;
@ -29,6 +28,18 @@ public abstract class BasicJdbcLiteralFormatter extends AbstractJdbcLiteralForma
return (X) value;
}
if ( !getJavaType().isInstance( value ) ) {
final Object coerce = getJavaType().coerce( value, wrapperOptions.getSession() );
if ( unwrapType.isInstance( coerce ) ) {
return (X) coerce;
}
return (X) getJavaType().unwrap(
coerce,
unwrapType,
wrapperOptions
);
}
return (X) getJavaType().unwrap( value, unwrapType, wrapperOptions );
}
}

View File

@ -1,9 +1,18 @@
/*
* 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.jpa.compliance;
import java.util.List;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
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;
@ -22,7 +31,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@Jpa(
annotatedClasses = ModulusTest.Person.class
annotatedClasses = ModulusTest.Person.class,
properties = @Setting( name = AvailableSettings.JPA_QUERY_COMPLIANCE, value = "true")
)
public class ModulusTest {

View File

@ -10,7 +10,21 @@ import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collections;
import java.util.List;
import jakarta.persistence.EntityManager;
import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.DerbyDialect;
import org.hibernate.orm.test.jpa.metamodel.AbstractMetamodelSpecificTest;
import org.hibernate.orm.test.jpa.metamodel.Phone;
import org.hibernate.orm.test.jpa.metamodel.Product;
import org.hibernate.orm.test.jpa.metamodel.Product_;
import org.hibernate.query.Query;
import org.hibernate.testing.TestForIssue;
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 jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
@ -18,21 +32,7 @@ import jakarta.persistence.criteria.ParameterExpression;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.DerbyDialect;
import org.hibernate.query.Query;
import org.hibernate.orm.test.jpa.metamodel.AbstractMetamodelSpecificTest;
import org.hibernate.orm.test.jpa.metamodel.Phone;
import org.hibernate.orm.test.jpa.metamodel.Product;
import org.hibernate.orm.test.jpa.metamodel.Product_;
import org.hibernate.testing.TestForIssue;
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.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
@ -47,175 +47,187 @@ public class ExpressionsTest extends AbstractMetamodelSpecificTest {
public void prepareTestData() {
builder = entityManagerFactory().getCriteriaBuilder();
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
Product product = new Product();
product.setId( "product1" );
product.setPrice( 1.23d );
product.setQuantity( 2 );
product.setPartNumber( ((long)Integer.MAX_VALUE) + 1 );
product.setRating( 1.999f );
product.setSomeBigInteger( BigInteger.valueOf( 987654321 ) );
product.setSomeBigDecimal( BigDecimal.valueOf( 987654.32 ) );
em.persist( product );
em.getTransaction().commit();
em.close();
doInJPA(
this::entityManagerFactory,
entityManager -> {
Product product = new Product();
product.setId( "product1" );
product.setPrice( 1.23d );
product.setQuantity( 2 );
product.setPartNumber( ( (long) Integer.MAX_VALUE ) + 1 );
product.setRating( 1.999f );
product.setSomeBigInteger( BigInteger.valueOf( 987654321 ) );
product.setSomeBigDecimal( BigDecimal.valueOf( 987654.32 ) );
entityManager.persist( product );
}
);
}
@AfterEach
public void cleanupTestData() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
em.remove( em.find( Product.class, "product1" ) );
em.getTransaction().commit();
em.close();
doInJPA(
this::entityManagerFactory,
entityManager -> {
entityManager.createQuery( "delete from Product" ).executeUpdate();
}
);
}
@Test
public void testEmptyConjunction() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
CriteriaQuery<Product> criteria = builder.createQuery( Product.class );
criteria.from( Product.class );
criteria.where( builder.and() );
List<Product> result = em.createQuery( criteria ).getResultList();
assertEquals( 1, result.size() );
em.getTransaction().commit();
em.close();
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<Product> criteria = builder.createQuery( Product.class );
criteria.from( Product.class );
criteria.where( builder.and() );
List<Product> result = entityManager.createQuery( criteria ).getResultList();
assertEquals( 1, result.size() );
}
);
}
@Test
@TestForIssue( jiraKey = "HHH-6876" )
@TestForIssue(jiraKey = "HHH-6876")
public void testEmptyInList() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
CriteriaQuery<Product> criteria = builder.createQuery( Product.class );
Root<Product> from = criteria.from( Product.class );
criteria.where( from.get( Product_.partNumber ).in() ); // empty IN list
List<Product> result = em.createQuery( criteria ).getResultList();
assertEquals( 0, result.size() );
em.getTransaction().commit();
em.close();
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<Product> criteria = builder.createQuery( Product.class );
Root<Product> from = criteria.from( Product.class );
criteria.where( from.get( Product_.partNumber ).in() ); // empty IN list
List<Product> result = entityManager.createQuery( criteria ).getResultList();
assertEquals( 0, result.size() );
}
);
}
@Test
public void testEmptyConjunctionIsTrue() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
CriteriaQuery<Product> criteria = builder.createQuery( Product.class );
criteria.from( Product.class );
criteria.where( builder.isTrue( builder.and() ) );
List<Product> result = em.createQuery( criteria ).getResultList();
assertEquals( 1, result.size() );
em.getTransaction().commit();
em.close();
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<Product> criteria = builder.createQuery( Product.class );
criteria.from( Product.class );
criteria.where( builder.isTrue( builder.and() ) );
List<Product> result = entityManager.createQuery( criteria ).getResultList();
assertEquals( 1, result.size() );
}
);
}
@Test
public void testEmptyConjunctionIsFalse() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
CriteriaQuery<Product> criteria = builder.createQuery( Product.class );
criteria.from( Product.class );
criteria.where( builder.isFalse( builder.and() ) );
List<Product> result = em.createQuery( criteria ).getResultList();
assertEquals( 0, result.size() );
em.getTransaction().commit();
em.close();
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<Product> criteria = builder.createQuery( Product.class );
criteria.from( Product.class );
criteria.where( builder.isFalse( builder.and() ) );
List<Product> result = entityManager.createQuery( criteria ).getResultList();
assertEquals( 0, result.size() );
}
);
}
@Test
public void testEmptyDisjunction() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
CriteriaQuery<Product> criteria = builder.createQuery( Product.class );
criteria.from( Product.class );
criteria.where( builder.disjunction() );
List<Product> result = em.createQuery( criteria ).getResultList();
assertEquals( 0, result.size() );
em.getTransaction().commit();
em.close();
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<Product> criteria = builder.createQuery( Product.class );
criteria.from( Product.class );
criteria.where( builder.disjunction() );
List<Product> result = entityManager.createQuery( criteria ).getResultList();
assertEquals( 0, result.size() );
}
);
}
@Test
public void testEmptyDisjunctionIsTrue() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
CriteriaQuery<Product> criteria = builder.createQuery( Product.class );
criteria.from( Product.class );
criteria.where( builder.isTrue( builder.disjunction() ) );
List<Product> result = em.createQuery( criteria ).getResultList();
assertEquals( 0, result.size() );
em.getTransaction().commit();
em.close();
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<Product> criteria = builder.createQuery( Product.class );
criteria.from( Product.class );
criteria.where( builder.isTrue( builder.disjunction() ) );
List<Product> result = entityManager.createQuery( criteria ).getResultList();
assertEquals( 0, result.size() );
}
);
}
@Test
public void testEmptyDisjunctionIsFalse() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
CriteriaQuery<Product> criteria = builder.createQuery( Product.class );
criteria.from( Product.class );
criteria.where( builder.isFalse( builder.disjunction() ) );
List<Product> result = em.createQuery( criteria ).getResultList();
assertEquals( 1, result.size() );
em.getTransaction().commit();
em.close();
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<Product> criteria = builder.createQuery( Product.class );
criteria.from( Product.class );
criteria.where( builder.isFalse( builder.disjunction() ) );
List<Product> result = entityManager.createQuery( criteria ).getResultList();
assertEquals( 1, result.size() );
}
);
}
@Test
public void testDiff() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
CriteriaQuery<Integer> criteria = builder.createQuery( Integer.class );
criteria.from( Product.class );
criteria.select( builder.diff( builder.literal( 5 ), builder.literal( 2 ) ) );
Integer result = em.createQuery( criteria ).getSingleResult();
assertEquals( Integer.valueOf( 3 ), result );
em.getTransaction().commit();
em.close();
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<Integer> criteria = builder.createQuery( Integer.class );
criteria.from( Product.class );
criteria.select( builder.diff( builder.literal( 5 ), builder.literal( 2 ) ) );
Integer result = entityManager.createQuery( criteria ).getSingleResult();
assertEquals( Integer.valueOf( 3 ), result );
}
);
}
@Test
public void testDiffWithQuotient() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
CriteriaQuery<Number> criteria = builder.createQuery( Number.class );
criteria.from( Product.class );
criteria.select(
builder.quot(
builder.diff(
builder.literal( BigDecimal.valueOf( 2.0 ) ),
builder.literal( BigDecimal.valueOf( 1.0 ) )
),
BigDecimal.valueOf( 2.0 )
)
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<Number> criteria = builder.createQuery( Number.class );
criteria.from( Product.class );
criteria.select(
builder.quot(
builder.diff(
builder.literal( BigDecimal.valueOf( 2.0 ) ),
builder.literal( BigDecimal.valueOf( 1.0 ) )
),
BigDecimal.valueOf( 2.0 )
)
);
Number result = entityManager.createQuery( criteria ).getSingleResult();
assertEquals( 0.5d, result.doubleValue(), 0.1d );
}
);
Number result = em.createQuery( criteria ).getSingleResult();
assertEquals(0.5d, result.doubleValue(), 0.1d);
em.getTransaction().commit();
em.close();
}
@Test
public void testSumWithQuotient() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
CriteriaQuery<Number> criteria = builder.createQuery( Number.class );
criteria.from( Product.class );
criteria.select(
builder.quot(
builder.sum(
builder.literal( BigDecimal.valueOf( 0.0 ) ),
builder.literal( BigDecimal.valueOf( 1.0 ) )
),
BigDecimal.valueOf( 2.0 )
)
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<Number> criteria = builder.createQuery( Number.class );
criteria.from( Product.class );
criteria.select(
builder.quot(
builder.sum(
builder.literal( BigDecimal.valueOf( 0.0 ) ),
builder.literal( BigDecimal.valueOf( 1.0 ) )
),
BigDecimal.valueOf( 2.0 )
)
);
Number result = entityManager.createQuery( criteria ).getSingleResult();
assertEquals( 0.5d, result.doubleValue(), 0.1d );
}
);
Number result = em.createQuery( criteria ).getSingleResult();
assertEquals(0.5d, result.doubleValue(), 0.1d);
em.getTransaction().commit();
em.close();
}
@Test
@ -225,55 +237,56 @@ public class ExpressionsTest extends AbstractMetamodelSpecificTest {
"that is always rendered as literal. Since numeric literal + parameter arithmetic is rare, we skip this for now.")
@SkipForDialect(dialectClass = DB2Dialect.class, reason = "Same reason as for Derby")
public void testQuotientAndMultiply() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
CriteriaQuery<Number> criteria = builder.createQuery( Number.class );
criteria.from( Product.class );
criteria.select(
builder.quot(
builder.prod(
builder.literal( BigDecimal.valueOf( 10.0 ) ),
builder.literal( BigDecimal.valueOf( 5.0 ) )
),
BigDecimal.valueOf( 2.0 )
)
);
Number result = em.createQuery( criteria ).getSingleResult();
assertEquals(25.0d, result.doubleValue(), 0.1d);
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<Number> criteria = builder.createQuery( Number.class );
criteria.from( Product.class );
criteria.select(
builder.quot(
builder.prod(
builder.literal( BigDecimal.valueOf( 10.0 ) ),
builder.literal( BigDecimal.valueOf( 5.0 ) )
),
BigDecimal.valueOf( 2.0 )
)
);
Number result = entityManager.createQuery( criteria ).getSingleResult();
assertEquals( 25.0d, result.doubleValue(), 0.1d );
criteria.select(
builder.prod(
builder.quot(
builder.literal( BigDecimal.valueOf( 10.0 ) ),
builder.literal( BigDecimal.valueOf( 5.0 ) )
),
BigDecimal.valueOf( 2.0 )
)
criteria.select(
builder.prod(
builder.quot(
builder.literal( BigDecimal.valueOf( 10.0 ) ),
builder.literal( BigDecimal.valueOf( 5.0 ) )
),
BigDecimal.valueOf( 2.0 )
)
);
result = entityManager.createQuery( criteria ).getSingleResult();
assertEquals( 4.0d, result.doubleValue(), 0.1d );
}
);
result = em.createQuery( criteria ).getSingleResult();
assertEquals(4.0d, result.doubleValue(), 0.1d);
em.getTransaction().commit();
em.close();
}
@Test
public void testParameterReuse() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
CriteriaQuery<Product> criteria = em.getCriteriaBuilder().createQuery( Product.class );
Root<Product> from = criteria.from( Product.class );
ParameterExpression<String> param = em.getCriteriaBuilder().parameter( String.class );
Predicate predicate = em.getCriteriaBuilder().equal( from.get( Product_.id ), param );
Predicate predicate2 = em.getCriteriaBuilder().equal( from.get( Product_.name ), param );
criteria.where( em.getCriteriaBuilder().or( predicate, predicate2 ) );
assertEquals( 1, criteria.getParameters().size() );
TypedQuery<Product> query = em.createQuery( criteria );
int hqlParamCount = countGeneratedParameters( query.unwrap( Query.class ) );
assertEquals( 1, hqlParamCount );
query.setParameter( param, "abc" ).getResultList();
em.getTransaction().commit();
em.close();
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<Product> criteria = entityManager.getCriteriaBuilder().createQuery( Product.class );
Root<Product> from = criteria.from( Product.class );
ParameterExpression<String> param = entityManager.getCriteriaBuilder().parameter( String.class );
Predicate predicate = entityManager.getCriteriaBuilder().equal( from.get( Product_.id ), param );
Predicate predicate2 = entityManager.getCriteriaBuilder().equal( from.get( Product_.name ), param );
criteria.where( entityManager.getCriteriaBuilder().or( predicate, predicate2 ) );
assertEquals( 1, criteria.getParameters().size() );
TypedQuery<Product> query = entityManager.createQuery( criteria );
int hqlParamCount = countGeneratedParameters( query.unwrap( Query.class ) );
assertEquals( 1, hqlParamCount );
query.setParameter( param, "abc" ).getResultList();
}
);
}
private int countGeneratedParameters(Query<?> query) {
@ -282,55 +295,60 @@ public class ExpressionsTest extends AbstractMetamodelSpecificTest {
@Test
public void testInExplicitTupleList() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
CriteriaQuery<Product> criteria = builder.createQuery( Product.class );
Root<Product> from = criteria.from( Product.class );
criteria.where( from.get( Product_.partNumber ).in( Collections.singletonList( ((long)Integer.MAX_VALUE) + 1 ) ) );
List<Product> result = em.createQuery( criteria ).getResultList();
assertEquals( 1, result.size() );
em.getTransaction().commit();
em.close();
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<Product> criteria = builder.createQuery( Product.class );
Root<Product> from = criteria.from( Product.class );
criteria.where( from.get( Product_.partNumber )
.in( Collections.singletonList( ( (long) Integer.MAX_VALUE ) + 1 ) ) );
List<Product> result = entityManager.createQuery( criteria ).getResultList();
assertEquals( 1, result.size() );
}
);
}
@Test
public void testInExplicitTupleListVarargs() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
CriteriaQuery<Product> criteria = builder.createQuery( Product.class );
Root<Product> from = criteria.from( Product.class );
criteria.where( from.get( Product_.partNumber ).in( ((long)Integer.MAX_VALUE) + 1 ) );
List<Product> result = em.createQuery( criteria ).getResultList();
assertEquals( 1, result.size() );
em.getTransaction().commit();
em.close();
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<Product> criteria = builder.createQuery( Product.class );
Root<Product> from = criteria.from( Product.class );
criteria.where( from.get( Product_.partNumber ).in( ( (long) Integer.MAX_VALUE ) + 1 ) );
List<Product> result = entityManager.createQuery( criteria ).getResultList();
assertEquals( 1, result.size() );
}
);
}
@Test
public void testInExpressionVarargs() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
CriteriaQuery<Product> criteria = builder.createQuery( Product.class );
Root<Product> from = criteria.from( Product.class );
criteria.where( from.get( Product_.partNumber ).in( from.get( Product_.partNumber ) ) );
List<Product> result = em.createQuery( criteria ).getResultList();
assertEquals( 1, result.size() );
em.getTransaction().commit();
em.close();
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<Product> criteria = builder.createQuery( Product.class );
Root<Product> from = criteria.from( Product.class );
criteria.where( from.get( Product_.partNumber ).in( from.get( Product_.partNumber ) ) );
List<Product> result = entityManager.createQuery( criteria ).getResultList();
assertEquals( 1, result.size() );
}
);
}
@Test
public void testJoinedElementCollectionValuesInTupleList() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
CriteriaQuery<Phone> criteria = builder.createQuery( Phone.class );
Root<Phone> from = criteria.from( Phone.class );
criteria.where(
from.join( "types" )
.in( Collections.singletonList( Phone.Type.WORK ) )
doInJPA(
this::entityManagerFactory,
entityManager -> {
CriteriaQuery<Phone> criteria = builder.createQuery( Phone.class );
Root<Phone> from = criteria.from( Phone.class );
criteria.where(
from.join( "types" )
.in( Collections.singletonList( Phone.Type.WORK ) )
);
entityManager.createQuery( criteria ).getResultList();
}
);
em.createQuery( criteria ).getResultList();
em.getTransaction().commit();
em.close();
}
}

View File

@ -0,0 +1,243 @@
/*
* 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.jpa.criteria.valuehandlingmode.inline;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
@Jpa(
annotatedClasses = {
ComponentInWhereClauseTest.Employee.class,
ComponentInWhereClauseTest.Project.class,
ComponentInWhereClauseTest.Person.class
}
, properties = @Setting(name = AvailableSettings.CRITERIA_VALUE_HANDLING_MODE, value = "inline")
)
public class ComponentInWhereClauseTest {
private Projects projects;
@BeforeAll
public void setUp(EntityManagerFactoryScope scope) {
scope.inTransaction(
entityManager -> {
projects = new Projects();
projects.addPreviousProject( new Project( "First" ) );
projects.addPreviousProject( new Project( "Second" ) );
projects.setCurrentProject( new Project( "Third" ) );
ContactDetail contactDetail = new ContactDetail();
contactDetail.setEmail( "abc@mail.org" );
contactDetail.addPhone( new Phone( "+4411111111" ) );
final Employee employee = new Employee();
employee.setProjects( projects );
employee.setContactDetail( contactDetail );
entityManager.persist( employee );
final Person person = new Person();
person.setInformation( new Information() );
ContactDetail infoContactDetail = new ContactDetail();
infoContactDetail.setEmail( "xyz@mail.org" );
infoContactDetail.addPhone( new Phone( "999-999-9999" ) );
person.getInformation().setInfoContactDetail( infoContactDetail );
entityManager.persist( person );
}
);
}
@Test
public void testInExpressionForTheManyToOnePropertyOfAComponent(EntityManagerFactoryScope scope) {
scope.inTransaction(
entityManager -> {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Employee> query = builder.createQuery( Employee.class );
Root<Employee> root = query.from( Employee.class );
query.where( root.get( "projects" )
.get( "currentProject" )
.in( projects.getCurrentProject() ) );
final List<Employee> results = entityManager.createQuery( query ).getResultList();
assertThat( results.size(), is( 1 ) );
}
);
}
@MappedSuperclass
public static abstract class AbstractEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
protected Long id;
public Long getId() {
return id;
}
}
@Entity(name = "Employee")
@Table(name = "EMPLOYEE")
public static class Employee extends AbstractEntity {
@Embedded
private Projects projects;
@Embedded
private ContactDetail contactDetail;
public void setProjects(Projects projects) {
this.projects = projects;
}
public void setContactDetail(ContactDetail contactDetail) {
this.contactDetail = contactDetail;
}
}
@Embeddable
public static class ContactDetail {
private String email;
@ElementCollection
private List<Phone> phones = new ArrayList<>();
public void addPhone(Phone phone) {
this.phones.add( phone );
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
@Embeddable
public static class Projects {
@OneToMany(cascade = CascadeType.PERSIST)
private Set<Project> previousProjects = new HashSet<>();
@ManyToOne(cascade = CascadeType.PERSIST)
private Project currentProject;
public void addPreviousProject(Project project) {
this.previousProjects.add( project );
}
public Set<Project> getPreviousProjects() {
return previousProjects;
}
public Project getCurrentProject() {
return currentProject;
}
public void setCurrentProject(Project project) {
this.currentProject = project;
}
}
@Entity(name = "Project")
@Table(name = "PROJECT")
public static class Project extends AbstractEntity {
private String name;
public Project() {
}
public Project(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
@Embeddable
public static class Phone {
@Column(name = "phone_number")
private String number;
public Phone() {
}
public Phone(String number) {
this.number = number;
}
public String getNumber() {
return this.number;
}
}
@Entity(name = "Person")
@Table(name = "PERSON")
public static class Person extends AbstractEntity {
@Embedded
private Information information;
public Information getInformation() {
return information;
}
public void setInformation(Information information) {
this.information = information;
}
}
@Embeddable
public static class Information {
@Embedded
private ContactDetail infoContactDetail;
public ContactDetail getInfoContactDetail() {
return infoContactDetail;
}
public void setInfoContactDetail(ContactDetail infoContactDetail) {
this.infoContactDetail = infoContactDetail;
}
}
}

View File

@ -0,0 +1,99 @@
/*
* 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.jpa.criteria.valuehandlingmode.inline;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.Date;
import java.util.List;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import static org.junit.jupiter.api.Assertions.assertEquals;
@Jpa(
annotatedClasses = DateCompositeCustomTypeTest.Payment.class
, properties = @Setting(name = AvailableSettings.CRITERIA_VALUE_HANDLING_MODE, value = "inline")
)
public class DateCompositeCustomTypeTest {
@Test
public void testDateCompositeCustomType(EntityManagerFactoryScope scope) {
final Date date = Date.from( Instant.now() );
final Payment payment = new Payment();
payment.setAmount( new BigDecimal( 1000 ) );
payment.setDate( date );
scope.inTransaction(
entityManager -> {
entityManager.persist( payment );
CriteriaQuery<Payment> criteria = entityManager.getCriteriaBuilder().createQuery( Payment.class );
Root<Payment> rp = criteria.from( Payment.class );
Predicate predicate = entityManager.getCriteriaBuilder().equal( rp.get( "date" ), date );
criteria.where( predicate );
TypedQuery<Payment> q = entityManager.createQuery( criteria );
List<Payment> payments = q.getResultList();
assertEquals( 1, payments.size() );
}
);
}
@Entity
@Table(name = "crit_basic_payment")
public class Payment {
private Long id;
private BigDecimal amount;
private Date date;
@Id
@GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
@Column(name = "payment_date")
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
}

View File

@ -0,0 +1,128 @@
/*
* 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.jpa.criteria.valuehandlingmode.inline;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.Hibernate;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityGraph;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Root;
import static org.junit.jupiter.api.Assertions.assertTrue;
@Jpa(
annotatedClasses = {
EntityGraphTest.Bar.class,
EntityGraphTest.Baz.class,
EntityGraphTest.Foo.class
}
, properties = @Setting(name = AvailableSettings.CRITERIA_VALUE_HANDLING_MODE, value = "inline")
)
public class EntityGraphTest {
@Test
public void loadIsMemeberQueriedCollection(EntityManagerFactoryScope scope) {
Integer id = scope.fromTransaction(
entityManager -> {
Bar bar = new Bar();
entityManager.persist( bar );
Foo foo = new Foo();
foo.bar = bar;
bar.foos.add( foo );
entityManager.persist( foo );
return foo.id;
}
);
scope.inTransaction(
entityManager -> {
Foo foo = entityManager.find( Foo.class, id );
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Bar> cq = cb.createQuery( Bar.class );
Root<Bar> from = cq.from( Bar.class );
Expression<Set<Foo>> foos = from.get( "foos" );
cq.where( cb.isMember( foo, foos ) );
TypedQuery<Bar> query = entityManager.createQuery( cq );
EntityGraph<Bar> barGraph = entityManager.createEntityGraph( Bar.class );
barGraph.addAttributeNodes( "foos" );
query.setHint( "javax.persistence.loadgraph", barGraph );
Bar result = query.getSingleResult();
assertTrue( Hibernate.isInitialized( result ) );
assertTrue( Hibernate.isInitialized( result.foos ) );
}
);
}
@Entity
@Table(name = "foo")
public static class Foo {
@Id
@GeneratedValue
public Integer id;
@ManyToOne(fetch = FetchType.LAZY)
public Bar bar;
@ManyToOne(fetch = FetchType.LAZY)
public Baz baz;
}
@Entity
@Table(name = "bar")
public static class Bar {
@Id
@GeneratedValue
public Integer id;
@OneToMany(mappedBy = "bar")
public Set<Foo> foos = new HashSet<>();
}
@Entity
@Table(name = "baz")
public static class Baz {
@Id
@GeneratedValue
public Integer id;
@OneToMany(mappedBy = "baz")
public Set<Foo> foos = new HashSet<>();
}
}

View File

@ -0,0 +1,471 @@
/*
* 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.jpa.criteria.valuehandlingmode.inline;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Date;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.metamodel.model.domain.JpaMetamodel;
import org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl;
import org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.Assert;
import org.junit.jupiter.api.Test;
import jakarta.persistence.AttributeOverride;
import jakarta.persistence.AttributeOverrides;
import jakarta.persistence.Basic;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.DiscriminatorColumn;
import jakarta.persistence.DiscriminatorType;
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Inheritance;
import jakarta.persistence.InheritanceType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OrderColumn;
import jakarta.persistence.PrimaryKeyJoinColumn;
import jakarta.persistence.SecondaryTable;
import jakarta.persistence.Table;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.metamodel.EntityType;
import static org.junit.jupiter.api.Assertions.assertEquals;
@Jpa(
annotatedClasses = {
EqualityComparisonTest.Address.class,
EqualityComparisonTest.Phone.class,
EqualityComparisonTest.Product.class
}
, properties = @Setting(name = AvailableSettings.CRITERIA_VALUE_HANDLING_MODE, value = "inline")
)
public class EqualityComparisonTest {
@Test
public void testEqualityComparisonEntityConversion(EntityManagerFactoryScope scope) {
Address address = new Address( "Street Id", "Fake Street", "Fake City", "Fake State", "Fake Zip" );
scope.inTransaction(
entityManager -> {
Phone phone1 = new Phone( "1", "555", "0001", address );
Phone phone2 = new Phone( "2", "555", "0002", address );
Phone phone3 = new Phone( "3", "555", "0003", address );
Phone phone4 = new Phone( "4", "555", "0004" );
List<Phone> phones = new ArrayList<>( 3 );
phones.add( phone1 );
phones.add( phone2 );
phones.add( phone3 );
address.setPhones( phones );
entityManager.persist( address );
entityManager.persist( phone4 );
}
);
scope.inTransaction(
entityManager -> {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
JpaMetamodel mm = (JpaMetamodel) entityManager.getMetamodel();
EntityType<Phone> Phone_ = mm.entity( Phone.class );
CriteriaQuery<Phone> cquery = cb.createQuery( Phone.class );
Root<Phone> phone = cquery.from( Phone.class );
Predicate predicate = cb.equal(
phone.get( Phone_.getSingularAttribute( "address", Address.class ) ),
address
);
cquery.where( predicate );
List<Phone> results = entityManager.createQuery( cquery ).getResultList();
assertEquals( 3, results.size() );
}
);
}
@Test
public void testEqualityComparisonLiteralConversion(EntityManagerFactoryScope scope) {
scope.inTransaction(
entityManager -> {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
JpaMetamodel mm = (JpaMetamodel) entityManager.getMetamodel();
CriteriaQuery<Integer> cquery = cb.createQuery( Integer.class );
Root<Product> product = cquery.from( Product.class );
EntityType<Product> Product_ = mm.entity( Product.class );
cquery.select(
cb.toInteger(
product.get(
Product_.getSingularAttribute( "quantity", Integer.class ) )
)
);
SqmComparisonPredicate predicate = (SqmComparisonPredicate) cb.equal(
product.get( Product_.getSingularAttribute( "partNumber", Long.class ) ),
373767373
);
Assert.assertEquals( Long.class, predicate.getLeftHandExpression().getJavaType() );
cquery.where( predicate );
entityManager.createQuery( cquery ).getResultList();
predicate = (SqmComparisonPredicate) cb.ge(
cb.length( product.get( Product_.getSingularAttribute( "name", String.class ) ) ),
4L
);
Assert.assertEquals( Integer.class, predicate.getLeftHandExpression().getJavaType() );
cquery.where( predicate );
entityManager.createQuery( cquery ).getResultList();
}
);
}
@Entity
@Table(name = "ADDRESS")
public static class Address implements java.io.Serializable {
private String id;
private String street;
private String city;
private String state;
private String zip;
private List<Phone> phones = new ArrayList<>();
public Address() {
}
public Address(String id, String street, String city, String state, String zip) {
this.id = id;
this.street = street;
this.city = city;
this.state = state;
this.zip = zip;
}
public Address(
String id,
String street,
String city,
String state,
String zip,
List<Phone> phones) {
this.id = id;
this.street = street;
this.city = city;
this.state = state;
this.zip = zip;
this.phones = phones;
}
@Id
@Column(name = "ID")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Column(name = "STREET")
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
@Column(name = "CITY")
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Column(name = "STATE")
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
@Column(name = "ZIP")
public String getZip() {
return zip;
}
public void setZip(String zip) {
this.zip = zip;
}
@OneToMany(cascade = CascadeType.ALL, mappedBy = "address")
@OrderColumn
public List<Phone> getPhones() {
return phones;
}
public void setPhones(List<Phone> phones) {
this.phones = phones;
}
}
@Entity
@Table(name = "PHONE_TABLE")
public static class Phone implements java.io.Serializable {
public enum Type {LAND_LINE, CELL, FAX, WORK, HOME}
private String id;
private String area;
private String number;
private Address address;
private Set<Phone.Type> types;
public Phone() {
}
public Phone(String v1, String v2, String v3) {
id = v1;
area = v2;
number = v3;
}
public Phone(String v1, String v2, String v3, Address v4) {
id = v1;
area = v2;
number = v3;
address = v4;
}
@Id
@Column(name = "ID")
public String getId() {
return id;
}
public void setId(String v) {
id = v;
}
@Column(name = "AREA")
public String getArea() {
return area;
}
public void setArea(String v) {
area = v;
}
@Column(name = "PHONE_NUMBER")
public String getNumber() {
return number;
}
public void setNumber(String v) {
number = v;
}
@ManyToOne
@JoinColumn(name = "FK_FOR_ADDRESS")
public Address getAddress() {
return address;
}
public void setAddress(Address a) {
address = a;
}
@ElementCollection
public Set<Phone.Type> getTypes() {
return types;
}
public void setTypes(Set<Phone.Type> types) {
this.types = types;
}
}
@Entity
@Table(name = "PRODUCT_TABLE")
@SecondaryTable(name = "PRODUCT_DETAILS", pkJoinColumns = @PrimaryKeyJoinColumn(name = "ID"))
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "PRODUCT_TYPE", discriminatorType = DiscriminatorType.STRING)
@DiscriminatorValue("Product")
public static class Product {
private String id;
private String name;
private double price;
private float rating;
private int quantity;
private long partNumber;
private BigInteger someBigInteger;
private BigDecimal someBigDecimal;
private String wareHouse;
private ShelfLife shelfLife;
public Product() {
}
public Product(String id, String name, double price, int quantity, long partNumber) {
this.id = id;
this.name = name;
this.price = price;
this.quantity = quantity;
this.partNumber = partNumber;
}
@Id
@Column(name = "ID")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Column(name = "NAME")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Column(name = "PRICE")
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Column(name = "QUANTITY")
public int getQuantity() {
return quantity;
}
public void setQuantity(int v) {
this.quantity = v;
}
@Column(name = "PNUM")
public long getPartNumber() {
return partNumber;
}
public void setPartNumber(long v) {
this.partNumber = v;
}
public float getRating() {
return rating;
}
public void setRating(float rating) {
this.rating = rating;
}
public BigInteger getSomeBigInteger() {
return someBigInteger;
}
public void setSomeBigInteger(BigInteger someBigInteger) {
this.someBigInteger = someBigInteger;
}
@Column(precision = 10, scale = 3)
public BigDecimal getSomeBigDecimal() {
return someBigDecimal;
}
public void setSomeBigDecimal(BigDecimal someBigDecimal) {
this.someBigDecimal = someBigDecimal;
}
@Column(name = "WHOUSE", nullable = true, table = "PRODUCT_DETAILS")
public String getWareHouse() {
return wareHouse;
}
public void setWareHouse(String v) {
this.wareHouse = v;
}
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "inceptionDate",
column = @Column(name = "INCEPTION", nullable = true)),
@AttributeOverride(name = "soldDate",
column = @Column(name = "SOLD", nullable = true))
})
public ShelfLife getShelfLife() {
return shelfLife;
}
public void setShelfLife(ShelfLife v) {
this.shelfLife = v;
}
}
@Embeddable
public static class ShelfLife implements java.io.Serializable {
private Date inceptionDate;
private Date soldDate;
public ShelfLife() {
}
public ShelfLife(Date inceptionDate, Date soldDate) {
this.inceptionDate = inceptionDate;
this.soldDate = soldDate;
}
@Basic
public Date getInceptionDate() {
return inceptionDate;
}
public void setInceptionDate(Date inceptionDate) {
this.inceptionDate = inceptionDate;
}
@Basic
public Date getSoldDate() {
return soldDate;
}
public void setSoldDate(Date soldDate) {
this.soldDate = soldDate;
}
}
}

View File

@ -0,0 +1,223 @@
/*
* 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.jpa.criteria.valuehandlingmode.inline;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Test;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OrderColumn;
import jakarta.persistence.Table;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
@Jpa(
annotatedClasses = {
ExpressionsTest.Address.class,
ExpressionsTest.Phone.class
}
, properties = @Setting(name = AvailableSettings.CRITERIA_VALUE_HANDLING_MODE, value = "inline")
)
public class ExpressionsTest {
@Test
public void testJoinedElementCollectionValuesInTupleList(EntityManagerFactoryScope scope) {
scope.inTransaction(
entityManager -> {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Phone> criteria = builder.createQuery( Phone.class );
Root<Phone> from = criteria.from( Phone.class );
criteria.where(
from.join( "types" )
.in( Collections.singletonList( Phone.Type.WORK ) )
);
entityManager.createQuery( criteria ).getResultList();
}
);
}
@Entity(name = "Phone")
@Table(name = "PHONE_TABLE")
public static class Phone implements java.io.Serializable {
public enum Type {LAND_LINE, CELL, FAX, WORK, HOME}
private String id;
private String area;
private String number;
private Address address;
private Set<Phone.Type> types;
public Phone() {
}
public Phone(String v1, String v2, String v3) {
id = v1;
area = v2;
number = v3;
}
public Phone(String v1, String v2, String v3, Address v4) {
id = v1;
area = v2;
number = v3;
address = v4;
}
@Id
@Column(name = "ID")
public String getId() {
return id;
}
public void setId(String v) {
id = v;
}
@Column(name = "AREA")
public String getArea() {
return area;
}
public void setArea(String v) {
area = v;
}
@Column(name = "PHONE_NUMBER")
public String getNumber() {
return number;
}
public void setNumber(String v) {
number = v;
}
@ManyToOne
@JoinColumn(name = "FK_FOR_ADDRESS")
public Address getAddress() {
return address;
}
public void setAddress(Address a) {
address = a;
}
@ElementCollection
public Set<Phone.Type> getTypes() {
return types;
}
public void setTypes(Set<Phone.Type> types) {
this.types = types;
}
}
@Entity
@Table(name = "ADDRESS")
public static class Address implements java.io.Serializable {
private String id;
private String street;
private String city;
private String state;
private String zip;
private List<Phone> phones = new java.util.ArrayList<Phone>();
public Address() {
}
public Address(String id, String street, String city, String state, String zip) {
this.id = id;
this.street = street;
this.city = city;
this.state = state;
this.zip = zip;
}
public Address(
String id, String street, String city, String state, String zip,
List<Phone> phones) {
this.id = id;
this.street = street;
this.city = city;
this.state = state;
this.zip = zip;
this.phones = phones;
}
@Id
@Column(name = "ID")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Column(name = "STREET")
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
@Column(name = "CITY")
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Column(name = "STATE")
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
@Column(name = "ZIP")
public String getZip() {
return zip;
}
public void setZip(String zip) {
this.zip = zip;
}
@OneToMany(cascade = CascadeType.ALL, mappedBy = "address")
@OrderColumn
public List<Phone> getPhones() {
return phones;
}
public void setPhones(List<Phone> phones) {
this.phones = phones;
}
}
}

View File

@ -0,0 +1,133 @@
/*
* 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.jpa.criteria.valuehandlingmode.inline;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Test;
import jakarta.persistence.CollectionTable;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.MapKeyJoinColumn;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.Table;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.MapJoin;
import jakarta.persistence.criteria.Root;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
/**
* @author Christian Beikov
*/
@Jpa(
annotatedClasses = {
MapJoinTestWithEmbeddable.Batch.class,
MapJoinTestWithEmbeddable.Node.class,
MapJoinTestWithEmbeddable.BatchNodeMetadata.class
}
,properties = @Setting( name = AvailableSettings.CRITERIA_VALUE_HANDLING_MODE, value = "inline")
)
public class MapJoinTestWithEmbeddable {
@Test
public void testSelectingKeyOfMapJoin(EntityManagerFactoryScope scope) {
scope.inTransaction( entityManager -> {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Node> query = cb.createQuery( Node.class );
Root<Batch> root = query.from( Batch.class );
MapJoin nodes = (MapJoin) root.join( "batchNodeMetadata" );
query.select( nodes.key() );
query.where( cb.equal( root.get( "id" ), 1 ) );
entityManager.createQuery( query ).getResultList();
} );
}
@Test
public void testSelectingValueOfMapJoin(EntityManagerFactoryScope scope) {
scope.inTransaction( entityManager -> {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<BatchNodeMetadata> query = cb.createQuery( BatchNodeMetadata.class );
Root<Batch> root = query.from( Batch.class );
MapJoin nodes = (MapJoin) root.join( "batchNodeMetadata" );
query.select( nodes );
query.where( cb.equal( root.get( "id" ), 1 ) );
entityManager.createQuery( query ).getResultList();
} );
}
@Entity
@Table(name = "batch")
public static class Batch implements Serializable {
@Id
@GeneratedValue(generator = "BatchGen")
@SequenceGenerator(name = "BatchGen", sequenceName = "seq_batch", allocationSize = 1)
private Long id;
@ElementCollection
@CollectionTable(
name = "batch_node",
//uniqueConstraints = @UniqueConstraint(columnNames = {"batch_id", "node_id"}),
//foreignKey = @ForeignKey,
joinColumns = @JoinColumn(name = "batch_id", nullable = false))
@MapKeyJoinColumn(name = "node_id")
private Map<Node, BatchNodeMetadata> batchNodeMetadata = new HashMap<>();
}
@Entity
@Table(name = "node")
public static class Node implements Serializable {
@Id
@GeneratedValue(generator = "NodeGen")
@SequenceGenerator(name = "NodeGen", sequenceName = "seq_node", allocationSize = 1)
private Long id;
}
@Embeddable
public static class BatchNodeMetadata implements Serializable {
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private NodeMigration migrering = NodeMigration.TOTAL;
public NodeMigration getMigrering() {
return migrering;
}
public void setMigrering(NodeMigration migrering) {
this.migrering = migrering;
}
}
public enum NodeMigration {
TOTAL
}
}

View File

@ -0,0 +1,113 @@
/*
* 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.jpa.criteria.valuehandlingmode.inline;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.OneToOne;
import jakarta.persistence.PrimaryKeyJoinColumn;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
@Jpa(
annotatedClasses = { OptionalOneToOneTest.Person.class, OptionalOneToOneTest.PersonAddress.class }
,properties = @Setting( name = AvailableSettings.CRITERIA_VALUE_HANDLING_MODE, value = "inline")
)
public class OptionalOneToOneTest {
@Test
public void testBidirQueryEntityProperty(EntityManagerFactoryScope scope) {
PersonAddress personAddress = scope.fromTransaction(
session -> {
PersonAddress address = new PersonAddress();
Person person = new Person();
address.setPerson( person );
person.setPersonAddress( address );
session.persist( person );
session.persist( address );
return address;
}
);
scope.inTransaction( session -> {
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<Person> criteria = criteriaBuilder.createQuery( Person.class );
Root<Person> root = criteria.from( Person.class );
criteria.where( criteriaBuilder.equal( root.get( "personAddress" ), personAddress ) );
session.createQuery( criteria ).getSingleResult();
} );
}
@Entity
public static class Person {
@Id @GeneratedValue(generator = "fk")
@GenericGenerator(strategy = "foreign", name = "fk", parameters = @Parameter(name="property", value="personAddress"))
private Integer id;
@PrimaryKeyJoinColumn
@OneToOne(optional=true)
private PersonAddress personAddress;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public PersonAddress getPersonAddress() {
return personAddress;
}
public void setPersonAddress(PersonAddress personAddress) {
this.personAddress = personAddress;
}
}
@Entity
public static class PersonAddress {
@Id
@GeneratedValue
private Integer id;
@OneToOne(mappedBy="personAddress")
private Person person;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
}

View File

@ -0,0 +1,172 @@
/*
* 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.jpa.criteria.valuehandlingmode.inline;
import java.util.List;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.CockroachDialect;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.Setting;
import org.hibernate.testing.orm.junit.SkipForDialect;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Path;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import static org.junit.jupiter.api.Assertions.assertTrue;
@Jpa(
annotatedClasses = {
PredicateTest.Customer.class,
PredicateTest.Order.class
}
, properties = @Setting(name = AvailableSettings.CRITERIA_VALUE_HANDLING_MODE, value = "inline")
)
public class PredicateTest {
@Test
@SkipForDialect(dialectClass = CockroachDialect.class, reason = "https://github.com/cockroachdb/cockroach/issues/41943")
public void testQuotientConversion(EntityManagerFactoryScope scope) {
scope.inTransaction(
entityManager -> {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Order> orderCriteria = builder.createQuery( Order.class );
Root<Order> orderRoot = orderCriteria.from( Order.class );
Long longValue = 999999999L;
Path<Double> doublePath = orderRoot.get( "totalPrice" );
Path<Integer> integerPath = orderRoot.get( "customer" ).get( "age" );
orderCriteria.select( orderRoot );
Predicate p = builder.ge(
builder.quot( integerPath, doublePath ),
longValue
);
orderCriteria.where( p );
List<Order> orders = entityManager.createQuery( orderCriteria ).getResultList();
assertTrue( orders.size() == 0 );
}
);
}
@Entity
@Table(name = "ORDER_TABLE")
public static class Order {
private String id;
private double totalPrice;
private Customer customer;
public Order() {
}
public Order(String id, double totalPrice) {
this.id = id;
this.totalPrice = totalPrice;
}
public Order(String id, Customer customer) {
this.id = id;
this.customer = customer;
}
public Order(String id) {
this.id = id;
}
@Id
@Column(name = "ID")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Column(name = "TOTALPRICE")
public double getTotalPrice() {
return totalPrice;
}
public void setTotalPrice(double price) {
this.totalPrice = price;
}
@ManyToOne
@JoinColumn(name = "FK4_FOR_CUSTOMER_TABLE")
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
@Entity
@Table(name = "CUSTOMER_TABLE")
public static class Customer {
private String id;
private String name;
private Integer age;
public Customer() {
}
public Customer(String id, String name) {
this.id = id;
this.name = name;
}
// Used by test case for HHH-8699.
public Customer(String id, String name, String greeting, Boolean something) {
this.id = id;
this.name = name;
}
@Id
@Column(name = "ID")
public String getId() {
return id;
}
public void setId(String v) {
this.id = v;
}
@Column(name = "NAME")
public String getName() {
return name;
}
public void setName(String v) {
this.name = v;
}
@Column(name = "AGE")
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
}

View File

@ -0,0 +1,227 @@
/*
* 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.jpa.criteria.valuehandlingmode.inline;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.From;
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.Subquery;
import jakarta.persistence.metamodel.EntityType;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
@Jpa(
annotatedClasses = {
SubQueryTest.Site.class,
SubQueryTest.Task.class,
SubQueryTest.Patient.class
}
, properties = @Setting(name = AvailableSettings.CRITERIA_VALUE_HANDLING_MODE, value = "inline")
)
public class SubQueryTest {
private Set<Site> validSites;
private Task taskWithoutPatient;
private Task taskWithPatientWithoutSite;
private Task taskWithPatient1WithValidSite1;
private Task taskWithPatient2WithValidSite1;
private Task taskWithPatient3WithValidSite2;
private Task taskWithPatientWithInvalidSite;
@BeforeEach
public void setUp(EntityManagerFactoryScope scope) {
scope.inTransaction( entityManager -> {
final Site validSite1 = new Site();
final Site validSite2 = new Site();
final Site invalidSite = new Site();
entityManager.persist( validSite1 );
entityManager.persist( validSite2 );
entityManager.persist( invalidSite );
validSites = new HashSet<>( Arrays.asList( validSite1, validSite2 ) );
final Patient patientWithoutSite = new Patient();
final Patient patient1WithValidSite1 = new Patient( validSite1 );
final Patient patient2WithValidSite1 = new Patient( validSite1 );
final Patient patient3WithValidSite2 = new Patient( validSite2 );
final Patient patientWithInvalidSite = new Patient( invalidSite );
entityManager.persist( patientWithoutSite );
entityManager.persist( patient1WithValidSite1 );
entityManager.persist( patient2WithValidSite1 );
entityManager.persist( patient3WithValidSite2 );
entityManager.persist( patientWithInvalidSite );
taskWithoutPatient = new Task();
taskWithoutPatient.description = "taskWithoutPatient";
taskWithPatientWithoutSite = new Task( patientWithoutSite );
taskWithPatientWithoutSite.description = "taskWithPatientWithoutSite";
taskWithPatient1WithValidSite1 = new Task( patient1WithValidSite1 );
taskWithPatient1WithValidSite1.description = "taskWithPatient1WithValidSite1";
taskWithPatient2WithValidSite1 = new Task( patient2WithValidSite1 );
taskWithPatient2WithValidSite1.description = "taskWithPatient2WithValidSite1";
taskWithPatient3WithValidSite2 = new Task( patient3WithValidSite2 );
taskWithPatient3WithValidSite2.description = "taskWithPatient3WithValidSite2";
taskWithPatientWithInvalidSite = new Task( patientWithInvalidSite );
taskWithPatientWithInvalidSite.description = "taskWithPatientWithInvalidSite";
entityManager.persist( taskWithoutPatient );
entityManager.persist( taskWithPatientWithoutSite );
entityManager.persist( taskWithPatient1WithValidSite1 );
entityManager.persist( taskWithPatient2WithValidSite1 );
entityManager.persist( taskWithPatient3WithValidSite2 );
entityManager.persist( taskWithPatientWithInvalidSite );
} );
}
@Test
public void testCorrelateSubQueryLeftJoin(EntityManagerFactoryScope scope) {
scope.inTransaction( entityManager -> {
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
final CriteriaQuery<Task> outerQuery = builder.createQuery( Task.class );
final Root<Task> outerTask = outerQuery.from( Task.class );
final Subquery<Task> subquery = outerQuery.subquery( Task.class );
final Root<Task> subtask = subquery.correlate( outerTask );
final EntityType<Task> taskEntityType = entityManager.getMetamodel().entity( Task.class );
final EntityType<Patient> patientEntityType = entityManager.getMetamodel().entity( Patient.class );
final From<Task, Patient> patient = subtask.join(
taskEntityType.getSingularAttribute( "patient", Patient.class ),
JoinType.LEFT
);
final From<Patient, Site> site = patient.join(
patientEntityType.getSingularAttribute( "site", Site.class ),
JoinType.LEFT
);
outerQuery.where(
builder.exists(
subquery.select( subtask )
.where(
builder.or(
patient.isNull(),
site.in( validSites )
)
)
)
);
final List<Task> tasks = entityManager.createQuery( outerQuery ).getResultList();
assertThat( new HashSet<>( tasks ), is( new HashSet<>( Arrays.asList(
taskWithoutPatient,
taskWithPatient1WithValidSite1,
taskWithPatient2WithValidSite1,
taskWithPatient3WithValidSite2
) ) ) );
} );
}
@Entity(name = "Task")
@Table(name = "Task")
public static class Task {
@Id
@GeneratedValue
Long id;
@ManyToOne(fetch = FetchType.LAZY)
Patient patient;
String description;
public Task() {
}
public Task(Patient patient) {
this.patient = patient;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
Task task = (Task) o;
return id.equals( task.id );
}
@Override
public int hashCode() {
return Objects.hash( id );
}
@Override
public String toString() {
return String.format( "Task(id: %d; description: %s)", id, description == null ? "null" : description );
}
}
@Entity(name = "Patient")
@Table(name = "Patient")
public static class Patient {
@Id
@GeneratedValue
Long id;
@ManyToOne(fetch = FetchType.LAZY)
Site site;
public Patient() {
}
public Patient(Site site) {
this.site = site;
}
}
@Entity(name = "Site")
@Table(name = "Site")
public static class Site {
@Id
@GeneratedValue
Long id;
String name;
}
}