[JIRA] (HHH-15730) Hibernate throws NullPointerException if loading an Entity that contains a null Embedded field and enableDirtyTracking = true
by Johan Steenkamp (JIRA)
Johan Steenkamp ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=6374fb5... ) *created* an issue
Hibernate ORM ( https://hibernate.atlassian.net/browse/HHH?atlOrigin=eyJpIjoiMzBkNzI3Nzcw... ) / Bug ( https://hibernate.atlassian.net/browse/HHH-15730?atlOrigin=eyJpIjoiMzBkNz... ) HHH-15730 ( https://hibernate.atlassian.net/browse/HHH-15730?atlOrigin=eyJpIjoiMzBkNz... ) Hibernate throws NullPointerException if loading an Entity that contains a null Embedded field and enableDirtyTracking = true ( https://hibernate.atlassian.net/browse/HHH-15730?atlOrigin=eyJpIjoiMzBkNz... )
Issue Type: Bug Affects Versions: 6.1.5 Assignee: Unassigned Components: bytecode-enhancement Created: 16/Nov/2022 08:22 AM Environment: Hibernate 6.1.5
JDK 19
Both Windows and Linux
SQL Server Priority: Critical Reporter: Johan Steenkamp ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=6374fb5... )
I have a Customer entity that is outlined like this:
@Entity
public class Customer {
...
@Embedded()
private Address postalAddress;
...
The Address Embeddable looks like this:
@Embeddable
public class Address implements Serializable {
@Basic
@Size(max = 50)
private String address1;
@Basic
@Size(max = 50)
private String address2;
@Basic
@Size(max = 50)
private String address3;
@Basic
@Size(max = 50)
private String city;
@Basic
@Size(max = 10)
private String postalCode;
public Address() {
super ();
}
public Address( final String address1, final String address2, final String address3, final String city,
final String postalCode) {
this ();
setAddress1(address1);
setAddress2(address2);
setAddress3(address3);
setCity(city);
setPostalCode(postalCode);
}
// Getters and setters
...
}
Now, if I Hibernate loads a Customer entity from the Database where all the address fields are null, it will set the postalAddress address reference in the resulting Customer Object also to null, which I think is correct. In general this null postalAddress reference doesn't cause any issue.
However, things start going wrong if I set enableDirtyTracking to true in gradle like this:
hibernate {
enhancement {
enableLazyInitialization = false
enableDirtyTracking = true
enableAssociationManagement = false
enableExtendedEnhancement = false
}
}
With such a setup, as soon as Hibernate tries load a customer that have a resulting null postalAddress reference, I get the following exception:
Caused by: java.lang.NullPointerException: Cannot invoke "org.hibernate.engine.spi.CompositeTracker.$$_hibernate_setOwner( String , org.hibernate.engine.spi.CompositeOwner)" because "value" is null
at org.hibernate(a)6.1.5.Final //org.hibernate.property.access.spi.EnhancedSetterImpl.set(EnhancedSetterImpl.java:53)
at org.hibernate(a)6.1.5.Final //org.hibernate.persister.entity.AbstractEntityPersister.setPropertyValues(AbstractEntityPersister.java:5140)
at org.hibernate(a)6.1.5.Final //org.hibernate.sql.results.graph.entity.AbstractEntityInitializer.initializeEntity(AbstractEntityInitializer.java:722)
at org.hibernate(a)6.1.5.Final //org.hibernate.sql.results.graph.entity.AbstractEntityInitializer.initializeInstance(AbstractEntityInitializer.java:655)
at org.hibernate(a)6.1.5.Final //org.hibernate.sql.results.internal.domain.CircularBiDirectionalFetchImpl$CircularFetchAssembler.assemble(CircularBiDirectionalFetchImpl.java:282)
at org.hibernate(a)6.1.5.Final //org.hibernate.sql.results.graph.DomainResultAssembler.assemble(DomainResultAssembler.java:33)
at org.hibernate(a)6.1.5.Final //org.hibernate.metamodel.mapping.EntityMappingType.lambda$extractConcreteTypeStateValues$1(EntityMappingType.java:331)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.hibernate(a)6.1.5.Final //org.hibernate.persister.entity.AbstractEntityPersister.visitAttributeMappings(AbstractEntityPersister.java:5685)
at org.hibernate(a)6.1.5.Final //org.hibernate.metamodel.mapping.EntityMappingType.extractConcreteTypeStateValues(EntityMappingType.java:323)
at org.hibernate(a)6.1.5.Final //org.hibernate.sql.results.graph.entity.AbstractEntityInitializer.initializeEntity(AbstractEntityInitializer.java:706)
at org.hibernate(a)6.1.5.Final //org.hibernate.sql.results.graph.entity.AbstractEntityInitializer.initializeInstance(AbstractEntityInitializer.java:662)
at org.hibernate(a)6.1.5.Final //org.hibernate.sql.results.internal.StandardRowReader.coordinateInitializers(StandardRowReader.java:153)
at org.hibernate(a)6.1.5.Final //org.hibernate.sql.results.internal.StandardRowReader.readRow(StandardRowReader.java:98)
at org.hibernate(a)6.1.5.Final //org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:143)
at org.hibernate(a)6.1.5.Final //org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:32)
at org.hibernate(a)6.1.5.Final //org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.doExecuteQuery(JdbcSelectExecutorStandardImpl.java:443)
at org.hibernate(a)6.1.5.Final //org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.executeQuery(JdbcSelectExecutorStandardImpl.java:166)
at org.hibernate(a)6.1.5.Final //org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.list(JdbcSelectExecutorStandardImpl.java:91)
at org.hibernate(a)6.1.5.Final //org.hibernate.sql.exec.spi.JdbcSelectExecutor.list(JdbcSelectExecutor.java:31)
at org.hibernate(a)6.1.5.Final //org.hibernate.loader.ast.internal.SingleIdLoadPlan.load(SingleIdLoadPlan.java:140)
at org.hibernate(a)6.1.5.Final //org.hibernate.loader.ast.internal.SingleIdEntityLoaderStandardImpl.load(SingleIdEntityLoaderStandardImpl.java:88)
at org.hibernate(a)6.1.5.Final //org.hibernate.loader.ast.internal.SingleIdEntityLoaderDynamicBatch.load(SingleIdEntityLoaderDynamicBatch.java:78)
at org.hibernate(a)6.1.5.Final //org.hibernate.loader.ast.internal.SingleIdEntityLoaderDynamicBatch.load(SingleIdEntityLoaderDynamicBatch.java:60)
at org.hibernate(a)6.1.5.Final //org.hibernate.persister.entity.AbstractEntityPersister.doLoad(AbstractEntityPersister.java:4401)
at org.hibernate(a)6.1.5.Final //org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:4391)
at org.hibernate(a)6.1.5.Final //org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:599)
at org.hibernate(a)6.1.5.Final //org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:572)
at org.hibernate(a)6.1.5.Final //org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:224)
at org.hibernate(a)6.1.5.Final //org.hibernate.event.internal.DefaultLoadEventListener.doOnLoad(DefaultLoadEventListener.java:108)
at org.hibernate(a)6.1.5.Final //org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:75)
at org.hibernate(a)6.1.5.Final //org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:118)
at org.hibernate(a)6.1.5.Final //org.hibernate.internal.SessionImpl.fireLoadNoChecks(SessionImpl.java:1241)
at org.hibernate(a)6.1.5.Final //org.hibernate.internal.SessionImpl.immediateLoad(SessionImpl.java:1045)
at org.hibernate(a)6.1.5.Final //org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:173)
at org.hibernate(a)6.1.5.Final //org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:309)
at org.hibernate(a)6.1.5.Final //org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:44)
at org.hibernate(a)6.1.5.Final //org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:95)
I had a look at the source code of EnhancedSetterImpl.java, where the exception occurs, which I have outlined below. If I have enabled enableDirtyTracking , then it does go into the first if statement shown. In such a case *value* will be null for postalAddress and will cause the NullpointerException.
I belief this should be a bug in how enableDirtyTracking works, since it does works fine if I don't enable this.
I have tried the same with 6.1.6 by getting the source from Github and compiling it, but I get the same error.
@Override
public void set( Object target, Object value) {
super.set( target, value );
// This sets the component relation for dirty tracking purposes
if ( ( enhancementState & COMPOSITE_OWNER ) != 0 && ( ( enhancementState & COMPOSITE_TRACKER_MASK ) != 0 || value instanceof CompositeTracker ) ) {
( (CompositeTracker) value ).$$_hibernate_setOwner( propertyName, (CompositeOwner) target );
}
// This marks the attribute as initialized, so it doesn't get lazily loaded afterwards
if ( ( enhancementState & PERSISTENT_ATTRIBUTE_INTERCEPTABLE_MASK ) != 0 ) {
final PersistentAttributeInterceptable asPersistentAttributeInterceptable = ManagedTypeHelper.asPersistentAttributeInterceptable(
target );
PersistentAttributeInterceptor interceptor = asPersistentAttributeInterceptable.$$_hibernate_getInterceptor();
if ( interceptor instanceof BytecodeLazyAttributeInterceptor ) {
( (BytecodeLazyAttributeInterceptor) interceptor ).attributeInitialized( propertyName );
}
}
}
( https://hibernate.atlassian.net/browse/HHH-15730#add-comment?atlOrigin=ey... ) Add Comment ( https://hibernate.atlassian.net/browse/HHH-15730#add-comment?atlOrigin=ey... )
Get Jira notifications on your phone! Download the Jira Cloud app for Android ( https://play.google.com/store/apps/details?id=com.atlassian.android.jira.... ) or iOS ( https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=EmailN... ) This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100210- sha1:9b34d7c )