HHH-14348 Special handling in bytecode enhancement for lazy PersistentCollection fields
This commit is contained in:
parent
36d9ec4040
commit
b08fbdaa6e
|
@ -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 {
|
||||
|
|
|
@ -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++ );
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -169,8 +169,7 @@ public class EntityMetamodel implements Serializable {
|
|||
persistentClass,
|
||||
idAttributeNames,
|
||||
nonAggregatedCidMapper,
|
||||
sessionFactoryOptions.isEnhancementAsProxyEnabled(),
|
||||
sessionFactoryOptions.isCollectionsInDefaultFetchGroupEnabled()
|
||||
sessionFactoryOptions.isEnhancementAsProxyEnabled()
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue