HHH-7573 Added check for handle LazyPropertyInitializer.UNFETCHED_PROPERTY
Added test to check that the change handles the various possible scenarios.
Test requires build time instrumentation.
(cherry picked from commit e7475f12b0
)
This commit is contained in:
parent
16c0a1f144
commit
91995cf951
|
@ -112,3 +112,17 @@ task testJar(type: Jar, dependsOn: testClasses) {
|
|||
artifacts {
|
||||
tests testJar
|
||||
}
|
||||
|
||||
task instrument(dependsOn: compileTestJava) << {
|
||||
ant.taskdef(name: 'hibInstrument',
|
||||
classname: 'org.hibernate.tool.instrument.javassist.InstrumentTask',
|
||||
classpath: configurations.testCompile.asPath)
|
||||
ant.hibInstrument(verbose: 'true'){
|
||||
fileset(dir:"$buildDir/classes/test/org/hibernate/jpa/test/callbacks/"){
|
||||
include(name: "EntityWithLazyProperty.class")
|
||||
}
|
||||
}
|
||||
println("hibernate instrumentation")
|
||||
}
|
||||
|
||||
test.dependsOn instrument
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.jpa.event.internal.core;
|
||||
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.bytecode.instrumentation.spi.LazyPropertyInitializer;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.engine.spi.Status;
|
||||
|
@ -61,7 +62,8 @@ public class JpaFlushEntityEventListener extends DefaultFlushEntityEventListener
|
|||
int size = newState.length;
|
||||
boolean isDirty = false;
|
||||
for ( int index = 0; index < size ; index++ ) {
|
||||
if ( !types[index].isEqual( state[index], newState[index] ) ) {
|
||||
if (state[index] == LazyPropertyInitializer.UNFETCHED_PROPERTY && newState[index] != LazyPropertyInitializer.UNFETCHED_PROPERTY
|
||||
|| state[index] != newState[index] && !types[index].isEqual(state[index], newState[index])) {
|
||||
isDirty = true;
|
||||
state[index] = newState[index];
|
||||
}
|
||||
|
|
|
@ -251,7 +251,76 @@ public class CallbacksTest extends BaseEntityManagerFunctionalTestCase {
|
|||
RemoteControl.class,
|
||||
Rythm.class,
|
||||
Plant.class,
|
||||
Kitten.class
|
||||
Kitten.class,
|
||||
EntityWithLazyProperty.class
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for HHH-7573.
|
||||
* Load some test data into an entity which has a lazy property and a @PreUpdate callback, then reload and update a
|
||||
* non lazy field which will trigger the PreUpdate lifecycle callback.
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testJpaFlushEntityEventListener() throws Exception {
|
||||
EntityWithLazyProperty entity;
|
||||
EntityManager em = getOrCreateEntityManager();
|
||||
|
||||
byte[] testArray = new byte[]{0x2A};
|
||||
|
||||
//persist the test entity.
|
||||
em.getTransaction().begin();
|
||||
entity = new EntityWithLazyProperty();
|
||||
entity.setSomeField("TEST");
|
||||
entity.setLazyData(testArray);
|
||||
em.persist(entity);
|
||||
em.getTransaction().commit();
|
||||
checkLazyField(entity, em, testArray);
|
||||
|
||||
/**
|
||||
* Set a non lazy field, therefore the lazyData field will be LazyPropertyInitializer.UNFETCHED_PROPERTY
|
||||
* for both state and newState so the field should not change. This should no longer cause a ClassCastException.
|
||||
*/
|
||||
em.getTransaction().begin();
|
||||
entity = em.find(EntityWithLazyProperty.class, entity.getId());
|
||||
entity.setSomeField("TEST1");
|
||||
em.getTransaction().commit();
|
||||
checkLazyField(entity, em, testArray);
|
||||
|
||||
/**
|
||||
* Set the updateLazyFieldInPreUpdate flag so that the lazy field is updated from within the
|
||||
* PreUpdate annotated callback method. So state == LazyPropertyInitializer.UNFETCHED_PROPERTY and
|
||||
* newState == EntityWithLazyProperty.PRE_UPDATE_VALUE. This should no longer cause a ClassCastException.
|
||||
*/
|
||||
em.getTransaction().begin();
|
||||
entity = em.find(EntityWithLazyProperty.class, entity.getId());
|
||||
entity.setUpdateLazyFieldInPreUpdate(true);
|
||||
entity.setSomeField("TEST2");
|
||||
em.getTransaction().commit();
|
||||
checkLazyField(entity, em, EntityWithLazyProperty.PRE_UPDATE_VALUE);
|
||||
|
||||
/**
|
||||
* Set the updateLazyFieldInPreUpdate flag so that the lazy field is updated from within the
|
||||
* PreUpdate annotated callback method and also set the lazyData field directly to testArray1. When we reload we
|
||||
* should get EntityWithLazyProperty.PRE_UPDATE_VALUE.
|
||||
*/
|
||||
em.getTransaction().begin();
|
||||
entity = em.find(EntityWithLazyProperty.class, entity.getId());
|
||||
entity.setUpdateLazyFieldInPreUpdate(true);
|
||||
entity.setLazyData(testArray);
|
||||
entity.setSomeField("TEST3");
|
||||
em.getTransaction().commit();
|
||||
checkLazyField(entity, em, EntityWithLazyProperty.PRE_UPDATE_VALUE);
|
||||
|
||||
em.close();
|
||||
}
|
||||
|
||||
private void checkLazyField(EntityWithLazyProperty entity, EntityManager em, byte[] expected) {
|
||||
// reload the entity and check the lazy value matches what we expect.
|
||||
em.getTransaction().begin();
|
||||
entity = em.find(EntityWithLazyProperty.class, entity.getId());
|
||||
assertEquals(expected, entity.getLazyData());
|
||||
em.getTransaction().commit();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
package org.hibernate.jpa.test.callbacks;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
/**
|
||||
* Test entity with a lazy property which requires build time instrumentation.
|
||||
*/
|
||||
@Entity
|
||||
public class EntityWithLazyProperty {
|
||||
|
||||
public static final byte[] PRE_UPDATE_VALUE = new byte[]{0x2A, 0x2A, 0x2A, 0x2A};
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
private byte[] lazyData;
|
||||
|
||||
private String someField;
|
||||
|
||||
private boolean updateLazyFieldInPreUpdate;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public byte[] getLazyData() {
|
||||
return lazyData;
|
||||
}
|
||||
|
||||
public void setLazyData(final byte[] lazyData) {
|
||||
this.lazyData = lazyData;
|
||||
}
|
||||
|
||||
public String getSomeField() {
|
||||
return someField;
|
||||
}
|
||||
|
||||
public void setSomeField(String someField) {
|
||||
this.someField = someField;
|
||||
}
|
||||
|
||||
public boolean isUpdateLazyFieldInPreUpdate() {
|
||||
return updateLazyFieldInPreUpdate;
|
||||
}
|
||||
|
||||
public void setUpdateLazyFieldInPreUpdate(boolean updateLazyFieldInPreUpdate) {
|
||||
this.updateLazyFieldInPreUpdate = updateLazyFieldInPreUpdate;
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
public void onPreUpdate() {
|
||||
//Allow the update of the lazy field from within the pre update to check that this does not break things.
|
||||
if(isUpdateLazyFieldInPreUpdate()) {
|
||||
this.setLazyData(PRE_UPDATE_VALUE);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue