HHH-14619 Test and fix ClassCastException because collection of uninitialized proxy is dirty checked
This commit is contained in:
parent
6dc3b4a726
commit
bf19f98c2d
|
@ -219,7 +219,6 @@ public abstract class AbstractFlushingEventListener implements JpaBootstrapSensi
|
|||
final int count = entityEntries.length;
|
||||
|
||||
for ( Map.Entry<Object,EntityEntry> me : entityEntries ) {
|
||||
|
||||
// Update the status of the object and if necessary, schedule an update
|
||||
|
||||
EntityEntry entry = me.getValue();
|
||||
|
|
|
@ -485,6 +485,7 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
|
|||
private boolean hasDirtyCollections(FlushEntityEvent event, EntityPersister persister, Status status) {
|
||||
if ( isCollectionDirtyCheckNecessary( persister, status ) ) {
|
||||
DirtyCollectionSearchVisitor visitor = new DirtyCollectionSearchVisitor(
|
||||
event.getEntity(),
|
||||
event.getSession(),
|
||||
persister.getPropertyVersionability()
|
||||
);
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
package org.hibernate.event.internal;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
|
||||
import org.hibernate.collection.spi.PersistentCollection;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.type.CollectionType;
|
||||
|
@ -22,11 +24,19 @@ import org.hibernate.type.CollectionType;
|
|||
*/
|
||||
public class DirtyCollectionSearchVisitor extends AbstractVisitor {
|
||||
|
||||
private final EnhancementAsProxyLazinessInterceptor interceptor;
|
||||
private final boolean[] propertyVersionability;
|
||||
private boolean dirty;
|
||||
private boolean[] propertyVersionability;
|
||||
|
||||
public DirtyCollectionSearchVisitor(EventSource session, boolean[] propertyVersionability) {
|
||||
public DirtyCollectionSearchVisitor(Object entity, EventSource session, boolean[] propertyVersionability) {
|
||||
super( session );
|
||||
EnhancementAsProxyLazinessInterceptor interceptor = null;
|
||||
if ( entity instanceof PersistentAttributeInterceptable ) {
|
||||
if ( ( (PersistentAttributeInterceptable) entity ).$$_hibernate_getInterceptor() instanceof EnhancementAsProxyLazinessInterceptor ) {
|
||||
interceptor = (EnhancementAsProxyLazinessInterceptor) ( (PersistentAttributeInterceptable) entity ).$$_hibernate_getInterceptor();
|
||||
}
|
||||
}
|
||||
this.interceptor = interceptor;
|
||||
this.propertyVersionability = propertyVersionability;
|
||||
}
|
||||
|
||||
|
@ -45,6 +55,9 @@ public class DirtyCollectionSearchVisitor extends AbstractVisitor {
|
|||
// return (ah==null) ? true : searchForDirtyCollections(ah, type);
|
||||
}
|
||||
else {
|
||||
if ( interceptor != null && !interceptor.isAttributeLoaded( type.getName() ) ) {
|
||||
return null;
|
||||
}
|
||||
// if not wrapped yet, its dirty (this can't occur, because
|
||||
// we now always call wrap() before getting to here)
|
||||
// return ( ! (obj instanceof PersistentCollection) ) ?
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* 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.lazy;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.bytecode.enhance.spi.UnloadedClass;
|
||||
import org.hibernate.event.service.spi.EventListenerRegistry;
|
||||
import org.hibernate.event.spi.EventType;
|
||||
import org.hibernate.event.spi.LoadEvent;
|
||||
import org.hibernate.event.spi.LoadEventListener;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext;
|
||||
import org.hibernate.testing.bytecode.enhancement.EnhancerTestContext;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Version;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
|
||||
/**
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
@TestForIssue( jiraKey = "HHH-14619" )
|
||||
@RunWith( BytecodeEnhancerRunner.class )
|
||||
public class LazyProxyWithCollectionTest extends BaseCoreFunctionalTestCase {
|
||||
|
||||
private Long childId;
|
||||
|
||||
@Override
|
||||
public Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[]{Parent.class, Child.class};
|
||||
}
|
||||
|
||||
@Before
|
||||
public void prepare() {
|
||||
doInJPA( this::sessionFactory, em -> {
|
||||
Child c = new Child();
|
||||
em.persist( c );
|
||||
childId = c.getId();
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReference() {
|
||||
doInJPA( this::sessionFactory, em -> {
|
||||
Child child = em.getReference( Child.class, childId );
|
||||
Parent parent = new Parent();
|
||||
parent.child = child;
|
||||
em.persist( parent );
|
||||
// Class cast exception occurs during auto-flush
|
||||
em.find( Parent.class, parent.getId() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLazyCollection() {
|
||||
doInJPA( this::sessionFactory, em -> {
|
||||
Child child = em.find( Child.class, childId );
|
||||
Parent parent = new Parent();
|
||||
parent.child = child;
|
||||
em.persist( parent );
|
||||
child.children = new HashSet<>();
|
||||
// Class cast exception occurs during auto-flush
|
||||
em.find( Parent.class, parent.getId() );
|
||||
} );
|
||||
}
|
||||
|
||||
// --- //
|
||||
|
||||
@Entity
|
||||
@Table( name = "PARENT" )
|
||||
private static class Parent {
|
||||
|
||||
@Id
|
||||
@GeneratedValue( strategy = GenerationType.AUTO )
|
||||
Long id;
|
||||
|
||||
@OneToOne( fetch = FetchType.LAZY )
|
||||
Child child;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Child getChild() {
|
||||
return child;
|
||||
}
|
||||
|
||||
public void setChild(Child child) {
|
||||
this.child = child;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity
|
||||
@Table( name = "CHILD" )
|
||||
private static class Child {
|
||||
|
||||
@Id
|
||||
@GeneratedValue( strategy = GenerationType.AUTO )
|
||||
Long id;
|
||||
@Version
|
||||
Long version;
|
||||
|
||||
String name;
|
||||
|
||||
@OneToMany
|
||||
Set<Child> children = new HashSet<>();
|
||||
|
||||
Child() {
|
||||
// No-arg constructor necessary for proxy factory
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Long version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Set<Child> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(Set<Child> children) {
|
||||
this.children = children;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue