remove the @Deprecated and @Incubating support for hibernate.create_empty_composites.enabled

Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
Gavin King 2024-06-03 14:23:01 +02:00 committed by Steve Ebersole
parent 0cbdc44fbc
commit 3fd2a146ef
21 changed files with 9 additions and 1792 deletions

View File

@ -16,7 +16,6 @@ import java.util.List;
import java.util.ServiceConfigurationError; import java.util.ServiceConfigurationError;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import java.util.Set; import java.util.Set;
import java.util.stream.Stream;
import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;

View File

@ -453,29 +453,6 @@ public interface MappingSettings {
*/ */
String DEFAULT_LIST_SEMANTICS = "hibernate.mapping.default_list_semantics"; String DEFAULT_LIST_SEMANTICS = "hibernate.mapping.default_list_semantics";
/**
* Enable instantiation of composite/embedded objects when all attribute values
* are {@code null}. The default (and historical) behavior is that a {@code null}
* reference will be used to represent the composite value when all of its
* attributes are {@code null}.
*
* @apiNote This is an experimental feature that has known issues. It should not
* be used in production until it is stabilized. See Hibernate JIRA issue
* HHH-11936 for details.
*
* @deprecated It makes no sense at all to enable this at the global level for a
* persistence unit. If anything, it could be a setting specific to
* a given embeddable class. But, four years after the introduction of
* this feature, it's still marked experimental and has multiple known
* unresolved bugs. It's therefore time for those who advocated for
* this feature to accept defeat.
*
* @since 5.1
*/
@Incubating
@Deprecated(since = "6")
String CREATE_EMPTY_COMPOSITES_ENABLED = "hibernate.create_empty_composites.enabled";
/** /**
* The {@link org.hibernate.annotations.Where @Where} annotation specifies a * The {@link org.hibernate.annotations.Where @Where} annotation specifies a
* restriction on the table rows which are visible as entity class instances or * restriction on the table rows which are visible as entity class instances or

View File

@ -138,7 +138,6 @@ import jakarta.persistence.TypedQueryReference;
import static jakarta.persistence.SynchronizationType.SYNCHRONIZED; import static jakarta.persistence.SynchronizationType.SYNCHRONIZED;
import static java.util.Collections.emptySet; import static java.util.Collections.emptySet;
import static java.util.Collections.unmodifiableSet; import static java.util.Collections.unmodifiableSet;
import static org.hibernate.cfg.AvailableSettings.CREATE_EMPTY_COMPOSITES_ENABLED;
import static org.hibernate.cfg.AvailableSettings.CURRENT_SESSION_CONTEXT_CLASS; import static org.hibernate.cfg.AvailableSettings.CURRENT_SESSION_CONTEXT_CLASS;
import static org.hibernate.cfg.AvailableSettings.JAKARTA_VALIDATION_FACTORY; import static org.hibernate.cfg.AvailableSettings.JAKARTA_VALIDATION_FACTORY;
import static org.hibernate.cfg.AvailableSettings.JPA_VALIDATION_FACTORY; import static org.hibernate.cfg.AvailableSettings.JPA_VALIDATION_FACTORY;
@ -147,7 +146,6 @@ import static org.hibernate.cfg.PersistenceSettings.SESSION_FACTORY_JNDI_NAME;
import static org.hibernate.engine.config.spi.StandardConverters.STRING; import static org.hibernate.engine.config.spi.StandardConverters.STRING;
import static org.hibernate.internal.FetchProfileHelper.getFetchProfiles; import static org.hibernate.internal.FetchProfileHelper.getFetchProfiles;
import static org.hibernate.internal.log.DeprecationLogger.DEPRECATION_LOGGER; import static org.hibernate.internal.log.DeprecationLogger.DEPRECATION_LOGGER;
import static org.hibernate.internal.util.config.ConfigurationHelper.getBoolean;
import static org.hibernate.jpa.HibernateHints.HINT_TENANT_ID; import static org.hibernate.jpa.HibernateHints.HINT_TENANT_ID;
import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
import static org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT; import static org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT;
@ -240,7 +238,6 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im
maskOutSensitiveInformation( settings ); maskOutSensitiveInformation( settings );
deprecationCheck( settings ); deprecationCheck( settings );
LOG.debugf( "Instantiating SessionFactory with settings: %s", settings); LOG.debugf( "Instantiating SessionFactory with settings: %s", settings);
logIfEmptyCompositesEnabled( settings );
sqlStringGenerationContext = createSqlStringGenerationContext( bootMetamodel, options, jdbcServices ); sqlStringGenerationContext = createSqlStringGenerationContext( bootMetamodel, options, jdbcServices );
@ -1784,13 +1781,6 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im
} }
} }
private static void logIfEmptyCompositesEnabled(Map<String, Object> props ) {
final boolean isEmptyCompositesEnabled = getBoolean( CREATE_EMPTY_COMPOSITES_ENABLED, props );
if ( isEmptyCompositesEnabled ) {
LOG.emptyCompositesEnabled();
}
}
/** /**
* @return the {@link FastSessionServices} for this {@code SessionFactory}. * @return the {@link FastSessionServices} for this {@code SessionFactory}.
*/ */

View File

@ -39,8 +39,6 @@ public interface EmbeddableMappingType extends ManagedMappingType, SelectableMap
EmbeddableRepresentationStrategy getRepresentationStrategy(); EmbeddableRepresentationStrategy getRepresentationStrategy();
boolean isCreateEmptyCompositesEnabled();
/** /**
* Returns the {@linkplain EmbeddableDiscriminatorMapping discriminator mapping} * Returns the {@linkplain EmbeddableDiscriminatorMapping discriminator mapping}
* if this discriminator type is polymorphic, {@code null} otherwise. * if this discriminator type is polymorphic, {@code null} otherwise.

View File

@ -19,16 +19,13 @@ import java.util.function.Function;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.SharedSessionContract; import org.hibernate.SharedSessionContract;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.aggregate.AggregateSupport; import org.hibernate.dialect.aggregate.AggregateSupport;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.CascadeStyle; import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.mapping.AggregateColumn; import org.hibernate.mapping.AggregateColumn;
import org.hibernate.mapping.Any; import org.hibernate.mapping.Any;
import org.hibernate.mapping.BasicValue; import org.hibernate.mapping.BasicValue;
@ -179,7 +176,6 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
private final Map<String, ConcreteEmbeddableTypeImpl> concreteEmbeddableBySubclass; private final Map<String, ConcreteEmbeddableTypeImpl> concreteEmbeddableBySubclass;
private final Map<Object, ConcreteEmbeddableTypeImpl> concreteEmbeddableByDiscriminator; private final Map<Object, ConcreteEmbeddableTypeImpl> concreteEmbeddableByDiscriminator;
private final boolean createEmptyCompositesEnabled;
private final SelectableMapping aggregateMapping; private final SelectableMapping aggregateMapping;
private final boolean aggregateMappingRequiresColumnWriter; private final boolean aggregateMappingRequiresColumnWriter;
private final boolean preferSelectAggregateMapping; private final boolean preferSelectAggregateMapping;
@ -222,12 +218,6 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
this.concreteEmbeddableBySubclass = null; this.concreteEmbeddableBySubclass = null;
} }
this.createEmptyCompositesEnabled = ConfigurationHelper.getBoolean(
Environment.CREATE_EMPTY_COMPOSITES_ENABLED,
creationContext.getServiceRegistry()
.requireService( ConfigurationService.class )
.getSettings()
);
final AggregateColumn aggregateColumn = bootDescriptor.getAggregateColumn(); final AggregateColumn aggregateColumn = bootDescriptor.getAggregateColumn();
if ( aggregateColumn != null ) { if ( aggregateColumn != null ) {
final Dialect dialect = creationContext.getDialect(); final Dialect dialect = creationContext.getDialect();
@ -366,7 +356,6 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
this.discriminatorMapping = null; this.discriminatorMapping = null;
this.concreteEmbeddableBySubclass = null; this.concreteEmbeddableBySubclass = null;
this.concreteEmbeddableByDiscriminator = null; this.concreteEmbeddableByDiscriminator = null;
this.createEmptyCompositesEnabled = inverseMappingType.isCreateEmptyCompositesEnabled();
this.aggregateMapping = null; this.aggregateMapping = null;
this.aggregateMappingRequiresColumnWriter = false; this.aggregateMappingRequiresColumnWriter = false;
this.preferSelectAggregateMapping = false; this.preferSelectAggregateMapping = false;
@ -1120,11 +1109,6 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
} }
@Override
public boolean isCreateEmptyCompositesEnabled() {
return createEmptyCompositesEnabled;
}
@Override @Override
public SelectableMapping getAggregateMapping() { public SelectableMapping getAggregateMapping() {
return aggregateMapping; return aggregateMapping;

View File

@ -274,14 +274,6 @@ public class IdClassEmbeddable extends AbstractEmbeddableMapping implements Iden
return embedded; return embedded;
} }
@Override
public boolean isCreateEmptyCompositesEnabled() {
// generally we do not want empty composites for identifiers
return false;
}
@Override @Override
public void forEachAttributeMapping(Consumer<? super AttributeMapping> action) { public void forEachAttributeMapping(Consumer<? super AttributeMapping> action) {
forEachAttribute( (index, attribute) -> action.accept( attribute ) ); forEachAttribute( (index, attribute) -> action.accept( attribute ) );

View File

@ -146,12 +146,6 @@ public class VirtualIdEmbeddable extends AbstractEmbeddableMapping implements Id
return representationStrategy; return representationStrategy;
} }
@Override
public boolean isCreateEmptyCompositesEnabled() {
// generally we do not want empty composites for identifiers
return false;
}
@Override @Override
public EntityMappingType findContainingEntityMapping() { public EntityMappingType findContainingEntityMapping() {
return idMapping.findContainingEntityMapping(); return idMapping.findContainingEntityMapping();

View File

@ -188,11 +188,6 @@ public class AnonymousTupleEmbeddableValuedModelPart implements EmbeddableValued
.getRepresentationStrategy(); .getRepresentationStrategy();
} }
@Override
public boolean isCreateEmptyCompositesEnabled() {
return false;
}
@Override @Override
public EmbeddableMappingType createInverseMappingType( public EmbeddableMappingType createInverseMappingType(
EmbeddedAttributeMapping valueMapping, EmbeddedAttributeMapping valueMapping,

View File

@ -21,7 +21,6 @@ import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.metamodel.spi.ValueAccess; import org.hibernate.metamodel.spi.ValueAccess;
import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.property.access.spi.Setter; import org.hibernate.property.access.spi.Setter;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer; import org.hibernate.proxy.LazyInitializer;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.AssemblerCreationState;
@ -44,6 +43,8 @@ import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
import static org.hibernate.sql.results.graph.embeddable.EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER;
import static org.hibernate.sql.results.graph.entity.internal.BatchEntityInsideEmbeddableSelectFetchInitializer.BATCH_PROPERTY; import static org.hibernate.sql.results.graph.entity.internal.BatchEntityInsideEmbeddableSelectFetchInitializer.BATCH_PROPERTY;
/** /**
@ -58,7 +59,6 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
private final @Nullable InitializerParent<InitializerData> parent; private final @Nullable InitializerParent<InitializerData> parent;
private final boolean isResultInitializer; private final boolean isResultInitializer;
private final boolean isPartOfKey; private final boolean isPartOfKey;
private final boolean createEmptyCompositesEnabled;
private final SessionFactoryImplementor sessionFactory; private final SessionFactoryImplementor sessionFactory;
protected final DomainResultAssembler<?>[][] assemblers; protected final DomainResultAssembler<?>[][] assemblers;
@ -117,7 +117,6 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
this.isPartOfKey = embedded.isEntityIdentifierMapping() || Initializer.isPartOfKey( navigablePath, parent ); this.isPartOfKey = embedded.isEntityIdentifierMapping() || Initializer.isPartOfKey( navigablePath, parent );
// We never want to create empty composites for the FK target or PK, otherwise collections would break // We never want to create empty composites for the FK target or PK, otherwise collections would break
this.createEmptyCompositesEnabled = !isPartOfKey && embeddableMappingType.isCreateEmptyCompositesEnabled();
this.sessionFactory = creationState.getSqlAstCreationContext().getSessionFactory(); this.sessionFactory = creationState.getSqlAstCreationContext().getSessionFactory();
final Collection<EmbeddableMappingType.ConcreteEmbeddableType> concreteEmbeddableTypes = embeddableMappingType.getConcreteEmbeddableTypes(); final Collection<EmbeddableMappingType.ConcreteEmbeddableType> concreteEmbeddableTypes = embeddableMappingType.getConcreteEmbeddableTypes();
final DomainResultAssembler<?>[][] assemblers = new DomainResultAssembler[concreteEmbeddableTypes.isEmpty() ? 1 : concreteEmbeddableTypes.size()][]; final DomainResultAssembler<?>[][] assemblers = new DomainResultAssembler[concreteEmbeddableTypes.isEmpty() ? 1 : concreteEmbeddableTypes.size()][];
@ -474,6 +473,8 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
if ( data.getInstance() == null ) { if ( data.getInstance() == null ) {
data.setInstance( createCompositeInstance( data ) ); data.setInstance( createCompositeInstance( data ) );
} }
EMBEDDED_LOAD_LOGGER.debugf( "Created composite instance [%s]", navigablePath );
} }
private void extractRowState(EmbeddableInitializerData data) { private void extractRowState(EmbeddableInitializerData data) {
@ -515,11 +516,7 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
private Object createCompositeInstance(EmbeddableInitializerData data) { private Object createCompositeInstance(EmbeddableInitializerData data) {
if ( data.getState() == State.MISSING ) { if ( data.getState() == State.MISSING ) {
// todo (6.0) : should we initialize the composite instance if it has a parent attribute? return null;
// if ( !createEmptyCompositesEnabled && embedded.getParentInjectionAttributePropertyAccess() == null ) {
if ( !createEmptyCompositesEnabled ) {
return null;
}
} }
final EmbeddableInstantiator instantiator = data.concreteEmbeddableType == null final EmbeddableInstantiator instantiator = data.concreteEmbeddableType == null
@ -527,6 +524,7 @@ public class EmbeddableInitializerImpl extends AbstractInitializer<EmbeddableIni
: data.concreteEmbeddableType.getInstantiator(); : data.concreteEmbeddableType.getInstantiator();
final Object instance = instantiator.instantiate( data, sessionFactory ); final Object instance = instantiator.instantiate( data, sessionFactory );
data.setState( State.RESOLVED ); data.setState( State.RESOLVED );
EMBEDDED_LOAD_LOGGER.debugf( "Created composite instance [%s] : %s", navigablePath, instance );
return instance; return instance;
} }

View File

@ -1,174 +0,0 @@
/*
* 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.component.empty;
import java.io.Serializable;
import java.util.Set;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Embeddable;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import org.hibernate.Hibernate;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
* @author Gail Badner
*/
public class EmptyCompositeCollectionKeyEagerTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] {
AnEntity.class
};
}
@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
configuration.getProperties().put( Environment.CREATE_EMPTY_COMPOSITES_ENABLED, "true" );
configuration.getProperties().put( Environment.USE_SECOND_LEVEL_CACHE, "false" );
}
@Test
public void testGetEntityWithEmptyCollection() {
AnEntity.PK id = doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = new AnEntity( new AnEntity.PK( "first", "last" ));
session.persist( anEntity );
return anEntity.id;
}
);
doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = session.find( AnEntity.class, id );
assertTrue( Hibernate.isInitialized( anEntity.names ) );
assertTrue( anEntity.names.isEmpty() );
}
);
}
@Test
public void testQueryEntityWithEmptyCollection() {
AnEntity.PK id = doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = new AnEntity( new AnEntity.PK( "first", "last" ) );
session.persist( anEntity );
return anEntity.id;
}
);
doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = session.createQuery(
"from AnEntity where id = :id",
AnEntity.class
).setParameter( "id", id ).uniqueResult();
assertTrue( Hibernate.isInitialized( anEntity.names ) );
assertTrue( anEntity.names.isEmpty() );
}
);
}
@Test
public void testQueryEntityJoinFetchEmptyCollection() {
AnEntity.PK id = doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = new AnEntity( new AnEntity.PK( "first", "last" ) );
session.persist( anEntity );
return anEntity.id;
}
);
doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = session.createQuery(
"from AnEntity e join fetch e.names where e.id = :id ",
AnEntity.class
).setParameter( "id", id ).uniqueResult();
assertNull( anEntity );
}
);
}
@Test
public void testQueryEntityLeftJoinFetchEmptyCollection() {
AnEntity.PK id = doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = new AnEntity( new AnEntity.PK( "first", "last" ) );
session.persist( anEntity );
return anEntity.id;
}
);
doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = session.createQuery(
"from AnEntity e left join fetch e.names where e.id = :id",
AnEntity.class
).setParameter( "id", id ).uniqueResult();
assertTrue( Hibernate.isInitialized( anEntity.names ) );
assertTrue( anEntity.names.isEmpty() );
}
);
}
@Override
protected boolean isCleanupTestDataRequired() {
return true;
}
@Entity(name = "AnEntity")
public static class AnEntity {
@EmbeddedId
private PK id;
@ElementCollection(fetch = FetchType.EAGER)
private Set<String> names;
public AnEntity() {
}
public AnEntity(PK id) {
this.id = id;
}
@Embeddable
public static class PK implements Serializable {
private String firstName;
private String lastName;
public PK() {
}
public PK(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
}
}

View File

@ -1,176 +0,0 @@
/*
* 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.component.empty;
import java.io.Serializable;
import java.util.Set;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Embeddable;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Entity;
import org.hibernate.Hibernate;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
* @author Gail Badner
*/
public class EmptyCompositeCollectionKeyLazyTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] {
AnEntity.class
};
}
@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
configuration.getProperties().put( Environment.CREATE_EMPTY_COMPOSITES_ENABLED, "true" );
configuration.getProperties().put( Environment.USE_SECOND_LEVEL_CACHE, "false" );
}
@Test
public void testGetEntityWithEmptyCollection() {
AnEntity.PK id = doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = new AnEntity( new AnEntity.PK( "first", "last" ));
session.persist( anEntity );
return anEntity.id;
}
);
doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = session.find( AnEntity.class, id );
assertFalse( Hibernate.isInitialized( anEntity.names ) );
assertTrue( anEntity.names.isEmpty() );
}
);
}
@Test
public void testQueryEntityWithEmptyCollection() {
AnEntity.PK id = doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = new AnEntity( new AnEntity.PK( "first", "last" ) );
session.persist( anEntity );
return anEntity.id;
}
);
doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = session.createQuery(
"from AnEntity where id = :id",
AnEntity.class
).setParameter( "id", id ).uniqueResult();
assertFalse( Hibernate.isInitialized( anEntity.names ) );
assertTrue( anEntity.names.isEmpty() );
}
);
}
@Test
public void testQueryEntityJoinFetchEmptyCollection() {
AnEntity.PK id = doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = new AnEntity( new AnEntity.PK( "first", "last" ) );
session.persist( anEntity );
return anEntity.id;
}
);
doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = session.createQuery(
"from AnEntity e join fetch e.names where e.id = :id ",
AnEntity.class
).setParameter( "id", id ).uniqueResult();
assertNull( anEntity );
}
);
}
@Test
@TestForIssue( jiraKey = "HHH-11928" )
public void testQueryEntityLeftJoinFetchEmptyCollection() {
AnEntity.PK id = doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = new AnEntity( new AnEntity.PK( "first", "last" ) );
session.persist( anEntity );
return anEntity.id;
}
);
doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = session.createQuery(
"from AnEntity e left join fetch e.names where e.id = :id",
AnEntity.class
).setParameter( "id", id ).uniqueResult();
assertTrue( Hibernate.isInitialized( anEntity.names ) );
assertTrue( anEntity.names.isEmpty() );
}
);
}
@Override
protected boolean isCleanupTestDataRequired() {
return true;
}
@Entity(name = "AnEntity")
public static class AnEntity {
@EmbeddedId
private PK id;
@ElementCollection
private Set<String> names;
public AnEntity() {
}
public AnEntity(PK id) {
this.id = id;
}
@Embeddable
public static class PK implements Serializable {
private String firstName;
private String lastName;
public PK() {
}
public PK(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
}
}

View File

@ -1,226 +0,0 @@
/*
* 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.component.empty;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import org.hibernate.annotations.Parent;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.type.CompositeType;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* @author Gail Badner
*/
public class EmptyCompositeEquivalentToNullTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { AnEntity.class };
}
@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
configuration.getProperties().put( Environment.CREATE_EMPTY_COMPOSITES_ENABLED, "true" );
}
@Test
@TestForIssue( jiraKey = "HHH-11898" )
@FailureExpected( jiraKey = "HHH-11898" )
public void testPrimitive() {
doInHibernate(
this::sessionFactory,
session -> {
AnEntity anEntity = new AnEntity();
session.persist( anEntity );
session.flush();
session.clear();
anEntity = session.get( AnEntity.class, anEntity.id );
checkEmptyCompositeTypeEquivalentToNull(
anEntity.embeddableWithPrimitive,
"embeddableWithPrimitive",
sessionFactory()
);
}
);
}
@Test
@TestForIssue( jiraKey = "HHH-11898" )
public void testParent() {
doInHibernate(
this::sessionFactory,
session -> {
AnEntity anEntity = new AnEntity();
session.persist( anEntity );
session.flush();
session.clear();
anEntity = session.get( AnEntity.class, anEntity.id );
checkEmptyCompositeTypeEquivalentToNull(
anEntity.embeddableWithParent,
"embeddableWithParent",
sessionFactory()
);
}
);
}
@Test
@TestForIssue( jiraKey = "HHH-11898" )
public void testNoPrimitiveNoParent() {
doInHibernate(
this::sessionFactory,
session -> {
AnEntity anEntity = new AnEntity();
session.persist( anEntity );
session.flush();
session.clear();
anEntity = session.get( AnEntity.class, anEntity.id );
checkEmptyCompositeTypeEquivalentToNull(
anEntity.embeddableWithNoPrimitiveNoParent,
"embeddableWithNoPrimitiveNoParent",
sessionFactory()
);
}
);
}
private void checkEmptyCompositeTypeEquivalentToNull(
Object compositeValue,
String componentPropertyName,
SessionFactoryImplementor sessionFactory) {
assertNotNull( compositeValue );
final CompositeType compositeType = getCompositeType( componentPropertyName, sessionFactory );
assertTrue( compositeType.isEqual( null, compositeValue, sessionFactory ) );
assertTrue( compositeType.isEqual( compositeValue, null, sessionFactory ) );
}
private CompositeType getCompositeType(String componentPropertyName, SessionFactoryImplementor sessionFactory) {
return (CompositeType) sessionFactory
.getRuntimeMetamodels()
.getMappingMetamodel()
.getEntityDescriptor( AnEntity.class )
.getPropertyType( componentPropertyName );
}
@Entity(name = "AnEntity")
public static class AnEntity {
private int id;
private EmbeddableWithParent embeddableWithParent;
private EmbeddableWithPrimitive embeddableWithPrimitive;
private EmbeddableWithNoPrimitiveNoParent embeddableWithNoPrimitiveNoParent;
@Id
@GeneratedValue
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public EmbeddableWithParent getEmbeddableWithParent() {
return embeddableWithParent;
}
public void setEmbeddableWithParent(EmbeddableWithParent embeddableWithParent) {
this.embeddableWithParent = embeddableWithParent;
}
public EmbeddableWithPrimitive getEmbeddableWithPrimitive() {
return embeddableWithPrimitive;
}
public void setEmbeddableWithPrimitive(EmbeddableWithPrimitive embeddableWithPrimitive) {
this.embeddableWithPrimitive = embeddableWithPrimitive;
}
public EmbeddableWithNoPrimitiveNoParent getEmbeddableWithNoPrimitiveNoParent() {
return embeddableWithNoPrimitiveNoParent;
}
public void setEmbeddableWithNoPrimitiveNoParent(EmbeddableWithNoPrimitiveNoParent embeddableWithNoPrimitiveNoParent) {
this.embeddableWithNoPrimitiveNoParent = embeddableWithNoPrimitiveNoParent;
}
}
@Embeddable
public static class EmbeddableWithParent {
private Object parent;
private Long longObjectValue;
@Parent
public Object getParent() {
return parent;
}
public void setParent(Object parent) {
this.parent = parent;
}
public Long getLongObjectValue() {
return longObjectValue;
}
public void setLongObjectValue(Long longObjectValue) {
this.longObjectValue = longObjectValue;
}
}
@Embeddable
public static class EmbeddableWithPrimitive {
private int intValue;
private String stringValue;
@Column(nullable = true)
public int getIntValue() {
return intValue;
}
public void setIntValue(int intValue) {
this.intValue = intValue;
}
public String getStringValue() {
return stringValue;
}
public void setStringValue(String stringValue) {
this.stringValue = stringValue;
}
}
@Embeddable
public static class EmbeddableWithNoPrimitiveNoParent {
private Integer intObjectValue;
private String otherStringValue;
public Integer getIntObjectValue() {
return intObjectValue;
}
public void setIntObjectValue(Integer intObjectValue) {
this.intObjectValue = intObjectValue;
}
public String getOtherStringValue() {
return otherStringValue;
}
public void setOtherStringValue(String otherStringValue) {
this.otherStringValue = otherStringValue;
}
}
}

View File

@ -1,435 +0,0 @@
/*
* 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.component.empty;
import java.io.Serializable;
import jakarta.persistence.Cacheable;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.SharedCacheMode;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.stat.CacheRegionStatistics;
import org.hibernate.stat.QueryStatistics;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
* @author Gail Badner
*/
public class EmptyCompositeManyToOneKeyCachedTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] {
AnEntity.class,
OtherEntity.class
};
}
@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
configuration.getProperties().put( Environment.CREATE_EMPTY_COMPOSITES_ENABLED, "true" );
configuration.getProperties().put( Environment.USE_SECOND_LEVEL_CACHE, "true" );
configuration.getProperties().put( Environment.DEFAULT_CACHE_CONCURRENCY_STRATEGY, AccessType.READ_WRITE.getExternalName() );
configuration.getProperties().put( Environment.USE_QUERY_CACHE, "true" );
configuration.getProperties().put( Environment.GENERATE_STATISTICS, "true" );
configuration.getProperties().put( Environment.CACHE_REGION_PREFIX, "" );
configuration.getProperties().put( "javax.persistence.sharedCache.mode", SharedCacheMode.ALL );
}
@Test
public void testGetEntityWithNullManyToOne() {
sessionFactory().getCache().evictAllRegions();
sessionFactory().getStatistics().clear();
int id = doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = new AnEntity();
session.persist( anEntity );
return anEntity.id;
}
);
assertEquals( 1, getEntity2LCStatistics( AnEntity.class ).getPutCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getPutCount() );
sessionFactory().getStatistics().clear();
doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = session.find( AnEntity.class, id );
assertNotNull( anEntity );
assertNull( anEntity.otherEntity );
}
);
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getPutCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getPutCount() );
assertEquals( 1, getEntity2LCStatistics( AnEntity.class ).getHitCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getHitCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getMissCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getMissCount() );
}
@Test
public void testQueryEntityWithNullManyToOne() {
sessionFactory().getCache().evictAllRegions();
sessionFactory().getStatistics().clear();
int id = doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = new AnEntity();
session.persist( anEntity );
return anEntity.id;
}
);
assertEquals( 1, getEntity2LCStatistics( AnEntity.class ).getPutCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getPutCount() );
sessionFactory().getStatistics().clear();
final String queryString = "from AnEntity where id = " + id;
doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = session.createQuery(
queryString,
AnEntity.class
).setCacheable( true ).uniqueResult();
assertNull( anEntity.otherEntity );
}
);
assertEquals( 0, getQueryStatistics( queryString ).getCacheHitCount() );
assertEquals( 1, getQueryStatistics( queryString ).getCacheMissCount() );
assertEquals( 1, getQueryStatistics( queryString ).getCachePutCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getPutCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getPutCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getHitCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getHitCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getMissCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getMissCount() );
sessionFactory().getStatistics().clear();
doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = session.createQuery(
queryString,
AnEntity.class
).setCacheable( true ).uniqueResult();
assertNull( anEntity.otherEntity );
}
);
assertEquals( 1, getQueryStatistics( queryString ).getCacheHitCount() );
assertEquals( 0, getQueryStatistics( queryString ).getCacheMissCount() );
assertEquals( 0, getQueryStatistics( queryString ).getCachePutCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getPutCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getPutCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getHitCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getHitCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getMissCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getMissCount() );
}
@Test
public void testQueryEntityJoinFetchNullManyToOne() {
sessionFactory().getCache().evictAllRegions();
sessionFactory().getStatistics().clear();
int id = doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = new AnEntity();
session.persist( anEntity );
return anEntity.id;
}
);
assertEquals( 1, getEntity2LCStatistics( AnEntity.class ).getPutCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getPutCount() );
sessionFactory().getStatistics().clear();
final String queryString = "from AnEntity e join fetch e.otherEntity where e.id = " + id;
doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = session.createQuery(
queryString,
AnEntity.class
).setCacheable( true ).uniqueResult();
assertNull( anEntity );
}
);
assertEquals( 0, getQueryStatistics( queryString ).getCacheHitCount() );
assertEquals( 1, getQueryStatistics( queryString ).getCacheMissCount() );
assertEquals( 1, getQueryStatistics( queryString ).getCachePutCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getPutCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getPutCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getHitCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getHitCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getMissCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getMissCount() );
sessionFactory().getStatistics().clear();
doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = session.createQuery(
queryString,
AnEntity.class
).setCacheable( true ).uniqueResult();
assertNull( anEntity );
}
);
assertEquals( 1, getQueryStatistics( queryString ).getCacheHitCount() );
assertEquals( 0, getQueryStatistics( queryString ).getCacheMissCount() );
assertEquals( 0, getQueryStatistics( queryString ).getCachePutCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getPutCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getPutCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getHitCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getHitCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getMissCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getMissCount() );
}
@Test
public void testQueryEntityLeftJoinFetchNullManyToOne() {
sessionFactory().getCache().evictAllRegions();
sessionFactory().getStatistics().clear();
int id = doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = new AnEntity();
session.persist( anEntity );
return anEntity.id;
}
);
assertEquals( 1, getEntity2LCStatistics( AnEntity.class ).getPutCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getPutCount() );
sessionFactory().getStatistics().clear();
final String queryString = "from AnEntity e left join fetch e.otherEntity where e.id = " + id;
doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = session.createQuery(
queryString,
AnEntity.class
).setCacheable( true ).uniqueResult();
assertNull( anEntity.otherEntity );
}
);
assertEquals( 0, getQueryStatistics( queryString ).getCacheHitCount() );
assertEquals( 1, getQueryStatistics( queryString ).getCacheMissCount() );
assertEquals( 1, getQueryStatistics( queryString ).getCachePutCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getPutCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getPutCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getHitCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getHitCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getMissCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getMissCount() );
sessionFactory().getStatistics().clear();
doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = session.createQuery(
queryString,
AnEntity.class
).setCacheable( true ).uniqueResult();
assertNull( anEntity.otherEntity );
}
);
assertEquals( 1, getQueryStatistics( queryString ).getCacheHitCount() );
assertEquals( 0, getQueryStatistics( queryString ).getCacheMissCount() );
assertEquals( 0, getQueryStatistics( queryString ).getCachePutCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getPutCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getPutCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getHitCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getHitCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getMissCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getMissCount() );
}
@Test
public void testQueryEntityAndNullManyToOne() {
sessionFactory().getCache().evictAllRegions();
sessionFactory().getStatistics().clear();
int id = doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = new AnEntity();
session.persist( anEntity );
return anEntity.id;
}
);
assertEquals( 1, getEntity2LCStatistics( AnEntity.class ).getPutCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getPutCount() );
sessionFactory().getStatistics().clear();
final String queryString = "select e, e.otherEntity from AnEntity e left join e.otherEntity where e.id = " + id;
doInHibernate(
this::sessionFactory,
session -> {
final Object[] result = session.createQuery(
queryString,
Object[].class
).setCacheable( true ).uniqueResult();
assertEquals( 2, result.length );
assertTrue( AnEntity.class.isInstance( result[0] ) );
assertNull( result[1] );
}
);
assertEquals( 0, getQueryStatistics( queryString ).getCacheHitCount() );
assertEquals( 1, getQueryStatistics( queryString ).getCacheMissCount() );
assertEquals( 1, getQueryStatistics( queryString ).getCachePutCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getPutCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getPutCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getHitCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getHitCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getMissCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getMissCount() );
sessionFactory().getStatistics().clear();
doInHibernate(
this::sessionFactory,
session -> {
final Object[] result = session.createQuery(
queryString,
Object[].class
).setCacheable( true ).uniqueResult();
assertEquals( 2, result.length );
assertTrue( AnEntity.class.isInstance( result[0] ) );
assertNull( result[1] );
}
);
assertEquals( 1, getQueryStatistics( queryString ).getCacheHitCount() );
assertEquals( 0, getQueryStatistics( queryString ).getCacheMissCount() );
assertEquals( 0, getQueryStatistics( queryString ).getCachePutCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getPutCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getPutCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getHitCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getHitCount() );
assertEquals( 0, getEntity2LCStatistics( AnEntity.class ).getMissCount() );
assertEquals( 0, getEntity2LCStatistics( OtherEntity.class ).getMissCount() );
}
@Override
protected boolean isCleanupTestDataRequired() {
return true;
}
private CacheRegionStatistics getEntity2LCStatistics(Class<?> className) {
return sessionFactory().getStatistics()
.getDomainDataRegionStatistics( className.getName() );
}
private QueryStatistics getQueryStatistics(String queryString) {
return sessionFactory().getStatistics().getQueryStatistics( queryString );
}
@Entity(name = "AnEntity")
@Cacheable
public static class AnEntity {
@Id
private int id;
@ManyToOne
private OtherEntity otherEntity;
}
@Entity(name = "OtherEntity")
@Cacheable
public static class OtherEntity implements Serializable {
@Id
private String firstName;
@Id
private String lastName;
private String description;
@Override
public String toString() {
return "OtherEntity{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", description='" + description + '\'' +
'}';
}
}
}

