Fix EntityEntry loaded state for persistent arrays

This commit is contained in:
Andrea Boriero 2021-12-02 14:11:08 +01:00 committed by Andrea Boriero
parent 6c4ec95182
commit 189bc54dbd
3 changed files with 232 additions and 1 deletions

View File

@ -828,7 +828,7 @@ public interface EntityPersister
Object[] getPropertyValues(Object object);
default Object getValue(Object object, int i) {
return getValue( object, i );
return getPropertyValue( object, i );
}
/**

View File

@ -18,9 +18,11 @@ import org.hibernate.cache.spi.access.CollectionDataAccess;
import org.hibernate.cache.spi.entry.CollectionCacheEntry;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.collection.internal.PersistentArrayHolder;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.spi.BatchFetchQueue;
import org.hibernate.engine.spi.CollectionEntry;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionEventListenerManager;
import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -28,6 +30,7 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister;
@ -145,6 +148,14 @@ public class ResultsHelper {
}
if ( collectionDescriptor.getCollectionType().hasHolder() ) {
// in case of PersistentArrayHolder we have to realign the EntityEntry loaded state with
// the entity values
final Object owner = collectionInstance.getOwner();
final EntityEntry entry = persistenceContext.getEntry( owner );
final PluralAttributeMapping mapping = collectionDescriptor.getAttributeMapping();
final int propertyIndex = mapping.getStateArrayPosition();
final Object[] loadedState = entry.getLoadedState();
loadedState[propertyIndex] = mapping.getValue( owner );
persistenceContext.addCollectionHolder( collectionInstance );
}

View File

@ -0,0 +1,220 @@
package org.hibernate.orm.test.collection;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.dialect.SybaseDialect;
import org.hibernate.event.spi.EventType;
import org.hibernate.event.spi.PostUpdateEvent;
import org.hibernate.event.spi.PostUpdateEventListener;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.SkipForDialect;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OrderColumn;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
@DomainModel(
annotatedClasses = {
LoadEntityWithElementCollectionTest.IndexedEntity.class
}
)
@SessionFactory
@SkipForDialect(dialectClass = SybaseDialect.class, matchSubTypes = true)
public class LoadEntityWithElementCollectionTest {
public static class PostUpdateTestListener implements PostUpdateEventListener {
boolean isPostUpdateCalled;
@Override
public void onPostUpdate(PostUpdateEvent event) {
isPostUpdateCalled = true;
}
@Override
public boolean requiresPostCommitHanding(EntityPersister persister) {
return false;
}
public boolean isPostUpdateCalled() {
return isPostUpdateCalled;
}
}
@BeforeEach
public void setUp(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
IndexedEntity entity = new IndexedEntity();
entity.setId( 1L );
entity.setSerializableArray( new boolean[] { true, false } );
session.persist( entity );
IndexedEntity entity2 = new IndexedEntity();
entity2.setId( 2L );
entity2.setElementCollectionArray( new boolean[] { true, false } );
final HashSet<IndexedEntity> indexedEntities = new HashSet<>();
indexedEntities.add( entity );
entity2.setIndexedEntities( indexedEntities );
session.persist( entity2 );
}
);
}
@AfterEach
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "delete from IndexedEntity" ).executeUpdate();
}
);
}
@Test
public void onPostUpdateMethodShouldBeCalledTest(SessionFactoryScope scope) {
final PostUpdateTestListener listener = new PostUpdateTestListener();
scope.getSessionFactory().getEventEngine().getListenerRegistry().setListeners(
EventType.POST_UPDATE,
listener
);
scope.inTransaction(
session -> {
final IndexedEntity indexedEntity = session.get( IndexedEntity.class, 1L );
assertNotNull( indexedEntity.getElementCollectionArray() );
assertEquals( 0, indexedEntity.getElementCollectionArray().length );
assertNotNull( indexedEntity.getSerializableArray() );
indexedEntity.setSerializableArray( new boolean[] { false, true } );
}
);
assertTrue( listener.isPostUpdateCalled() );
}
@Test
public void onPostUpdateMethodShouldBeCalledTest2(SessionFactoryScope scope) {
final PostUpdateTestListener listener = new PostUpdateTestListener();
scope.getSessionFactory().getEventEngine().getListenerRegistry().setListeners(
EventType.POST_UPDATE,
listener
);
scope.inTransaction(
session -> {
final IndexedEntity indexedEntity = session.get( IndexedEntity.class, 1L );
assertNotNull( indexedEntity.getElementCollectionArray() );
assertEquals( 0, indexedEntity.getElementCollectionArray().length );
final boolean[] serializableArray = indexedEntity.getSerializableArray();
assertNotNull( serializableArray );
serializableArray[0] = !serializableArray[0];
serializableArray[1] = !serializableArray[1];
}
);
assertTrue( listener.isPostUpdateCalled() );
}
@Test
public void onPostUpdateMethodShouldNotBeCalledTest(SessionFactoryScope scope) {
final PostUpdateTestListener listener = new PostUpdateTestListener();
scope.getSessionFactory().getEventEngine().getListenerRegistry().setListeners(
EventType.POST_UPDATE,
listener
);
scope.inTransaction(
session -> {
final IndexedEntity indexedEntity = session.get( IndexedEntity.class, 1L );
assertNotNull( indexedEntity.getElementCollectionArray() );
assertEquals( 0, indexedEntity.getElementCollectionArray().length );
assertNotNull( indexedEntity.getSerializableArray() );
}
);
assertFalse( listener.isPostUpdateCalled() );
scope.inTransaction(
session -> {
final IndexedEntity indexedEntity = session.get( IndexedEntity.class, 2L );
assertNotNull( indexedEntity.getElementCollectionArray() );
assertNull( indexedEntity.getSerializableArray() );
}
);
assertFalse( listener.isPostUpdateCalled() );
}
@Entity(name = "IndexedEntity")
public static class IndexedEntity {
@Id
public Long id;
public boolean[] serializableArray;
@ElementCollection
@OrderColumn
public boolean[] elementCollectionArray;
@OneToMany(fetch = FetchType.EAGER)
public Set<IndexedEntity> indexedEntities;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public boolean[] getSerializableArray() {
return serializableArray;
}
public void setSerializableArray(boolean[] serializableArray) {
this.serializableArray = serializableArray;
}
public boolean[] getElementCollectionArray() {
return elementCollectionArray;
}
public void setElementCollectionArray(boolean[] elementCollectionArray) {
this.elementCollectionArray = elementCollectionArray;
}
public Set<IndexedEntity> getIndexedEntities() {
return indexedEntities;
}
public void setIndexedEntities(Set<IndexedEntity> indexedEntities) {
this.indexedEntities = indexedEntities;
}
}
}