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:
ballm 2015-03-27 14:50:31 +00:00 committed by Gail Badner
parent 16c0a1f144
commit 91995cf951
4 changed files with 150 additions and 2 deletions

View File

@ -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

View File

@ -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];
}

View File

@ -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();
}
}

View File

@ -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);
}
}
}