View File

@ -1,202 +0,0 @@
/*
* 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.component.empty;
import java.io.Serializable;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
* @author Gail Badner
*/
public class EmptyCompositeManyToOneKeyTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] {
AnEntity.class,
OtherEntity.class
};
}
@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
configuration.getProperties().put( Environment.CREATE_EMPTY_COMPOSITES_ENABLED, "true" );
configuration.getProperties().put( Environment.USE_SECOND_LEVEL_CACHE, "false" );
}
@Test
@TestForIssue( jiraKey = "HHH-11922" )
public void testGetEntityWithNullManyToOne() {
int id = doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = new AnEntity();
session.persist( anEntity );
return anEntity.id;
}
);
doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = session.find( AnEntity.class, id );
assertNotNull( anEntity );
assertNull( anEntity.otherEntity );
}
);
}
@Test
@TestForIssue( jiraKey = "HHH-11922" )
public void testQueryEntityWithNullManyToOne() {
int id = doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = new AnEntity();
session.persist( anEntity );
return anEntity.id;
}
);
doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = session.createQuery(
"from AnEntity where id = " + id,
AnEntity.class
).uniqueResult();
assertNull( anEntity.otherEntity );
}
);
}
@Test
@TestForIssue( jiraKey = "HHH-11922" )
public void testQueryEntityJoinFetchNullManyToOne() {
int id = doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = new AnEntity();
session.persist( anEntity );
return anEntity.id;
}
);
doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = session.createQuery(
"from AnEntity e join fetch e.otherEntity where e.id = " + id,
AnEntity.class
).uniqueResult();
assertNull( anEntity );
}
);
}
@Test
@TestForIssue( jiraKey = "HHH-11922" )
public void testQueryEntityLeftJoinFetchNullManyToOne() {
int id = doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = new AnEntity();
session.persist( anEntity );
return anEntity.id;
}
);
doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = session.createQuery(
"from AnEntity e left join fetch e.otherEntity where e.id = " + id,
AnEntity.class
).uniqueResult();
assertNull( anEntity.otherEntity );
}
);
}
@Test
@TestForIssue( jiraKey = "HHH-11922" )
public void testQueryEntityAndNullManyToOne() {
int id = doInHibernate(
this::sessionFactory,
session -> {
final AnEntity anEntity = new AnEntity();
session.persist( anEntity );
return anEntity.id;
}
);
doInHibernate(
this::sessionFactory,
session -> {
final Object[] result = session.createQuery(
"select e, e.otherEntity from AnEntity e left join e.otherEntity where e.id = " + id,
Object[].class
).uniqueResult();
assertEquals( 2, result.length );
assertTrue( AnEntity.class.isInstance( result[0] ) );
assertNull( result[1] );
}
);
}
@Override
protected boolean isCleanupTestDataRequired() {
return true;
}
@Entity(name = "AnEntity")
public static class AnEntity {
@Id
private int id;
@ManyToOne
private OtherEntity otherEntity;
}
@Entity(name = "OtherEntity")
public static class OtherEntity implements Serializable {
@Id
private String firstName;
@Id
private String lastName;
private String description;
@Override
public String toString() {
return "OtherEntity{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", description='" + description + '\'' +
'}';
}
}
}

