diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/AttributeTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/AttributeTypeDescriptor.java index 7210884e87..0209b67110 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/AttributeTypeDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/AttributeTypeDescriptor.java @@ -6,15 +6,17 @@ */ package org.hibernate.bytecode.enhance.internal; +import java.util.Collection; +import java.util.Locale; +import javax.persistence.Id; + import javassist.CtClass; import javassist.CtField; import javassist.NotFoundException; + import org.hibernate.bytecode.enhance.spi.EnhancementContext; import org.hibernate.bytecode.enhance.spi.EnhancerConstants; - -import javax.persistence.Id; -import java.util.Collection; -import java.util.Locale; +import org.hibernate.internal.util.compare.EqualsHelper; /** * utility class to generate interceptor methods @@ -40,7 +42,7 @@ public abstract class AttributeTypeDescriptor { // primitives || enums if ( currentValue.getType().isPrimitive() || currentValue.getType().isEnum() ) { - builder.append( String.format( "if (%s != $1)", currentValue.getName()) ); + builder.append( String.format( "if (%s != $1)", currentValue.getName() ) ); } // simple data types else if ( currentValue.getType().getName().startsWith( "java.lang" ) @@ -62,10 +64,11 @@ public abstract class AttributeTypeDescriptor { } } } - // TODO: for now just call equals, should probably do something else here - builder.append( String.format( "if (%s == null || !%. + */ +package org.hibernate.test.bytecode.enhancement; + +import javassist.CtClass; + +import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext; + +/** + * Enhancement context used in tests + * + * @author Luis Barreiro + */ +public class EnhancerTestContext extends DefaultEnhancementContext { + + @Override + public boolean doFieldAccessEnhancement(CtClass classDescriptor) { + return true; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/EnhancerTestUtils.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/EnhancerTestUtils.java index 9ef4899a79..89e0a63cf8 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/EnhancerTestUtils.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/EnhancerTestUtils.java @@ -9,15 +9,14 @@ package org.hibernate.test.bytecode.enhancement; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.File; +import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Arrays; -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.tools.JavaFileObject; import javassist.ClassPool; import javassist.CtClass; @@ -25,7 +24,6 @@ import javassist.LoaderClassPath; import org.hibernate.HibernateException; import org.hibernate.LockMode; -import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext; import org.hibernate.bytecode.enhance.spi.EnhancementContext; import org.hibernate.bytecode.enhance.spi.Enhancer; import org.hibernate.engine.internal.MutableEntityEntryFactory; @@ -41,6 +39,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; /** * utility class to use in bytecode enhancement tests @@ -49,7 +48,7 @@ import static org.junit.Assert.assertTrue; */ public abstract class EnhancerTestUtils extends BaseUnitTestCase { - private static EnhancementContext enhancementContext = new DefaultEnhancementContext(); + private static EnhancementContext enhancementContext = new EnhancerTestContext(); private static String workingDir = System.getProperty( "java.io.tmpdir" ); @@ -82,13 +81,8 @@ public abstract class EnhancerTestUtils extends BaseUnitTestCase { private static CtClass generateCtClassForAnEntity(Class entityClassToEnhance) throws Exception { ClassPool cp = new ClassPool( false ); ClassLoader cl = EnhancerTestUtils.class.getClassLoader(); - return cp.makeClass( cl.getResourceAsStream( getFilenameForClassName( entityClassToEnhance.getName() ) ) ); + return cp.makeClass( cl.getResourceAsStream( entityClassToEnhance.getName().replace( '.', '/' ) + ".class" ) ); } - - private static String getFilenameForClassName(String className) { - return className.replace( '.', File.separatorChar ) + JavaFileObject.Kind.CLASS.extension; - } - /* --- */ @SuppressWarnings("unchecked") @@ -145,7 +139,7 @@ public abstract class EnhancerTestUtils extends BaseUnitTestCase { return c; } - final InputStream is = this.getResourceAsStream( getFilenameForClassName( name ) ); + final InputStream is = this.getResourceAsStream( name.replace( '.', '/' ) + ".class" ); if ( is == null ) { throw new ClassNotFoundException( name + " not found" ); } @@ -154,28 +148,42 @@ public abstract class EnhancerTestUtils extends BaseUnitTestCase { final byte[] original = new byte[is.available()]; new BufferedInputStream( is ).read( original ); - // Only enhance classes annotated with Entity or Embeddable - final Class p = getParent().loadClass( name ); - if ( p.getAnnotation( Entity.class ) != null || p.getAnnotation( Embeddable.class ) != null ) { - final byte[] enhanced = new Enhancer( enhancementContext ).enhance( name, original ); + final byte[] enhanced = new Enhancer( enhancementContext ).enhance( name, original ); - Path debugOutput = Paths.get( workingDir + File.separator + getFilenameForClassName( name ) ); - Files.createDirectories( debugOutput.getParent() ); - Files.write( debugOutput, enhanced, StandardOpenOption.CREATE ); + Path debugOutput = Paths.get( workingDir + File.separator + name.replace( '.', '/' ) + ".class" ); + Files.createDirectories( debugOutput.getParent() ); + Files.write( debugOutput, enhanced, StandardOpenOption.CREATE ); - return defineClass( name, enhanced, 0, enhanced.length ); - } - else { - return defineClass( name, original, 0, original.length ); - } + return defineClass( name, enhanced, 0, enhanced.length ); } catch (Throwable t) { throw new ClassNotFoundException( name + " not found", t ); + } finally { + try { + is.close(); + } + catch (IOException e) { // ignore + } } } }; } + public static Object getFieldByReflection(Object entity, String fieldName) { + try { + Field field = entity.getClass().getDeclaredField( fieldName ); + field.setAccessible( true ); + return field.get( entity ); + } + catch (NoSuchFieldException e) { + fail( "Fail to get field '" + fieldName + "' in entity " + entity ); + } + catch (IllegalAccessException e) { + fail( "Fail to get field '" + fieldName + "' in entity " + entity ); + } + return null; + } + /** * clears the dirty set for an entity */ diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/basic/BasicEnhancementTestTask.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/basic/BasicEnhancementTestTask.java index 76aa577472..a563acaa76 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/basic/BasicEnhancementTestTask.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/basic/BasicEnhancementTestTask.java @@ -56,13 +56,15 @@ public class BasicEnhancementTestTask extends AbstractEnhancerTestTask { interceptableEntity.$$_hibernate_setInterceptor( new ObjectAttributeMarkerInterceptor() ); assertNotNull( interceptableEntity.$$_hibernate_getInterceptor() ); - assertNull( entity.anUnspecifiedObject ); - entity.setAnObject( new Object() ); - assertSame( entity.anUnspecifiedObject, ObjectAttributeMarkerInterceptor.WRITE_MARKER ); - assertSame( entity.getAnObject(), ObjectAttributeMarkerInterceptor.READ_MARKER ); - entity.setAnObject( null ); - assertSame( entity.anUnspecifiedObject, ObjectAttributeMarkerInterceptor.WRITE_MARKER ); + assertNull( EnhancerTestUtils.getFieldByReflection( entity, "anUnspecifiedObject" ) ); + entity.setAnObject( new Object() ); + + assertSame( EnhancerTestUtils.getFieldByReflection( entity, "anUnspecifiedObject" ), ObjectAttributeMarkerInterceptor.WRITE_MARKER ); + assertSame( entity.getAnObject(), ObjectAttributeMarkerInterceptor.READ_MARKER ); + + entity.setAnObject( null ); + assertSame( EnhancerTestUtils.getFieldByReflection( entity, "anUnspecifiedObject" ), ObjectAttributeMarkerInterceptor.WRITE_MARKER ); } protected void cleanup() { diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/field/FieldAccessBidirectionalTestTasK.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/field/FieldAccessBidirectionalTestTasK.java new file mode 100644 index 0000000000..3cc500331b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/field/FieldAccessBidirectionalTestTasK.java @@ -0,0 +1,82 @@ +/* + * 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 . + */ +package org.hibernate.test.bytecode.enhancement.field; + +import java.util.UUID; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.OneToOne; + +import org.hibernate.test.bytecode.enhancement.AbstractEnhancerTestTask; +import org.hibernate.test.bytecode.enhancement.EnhancerTestUtils; +import org.junit.Assert; + +/** + * @author Luis Barreiro + */ +public class FieldAccessBidirectionalTestTasK extends AbstractEnhancerTestTask { + + public Class[] getAnnotatedClasses() { + return new Class[] {Customer.class, User.class}; + } + + public void prepare() { + } + + public void execute() { + User user = new User(); + user.login = UUID.randomUUID().toString(); + + Customer customer = new Customer(); + customer.user = user; + + Assert.assertEquals( customer, EnhancerTestUtils.getFieldByReflection( user, "customer" ) ); + + // check dirty tracking is set automatically with bi-directional association management + EnhancerTestUtils.checkDirtyTracking( user, "login", "customer" ); + + User anotherUser = new User(); + anotherUser.login = UUID.randomUUID().toString(); + + customer.user = anotherUser; + + Assert.assertNull( user.customer ); + Assert.assertEquals( customer, EnhancerTestUtils.getFieldByReflection( anotherUser, "customer" ) ); + + user.customer = new Customer(); + Assert.assertEquals( user, user.customer.user ); + } + + protected void cleanup() { + } + + @Entity public class Customer { + + @Id public int id; + + @OneToOne(fetch = FetchType.LAZY) public User user; + + public String firstName; + + public String lastName; + + public int version; + } + + @Entity public class User { + + @Id public int id; + + public String login; + + public String password; + + @OneToOne(mappedBy = "user", fetch = FetchType.LAZY) public Customer customer; + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/field/FieldAccessEnhancementTestTask.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/field/FieldAccessEnhancementTestTask.java new file mode 100644 index 0000000000..ae021074e4 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/field/FieldAccessEnhancementTestTask.java @@ -0,0 +1,90 @@ +/* + * 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 . + */ +package org.hibernate.test.bytecode.enhancement.field; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.OneToMany; + +import org.hibernate.engine.spi.PersistentAttributeInterceptable; + +import org.hibernate.test.bytecode.enhancement.AbstractEnhancerTestTask; +import org.hibernate.test.bytecode.enhancement.EnhancerTestUtils; +import org.hibernate.test.bytecode.enhancement.basic.ObjectAttributeMarkerInterceptor; +import org.junit.Assert; + +/** + * @author Luis Barreiro + */ +public class FieldAccessEnhancementTestTask extends AbstractEnhancerTestTask { + + public Class[] getAnnotatedClasses() { + return new Class[] {SimpleEntity.class}; + } + + public void prepare() { + } + + public void execute() { + // test uses ObjectAttributeMarkerInterceptor to ensure that field access is routed through enhanced methods + + SimpleEntity entity = new SimpleEntity(); + ( (PersistentAttributeInterceptable) entity ).$$_hibernate_setInterceptor( new ObjectAttributeMarkerInterceptor() ); + + Object decoy = new Object(); + entity.anUnspecifiedObject = decoy; + + Object gotByReflection = EnhancerTestUtils.getFieldByReflection( entity, "anUnspecifiedObject" ); + Assert.assertNotSame( gotByReflection, decoy ); + Assert.assertSame( gotByReflection, ObjectAttributeMarkerInterceptor.WRITE_MARKER ); + + Object entityObject = entity.anUnspecifiedObject; + + Assert.assertNotSame( entityObject, decoy ); + Assert.assertSame( entityObject, ObjectAttributeMarkerInterceptor.READ_MARKER ); + + // do some more calls on the various types, without the interceptor + ( (PersistentAttributeInterceptable) entity ).$$_hibernate_setInterceptor( null ); + + entity.id = 1234567890l; + Assert.assertEquals( entity.id, 1234567890l ); + + entity.name = "Entity Name"; + Assert.assertSame( entity.name, "Entity Name" ); + + entity.active = true; + Assert.assertTrue( entity.active ); + + entity.someStrings = Arrays.asList( "A", "B", "C", "D" ); + Assert.assertArrayEquals( new String[] { "A", "B", "C", "D" }, entity.someStrings.toArray() ); + } + + protected void cleanup() { + } + + @Entity public class SimpleEntity { + + @Id public long id; + + public String name; + + public boolean active; + + public long someNumber; + + public int anInt; + + public Object anUnspecifiedObject; + + public List someStrings; + + @OneToMany public Set someInts; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/LazyLoadingTestTask.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/LazyLoadingTestTask.java index 5f41687b30..dfd456f96e 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/LazyLoadingTestTask.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/LazyLoadingTestTask.java @@ -62,15 +62,18 @@ public class LazyLoadingTestTask extends AbstractEnhancerTestTask { Child loadedChild = s.load( Child.class, lastChildID ); - Assert.assertNull( "Lazy field 'parent' is initialized", loadedChild.parent ); + Object parentByReflection = EnhancerTestUtils.getFieldByReflection( loadedChild, "parent" ); + Assert.assertNull( "Lazy field 'parent' is initialized", parentByReflection ); Assert.assertFalse( loadedChild instanceof HibernateProxy ); Parent loadedParent = loadedChild.getParent(); EnhancerTestUtils.checkDirtyTracking( loadedChild ); - Assert.assertNotNull( "Lazy field 'parent' is not loaded", loadedChild.parent ); - Assert.assertNull( "Lazy field 'children' is initialized", loadedParent.children ); + parentByReflection = EnhancerTestUtils.getFieldByReflection( loadedChild, "parent" ); + Object childrenByReflection = EnhancerTestUtils.getFieldByReflection( loadedParent, "children" ); + Assert.assertNotNull( "Lazy field 'parent' is not loaded", parentByReflection ); + Assert.assertNull( "Lazy field 'children' is initialized", childrenByReflection ); Assert.assertFalse( loadedParent instanceof HibernateProxy ); Assert.assertTrue( parentID.equals( loadedParent.id ) ); @@ -79,7 +82,8 @@ public class LazyLoadingTestTask extends AbstractEnhancerTestTask { EnhancerTestUtils.checkDirtyTracking( loadedChild ); EnhancerTestUtils.checkDirtyTracking( loadedParent ); - Assert.assertNotNull( "Lazy field 'children' is not loaded", loadedParent.children ); + childrenByReflection = EnhancerTestUtils.getFieldByReflection( loadedParent, "children" ); + Assert.assertNotNull( "Lazy field 'children' is not loaded", childrenByReflection ); Assert.assertFalse( loadedChildren instanceof HibernateProxy ); Assert.assertEquals( CHILDREN_SIZE, loadedChildren.size() ); Assert.assertTrue( loadedChildren.contains( loadedChild ) );