HHH-14348 Special handling in bytecode enhancement for lazy PersistentCollection fields

This commit is contained in:
Christian Beikov 2020-11-25 15:06:02 +01:00 committed by Sanne Grinovero
parent 34151a9660
commit 7dd49e425e
5 changed files with 154 additions and 22 deletions

View File

@ -20,6 +20,7 @@ import org.hibernate.bytecode.enhance.internal.tracker.SimpleFieldTracker;
import org.hibernate.bytecode.enhance.spi.CollectionTracker;
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.spi.CompositeOwner;
import org.hibernate.engine.spi.CompositeTracker;
import org.hibernate.engine.spi.ExtendedSelfDirtinessTracker;
@ -170,11 +171,16 @@ class CodeTemplates {
@FieldValue Collection<?> collection,
@Advice.FieldValue(EnhancerConstants.TRACKER_COLLECTION_NAME) CollectionTracker $$_hibernate_collectionTracker) {
if ( !returned && $$_hibernate_collectionTracker != null ) {
if ( collection == null && $$_hibernate_collectionTracker.getSize( fieldName ) != -1 ) {
final int size = $$_hibernate_collectionTracker.getSize( fieldName );
if ( collection == null && size != -1 ) {
returned = true;
}
else if ( collection != null && $$_hibernate_collectionTracker.getSize( fieldName ) != collection.size() ) {
returned = true;
else if ( collection != null ) {
// We only check sizes of non-persistent or initialized persistent collections
if ( ( !( collection instanceof PersistentCollection ) || ( (PersistentCollection) collection ).wasInitialized() )
&& size != collection.size() ) {
returned = true;
}
}
}
}
@ -188,11 +194,16 @@ class CodeTemplates {
@FieldValue Map<?, ?> map,
@Advice.FieldValue(EnhancerConstants.TRACKER_COLLECTION_NAME) CollectionTracker $$_hibernate_collectionTracker) {
if ( !returned && $$_hibernate_collectionTracker != null ) {
if ( map == null && $$_hibernate_collectionTracker.getSize( fieldName ) != -1 ) {
final int size = $$_hibernate_collectionTracker.getSize( fieldName );
if ( map == null && size != -1 ) {
returned = true;
}
else if ( map != null && $$_hibernate_collectionTracker.getSize( fieldName ) != map.size() ) {
returned = true;
else if ( map != null ) {
// We only check sizes of non-persistent or initialized persistent collections
if ( ( !( map instanceof PersistentCollection ) || ( (PersistentCollection) map ).wasInitialized() )
&& size != map.size() ) {
returned = true;
}
}
}
}
@ -206,11 +217,16 @@ class CodeTemplates {
@Advice.Argument(0) DirtyTracker tracker,
@Advice.FieldValue(EnhancerConstants.TRACKER_COLLECTION_NAME) CollectionTracker $$_hibernate_collectionTracker) {
if ( $$_hibernate_collectionTracker != null ) {
if ( collection == null && $$_hibernate_collectionTracker.getSize( fieldName ) != -1 ) {
final int size = $$_hibernate_collectionTracker.getSize( fieldName );
if ( collection == null && size != -1 ) {
tracker.add( fieldName );
}
else if ( collection != null && $$_hibernate_collectionTracker.getSize( fieldName ) != collection.size() ) {
tracker.add( fieldName );
else if ( collection != null ) {
// We only check sizes of non-persistent or initialized persistent collections
if ( ( !( collection instanceof PersistentCollection ) || ( (PersistentCollection) collection ).wasInitialized() )
&& size != collection.size() ) {
tracker.add( fieldName );
}
}
}
}
@ -224,11 +240,16 @@ class CodeTemplates {
@Advice.Argument(0) DirtyTracker tracker,
@Advice.FieldValue(EnhancerConstants.TRACKER_COLLECTION_NAME) CollectionTracker $$_hibernate_collectionTracker) {
if ( $$_hibernate_collectionTracker != null ) {
if ( map == null && $$_hibernate_collectionTracker.getSize( fieldName ) != -1 ) {
final int size = $$_hibernate_collectionTracker.getSize( fieldName );
if ( map == null && size != -1 ) {
tracker.add( fieldName );
}
else if ( map != null && $$_hibernate_collectionTracker.getSize( fieldName ) != map.size() ) {
tracker.add( fieldName );
else if ( map != null ) {
// We only check sizes of non-persistent or initialized persistent collections
if ( ( !( map instanceof PersistentCollection ) || ( (PersistentCollection) map ).wasInitialized() )
&& size != map.size() ) {
tracker.add( fieldName );
}
}
}
}
@ -242,7 +263,8 @@ class CodeTemplates {
@Advice.Argument(value = 0, readOnly = false) LazyAttributeLoadingInterceptor lazyInterceptor,
@Advice.FieldValue(EnhancerConstants.TRACKER_COLLECTION_NAME) CollectionTracker $$_hibernate_collectionTracker) {
if ( lazyInterceptor == null || lazyInterceptor.isAttributeLoaded( fieldName ) ) {
if ( collection == null ) {
if ( collection == null || collection instanceof PersistentCollection && !( (PersistentCollection) collection )
.wasInitialized() ) {
$$_hibernate_collectionTracker.add( fieldName, -1 );
}
else {
@ -260,7 +282,8 @@ class CodeTemplates {
@Advice.Argument(value = 0, readOnly = false) LazyAttributeLoadingInterceptor lazyInterceptor,
@Advice.FieldValue(EnhancerConstants.TRACKER_COLLECTION_NAME) CollectionTracker $$_hibernate_collectionTracker) {
if ( lazyInterceptor == null || lazyInterceptor.isAttributeLoaded( fieldName ) ) {
if ( map == null ) {
if ( map == null || map instanceof PersistentCollection && !( (PersistentCollection) map )
.wasInitialized() ) {
$$_hibernate_collectionTracker.add( fieldName, -1 );
}
else {

View File

@ -34,8 +34,7 @@ public class LazyAttributesMetadata implements Serializable {
public static LazyAttributesMetadata from(
PersistentClass mappedEntity,
boolean isEnhanced,
boolean allowEnhancementAsProxy,
boolean collectionsInDefaultFetchGroupEnabled) {
boolean allowEnhancementAsProxy) {
final Map<String, LazyAttributeDescriptor> lazyAttributeDescriptorMap = new LinkedHashMap<>();
final Map<String, Set<String>> fetchGroupToAttributesMap = new HashMap<>();
@ -49,7 +48,7 @@ public class LazyAttributesMetadata implements Serializable {
property,
isEnhanced,
allowEnhancementAsProxy,
collectionsInDefaultFetchGroupEnabled
false
);
if ( lazy ) {
final LazyAttributeDescriptor lazyAttributeDescriptor = LazyAttributeDescriptor.from( property, i, x++ );

View File

@ -38,12 +38,11 @@ public final class BytecodeEnhancementMetadataPojoImpl implements BytecodeEnhanc
PersistentClass persistentClass,
Set<String> identifierAttributeNames,
CompositeType nonAggregatedCidMapper,
boolean allowEnhancementAsProxy,
boolean collectionsInDefaultFetchGroupEnabled) {
boolean allowEnhancementAsProxy) {
final Class mappedClass = persistentClass.getMappedClass();
final boolean enhancedForLazyLoading = PersistentAttributeInterceptable.class.isAssignableFrom( mappedClass );
final LazyAttributesMetadata lazyAttributesMetadata = enhancedForLazyLoading
? LazyAttributesMetadata.from( persistentClass, true, allowEnhancementAsProxy, collectionsInDefaultFetchGroupEnabled )
? LazyAttributesMetadata.from( persistentClass, true, allowEnhancementAsProxy )
: LazyAttributesMetadata.nonEnhanced( persistentClass.getEntityName() );
return new BytecodeEnhancementMetadataPojoImpl(

View File

@ -169,8 +169,7 @@ public class EntityMetamodel implements Serializable {
persistentClass,
idAttributeNames,
nonAggregatedCidMapper,
sessionFactoryOptions.isEnhancementAsProxyEnabled(),
sessionFactoryOptions.isCollectionsInDefaultFetchGroupEnabled()
sessionFactoryOptions.isEnhancementAsProxyEnabled()
);
}
else {

View File

@ -0,0 +1,112 @@
/*
* 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.test.bytecode.enhancement.dirty;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.hibernate.boot.internal.SessionFactoryBuilderImpl;
import org.hibernate.boot.internal.SessionFactoryOptionsBuilder;
import org.hibernate.boot.spi.SessionFactoryBuilderService;
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
import org.hibernate.cfg.Configuration;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.SelfDirtinessTracker;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author Christian Beikov
*/
@TestForIssue( jiraKey = "HHH-14348" )
@RunWith( BytecodeEnhancerRunner.class )
public class DirtyTrackingCollectionInDefaultFetchGroupTest extends BaseCoreFunctionalTestCase {
@Override
public Class<?>[] getAnnotatedClasses() {
return new Class<?>[]{StringsEntity.class};
}
@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
configuration.getStandardServiceRegistryBuilder().addService(
SessionFactoryBuilderService.class,
(SessionFactoryBuilderService) (metadata, bootstrapContext) -> {
SessionFactoryOptionsBuilder optionsBuilder = new SessionFactoryOptionsBuilder(
metadata.getMetadataBuildingOptions().getServiceRegistry(),
bootstrapContext
);
optionsBuilder.enableCollectionInDefaultFetchGroup( true );
return new SessionFactoryBuilderImpl( metadata, optionsBuilder );
}
);
}
@Before
public void prepare() {
doInJPA( this::sessionFactory, em -> {
StringsEntity entity = new StringsEntity();
entity.id = 1L;
entity.someStrings = new ArrayList<>( Arrays.asList( "a", "b", "c" ) );
em.persist( entity );
} );
}
@Test
public void test() {
doInJPA( this::sessionFactory, entityManager -> {
StringsEntity entity = entityManager.find( StringsEntity.class, 1L );
entityManager.flush();
BytecodeLazyAttributeInterceptor interceptor = (BytecodeLazyAttributeInterceptor) ( (PersistentAttributeInterceptable) entity )
.$$_hibernate_getInterceptor();
assertTrue( interceptor.hasAnyUninitializedAttributes() );
assertFalse( interceptor.isAttributeLoaded( "someStrings" ) );
assertFalse( interceptor.isAttributeLoaded( "someStringEntities" ) );
} );
}
// --- //
@Entity
@Table( name = "STRINGS_ENTITY" )
private static class StringsEntity {
@Id
Long id;
@ElementCollection
List<String> someStrings;
@ManyToOne(fetch = FetchType.LAZY)
StringsEntity parent;
@OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
Set<StringsEntity> someStringEntities;
}
}