View File

@ -1,68 +0,0 @@
/*
* 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.component.empty;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
/**
* Test class for empty embedded dirtiness computation.
*
* @author Laurent Almeras
*/
public class EmptyCompositesDirtynessTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { ComponentEmptyEmbeddedOwner.class, ComponentEmptyEmbedded.class };
}
@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
configuration.getProperties().put( Environment.CREATE_EMPTY_COMPOSITES_ENABLED, Boolean.valueOf( false ) );
}
/**
* Test for dirtyness computation consistency when a property is an empty composite.
*/
@Test
@TestForIssue(jiraKey = "HHH-7610")
public void testCompositesEmpty() {
Session s = openSession();
try {
s.getTransaction().begin();
ComponentEmptyEmbeddedOwner owner = new ComponentEmptyEmbeddedOwner();
s.persist( owner );
s.flush();
s.getTransaction().commit();
s.clear();
s.getTransaction().begin();
owner = (ComponentEmptyEmbeddedOwner) s.get( ComponentEmptyEmbeddedOwner.class, owner.getId() );
assertNull( owner.getEmbedded() );
owner.setEmbedded( new ComponentEmptyEmbedded() );
// technically, as all properties are null, update may not be necessary
assertFalse( session.isDirty() ); // must be false to avoid unnecessary updates
s.getTransaction().rollback();
}
finally {
s.close();
}
}
}

View File

@ -1,66 +0,0 @@
/*
* 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.component.empty;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
/**
* Test class for empty embedded dirtiness computation.
*
* @author Laurent Almeras
*/
public class EmptyInitializedCompositesDirtynessTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { ComponentEmptyEmbeddedOwner.class, ComponentEmptyEmbedded.class };
}
@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
configuration.getProperties().put( Environment.CREATE_EMPTY_COMPOSITES_ENABLED, Boolean.valueOf( true ) );
}
/**
* Test for dirtyness computation consistency when a property is an empty composite and that empty composite
* initialization is set.
*/
@Test
@TestForIssue(jiraKey = "HHH-7610")
public void testInitializedCompositesEmpty() {
Session s = openSession();
try {
s.getTransaction().begin();
ComponentEmptyEmbeddedOwner owner = new ComponentEmptyEmbeddedOwner();
s.persist( owner );
s.flush();
s.getTransaction().commit();
s.clear();
s.getTransaction().begin();
owner = (ComponentEmptyEmbeddedOwner) s.get( ComponentEmptyEmbeddedOwner.class, owner.getId() );
assertNotNull( owner.getEmbedded() );
assertFalse( s.isDirty() );
s.getTransaction().rollback();
}
finally {
s.close();
}
}
}

View File

@ -1,68 +0,0 @@
/*
* 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.component.empty;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
/**
* Test class for empty embedded dirtiness computation.
*
* @author Laurent Almeras
*/
public class EmptyInitializedCompositesTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { ComponentEmptyEmbeddedOwner.class, ComponentEmptyEmbedded.class };
}
@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
configuration.getProperties().put( Environment.CREATE_EMPTY_COMPOSITES_ENABLED, Boolean.valueOf( true ) );
}
/**
* Test empty composite initialization.
*/
@Test
@TestForIssue(jiraKey = "HHH-7610")
public void testCompositesEmpty() {
Session s = openSession();
try {
s.getTransaction().begin();
ComponentEmptyEmbeddedOwner owner = new ComponentEmptyEmbeddedOwner();
s.persist( owner );
s.flush();
s.getTransaction().commit();
s.clear();
s.getTransaction().begin();
owner = (ComponentEmptyEmbeddedOwner) s.get( ComponentEmptyEmbeddedOwner.class, owner.getId() );
assertNotNull( owner.getEmbedded() );
assertFalse( s.isDirty() );
owner.setEmbedded( null );
assertFalse( s.isDirty() ); // must be false to avoid unnecessary updates
s.getTransaction().rollback();
}
finally {
s.close();
}
}
}

View File

@ -1,114 +0,0 @@
/*
* 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.component.empty;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/**
* Tests that an empty embeddable that is nested inside an embeddable is initialized.
*
* @author Gail Badner
*/
@TestForIssue(jiraKey = "HHH-11926")
@DomainModel(
annotatedClasses = EmptyInitializedNestedCompositesTest.ComponentEmptyNestedEmbeddedOwner.class
)
@SessionFactory
@ServiceRegistry(
settings = @Setting(name = Environment.CREATE_EMPTY_COMPOSITES_ENABLED, value = "true")
)
public class EmptyInitializedNestedCompositesTest {
/**
* Test empty nested composite initialization.
*/
@Test
public void testCompositesEmpty(SessionFactoryScope scope) {
scope.inSession(
session -> {
session.beginTransaction();
try {
ComponentEmptyNestedEmbeddedOwner owner = new ComponentEmptyNestedEmbeddedOwner();
session.persist( owner );
session.flush();
session.getTransaction().commit();
session.clear();
session.getTransaction().begin();
owner = session.get( ComponentEmptyNestedEmbeddedOwner.class, owner.getId() );
assertNotNull( owner.getEmbedded() );
assertNotNull( owner.getEmbedded().getNestedEmbedded() );
session.getTransaction().commit();
}
finally {
if ( session.getTransaction().isActive() ) {
session.getTransaction().rollback();
}
}
}
);
}
@Entity(name = "EmptyNestedOwner")
public static class ComponentEmptyNestedEmbeddedOwner {
@Id
@GeneratedValue
private Integer id;
private EmptyNestedEmbeddedContainer embedded;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public EmptyNestedEmbeddedContainer getEmbedded() {
return embedded;
}
public void setEmbedded(EmptyNestedEmbeddedContainer embedded) {
this.embedded = embedded;
}
}
@Embeddable
public static class EmptyNestedEmbeddedContainer {
public ComponentEmptyEmbedded getNestedEmbedded() {
return nestedEmbedded;
}
public void setNestedEmbedded(ComponentEmptyEmbedded nestedEmbedded) {
this.nestedEmbedded = nestedEmbedded;
}
private ComponentEmptyEmbedded nestedEmbedded;
}
}

View File

@ -17,7 +17,6 @@ import jakarta.persistence.Version;
import org.hibernate.EmptyInterceptor; import org.hibernate.EmptyInterceptor;
import org.hibernate.annotations.SelectBeforeUpdate; import org.hibernate.annotations.SelectBeforeUpdate;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Configuration;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import org.junit.Test; import org.junit.Test;
@ -48,25 +47,16 @@ public class SelectBeforeUpdateEmbeddedTest extends BaseCoreFunctionalTestCase {
configuration.setInterceptor( i ); configuration.setInterceptor( i );
} }
@Test
@TestForIssue(jiraKey = "HHH-11237")
public void testSelectBeforeUpdateUsingEmptyComposites() {
// Opt-in behavior 5.1+
rebuildSessionFactory( c -> c.setProperty( AvailableSettings.CREATE_EMPTY_COMPOSITES_ENABLED, "true" ) );
testSelectBeforeUpdate();
}
@Test @Test
@TestForIssue(jiraKey = "HHH-11237") @TestForIssue(jiraKey = "HHH-11237")
public void testSelectBeforeUpdateUsingNullComposites() { public void testSelectBeforeUpdateUsingNullComposites() {
// Legacy behavior test // Legacy behavior test
rebuildSessionFactory( c -> c.setProperty( AvailableSettings.CREATE_EMPTY_COMPOSITES_ENABLED, "false" ) );
testSelectBeforeUpdate(); testSelectBeforeUpdate();
} }
/** /**
* Performs various tests both available attached and detached entities which use * Performs various tests both available attached and detached entities which use
* the {@code @SelectBeforeUpdate} annotation with an {code @Embedded} component. * the {@code @SelectBeforeUpdate} annotation with an {@code @Embedded} component.
*/ */
private void testSelectBeforeUpdate() { private void testSelectBeforeUpdate() {
TransactionUtil.doInHibernate( this::sessionFactory, session -> { TransactionUtil.doInHibernate( this::sessionFactory, session -> {

View File

@ -1,165 +0,0 @@
/*
* 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.where.annotations;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import jakarta.persistence.CascadeType;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.SQLRestriction;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
/**
* @author Gail Badner
*/
public class EagerManyToOneFetchModeSelectWhereTest extends BaseNonConfigCoreFunctionalTestCase {
@Override
protected void addSettings(Map<String,Object> settings) {
settings.put( AvailableSettings.CREATE_EMPTY_COMPOSITES_ENABLED, true );
}
@Override
protected Class[] getAnnotatedClasses() {
return new Class[] { Product.class, Category.class };
}
@Test
@TestForIssue( jiraKey = "HHH-12104" )
public void testAssociatedWhereClause() {
Product product = new Product();
Category category = new Category();
category.name = "flowers";
product.category = category;
product.containedCategory = new ContainedCategory();
product.containedCategory.category = category;
product.containedCategories.add( new ContainedCategory( category ) );
doInHibernate(
this::sessionFactory,
session -> {
session.persist( product );
}
);
doInHibernate(
this::sessionFactory,
session -> {
Product p = session.get( Product.class, product.id );
assertNotNull( p );
assertNotNull( p.category );
assertNotNull( p.containedCategory.category );
assertEquals( 1, p.containedCategories.size() );
assertSame( p.category, p.containedCategory.category );
assertSame( p.category, p.containedCategories.iterator().next().category );
}
);
doInHibernate(
this::sessionFactory,
session -> {
Category c = session.get( Category.class, category.id );
assertNotNull( c );
c.inactive = 1;
}
);
doInHibernate(
this::sessionFactory,
session -> {
Category c = session.get( Category.class, category.id );
assertNull( c );
}
);
doInHibernate(
this::sessionFactory,
session -> {
// Entity's where clause is taken into account when to-one associations
// to that entity is loaded eagerly using FetchMode.SELECT, so Category
// associations will be null.
Product p = session.get( Product.class, product.id );
assertNotNull( p );
assertNull( p.category );
assertNull( p.containedCategory.category );
assertEquals( 1, p.containedCategories.size() );
assertNull( p.containedCategories.iterator().next().category );
}
);
}
@Entity(name = "Product")
public static class Product {
@Id
@GeneratedValue
private int id;
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@NotFound(action = NotFoundAction.IGNORE)
@JoinColumn(name = "categoryId")
@Fetch(FetchMode.SELECT)
private Category category;
private ContainedCategory containedCategory;
@ElementCollection(fetch = FetchType.EAGER)
private Set<ContainedCategory> containedCategories = new HashSet<>();
}
@Entity(name = "Category")
@Table(name = "CATEGORY")
@SQLRestriction("inactive = 0")
public static class Category {
@Id
@GeneratedValue
private int id;
private String name;
private int inactive;
}
@Embeddable
public static class ContainedCategory {
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@NotFound(action = NotFoundAction.IGNORE)
@JoinColumn(name = "containedCategoryId")
@Fetch(FetchMode.SELECT)
private Category category;
public ContainedCategory() {
}
public ContainedCategory(Category category) {
this.category = category;
}
}
}

View File

@ -27,11 +27,6 @@ import static org.junit.Assert.assertSame;
*/ */
public class EagerManyToOneFetchModeSelectWhereTest extends BaseNonConfigCoreFunctionalTestCase { public class EagerManyToOneFetchModeSelectWhereTest extends BaseNonConfigCoreFunctionalTestCase {
@Override
protected void addSettings(Map<String,Object> settings) {
settings.put( AvailableSettings.CREATE_EMPTY_COMPOSITES_ENABLED, true );
}
@Override @Override
protected String getBaseForMappings() { protected String getBaseForMappings() {
return "org/hibernate/orm/test/"; return "org/hibernate/orm/test/";
@ -99,9 +94,8 @@ public class EagerManyToOneFetchModeSelectWhereTest extends BaseNonConfigCoreFun
Product p = session.get( Product.class, product.id ); Product p = session.get( Product.class, product.id );
assertNotNull( p ); assertNotNull( p );
assertNull( p.category ); assertNull( p.category );
assertNull( p.containedCategory.category ); assertNull( p.containedCategory );
assertEquals( 1, p.containedCategories.size() ); assertEquals( 0, p.containedCategories.size() );
assertNull( p.containedCategories.iterator().next().category );
} }
); );
} }