HHH-9938 - bytecode enhancer - field access enhancement feature
This commit is contained in:
parent
6d77ac39c5
commit
417baffa13
|
@ -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
|
||||
|
@ -62,8 +64,9 @@ public abstract class AttributeTypeDescriptor {
|
|||
}
|
||||
}
|
||||
}
|
||||
// TODO: for now just call equals, should probably do something else here
|
||||
builder.append( String.format( "if (%s == null || !%<s.equals($1))", currentValue.getName() ) );
|
||||
builder.append( String.format( "if (%1$s == null || !%2$s.equals(%1$s, $1))",
|
||||
currentValue.getName(),
|
||||
EqualsHelper.class.getName() ) );
|
||||
}
|
||||
builder.append( String.format( " { %s(\"%s\"); }", EnhancerConstants.TRACKER_CHANGER_NAME, currentValue.getName() ) );
|
||||
}
|
||||
|
|
|
@ -63,8 +63,13 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
|||
);
|
||||
}
|
||||
|
||||
// lastly, find all references to the transformed fields and replace with calls to the added reader/writer methods
|
||||
// find all references to the transformed fields and replace with calls to the added reader/writer methods
|
||||
enhanceAttributesAccess( managedCtClass, attrDescriptorMap );
|
||||
|
||||
// same thing for direct access to fields of other entities
|
||||
if ( this.enhancementContext.doFieldAccessEnhancement( managedCtClass ) ) {
|
||||
enhanceFieldAccess( managedCtClass );
|
||||
}
|
||||
}
|
||||
|
||||
private CtField[] collectPersistentFields(CtClass managedCtClass) {
|
||||
|
@ -74,6 +79,10 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
|||
if ( Modifier.isStatic( ctField.getModifiers() ) || ctField.getName().startsWith( "$$_hibernate_" ) ) {
|
||||
continue;
|
||||
}
|
||||
// skip outer reference in inner classes
|
||||
if ( "this$0".equals( ctField.getName() ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( enhancementContext.isPersistentField( ctField ) ) {
|
||||
persistentFieldList.add( ctField );
|
||||
}
|
||||
|
@ -114,7 +123,6 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
|||
return MethodWriter.addGetter( managedCtClass, fieldName, readerName );
|
||||
}
|
||||
|
||||
// TODO: temporary solution...
|
||||
try {
|
||||
return MethodWriter.write(
|
||||
managedCtClass, "public %s %s() {%n %s%n return this.%s;%n}",
|
||||
|
@ -520,4 +528,84 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace access to fields of entities (for example, entity.field) with a call to the enhanced getter / setter
|
||||
* (in this example, entity.$$_hibernate_read_field()). It's assumed that the target entity is enhanced as well.
|
||||
*
|
||||
* @param managedCtClass Class to enhance
|
||||
*/
|
||||
public void enhanceFieldAccess(CtClass managedCtClass) {
|
||||
final ConstPool constPool = managedCtClass.getClassFile().getConstPool();
|
||||
|
||||
for ( Object oMethod : managedCtClass.getClassFile().getMethods() ) {
|
||||
final MethodInfo methodInfo = (MethodInfo) oMethod;
|
||||
final String methodName = methodInfo.getName();
|
||||
|
||||
// skip methods added by enhancement and abstract methods (methods without any code)
|
||||
if ( methodName.startsWith( "$$_hibernate_" ) || methodInfo.getCodeAttribute() == null ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
final CodeIterator itr = methodInfo.getCodeAttribute().iterator();
|
||||
while ( itr.hasNext() ) {
|
||||
int index = itr.next();
|
||||
int op = itr.byteAt( index );
|
||||
if ( op != Opcode.PUTFIELD && op != Opcode.GETFIELD ) {
|
||||
continue;
|
||||
}
|
||||
String fieldName = constPool.getFieldrefName( itr.u16bitAt( index + 1 ) );
|
||||
String fieldClassName = constPool.getClassInfo( constPool.getFieldrefClass( itr.u16bitAt( index + 1 ) ) );
|
||||
CtClass targetCtClass = this.classPool.getCtClass( fieldClassName );
|
||||
|
||||
if ( !enhancementContext.isEntityClass( targetCtClass ) && !enhancementContext.isCompositeClass( targetCtClass ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( targetCtClass == managedCtClass
|
||||
|| !enhancementContext.isPersistentField( targetCtClass.getField( fieldName ) )
|
||||
|| "this$0".equals( fieldName ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
log.debugf( "Transforming access to field [%s] from method [%s]", fieldName, methodName );
|
||||
|
||||
if ( op == Opcode.GETFIELD ) {
|
||||
int fieldReaderMethodIndex = constPool.addMethodrefInfo(
|
||||
constPool.addClassInfo( fieldClassName ),
|
||||
EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + fieldName,
|
||||
"()" + constPool.getFieldrefType( itr.u16bitAt( index + 1 ) )
|
||||
);
|
||||
itr.writeByte( Opcode.INVOKEVIRTUAL, index );
|
||||
itr.write16bit( fieldReaderMethodIndex, index + 1 );
|
||||
}
|
||||
else {
|
||||
int fieldWriterMethodIndex = constPool.addMethodrefInfo(
|
||||
constPool.addClassInfo( fieldClassName ),
|
||||
EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + fieldName,
|
||||
"(" + constPool.getFieldrefType( itr.u16bitAt( index + 1 ) ) + ")V"
|
||||
);
|
||||
itr.writeByte( Opcode.INVOKEVIRTUAL, index );
|
||||
itr.write16bit( fieldWriterMethodIndex, index + 1 );
|
||||
}
|
||||
|
||||
}
|
||||
methodInfo.getCodeAttribute().setAttribute( MapMaker.make( classPool, methodInfo ) );
|
||||
}
|
||||
catch (BadBytecode bb) {
|
||||
final String msg = String.format(
|
||||
"Unable to perform field access transformation in method [%s]",
|
||||
methodName
|
||||
);
|
||||
throw new EnhancementException( msg, bb );
|
||||
}
|
||||
catch (NotFoundException nfe) {
|
||||
final String msg = String.format(
|
||||
"Unable to perform field access transformation in method [%s]",
|
||||
methodName
|
||||
);
|
||||
throw new EnhancementException( msg, nfe );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,9 +6,6 @@
|
|||
*/
|
||||
package org.hibernate.bytecode.enhance.spi;
|
||||
|
||||
import javassist.CtClass;
|
||||
import javassist.CtField;
|
||||
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
|
@ -16,6 +13,9 @@ import javax.persistence.ManyToMany;
|
|||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
import javassist.CtClass;
|
||||
import javassist.CtField;
|
||||
|
||||
/**
|
||||
* default implementation of EnhancementContext. May be sub-classed as needed.
|
||||
*
|
||||
|
@ -58,6 +58,13 @@ public class DefaultEnhancementContext implements EnhancementContext {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return false
|
||||
*/
|
||||
public boolean doFieldAccessEnhancement(CtClass classDescriptor) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true
|
||||
*/
|
||||
|
|
|
@ -73,6 +73,16 @@ public interface EnhancementContext {
|
|||
*/
|
||||
public boolean doDirtyCheckingInline(CtClass classDescriptor);
|
||||
|
||||
/**
|
||||
* Should we enhance field access to entities from this class?
|
||||
*
|
||||
* @param classDescriptor The descriptor of the class to check.
|
||||
*
|
||||
* @return {@code true} indicates that any direct access to fields of entities should be routed to the enhanced
|
||||
* getter / setter method.
|
||||
*/
|
||||
public boolean doFieldAccessEnhancement(CtClass classDescriptor);
|
||||
|
||||
/**
|
||||
* Does the given class define any lazy loadable attributes?
|
||||
*
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.hibernate.HibernateException;
|
|||
import org.hibernate.bytecode.enhance.internal.CompositeEnhancer;
|
||||
import org.hibernate.bytecode.enhance.internal.EntityEnhancer;
|
||||
import org.hibernate.bytecode.enhance.internal.FieldWriter;
|
||||
import org.hibernate.bytecode.enhance.internal.PersistentAttributesEnhancer;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.ManagedComposite;
|
||||
import org.hibernate.engine.spi.ManagedEntity;
|
||||
|
@ -143,6 +144,10 @@ public class Enhancer {
|
|||
log.debugf( "Enhancing [%s] as Composite", managedCtClass.getName() );
|
||||
new CompositeEnhancer( enhancementContext ).enhance( managedCtClass );
|
||||
}
|
||||
else if ( enhancementContext.doFieldAccessEnhancement( managedCtClass ) ) {
|
||||
log.debugf( "Enhancing field access in [%s]", managedCtClass.getName() );
|
||||
new PersistentAttributesEnhancer( enhancementContext ).enhanceFieldAccess( managedCtClass );
|
||||
}
|
||||
else {
|
||||
log.debug( "Skipping enhancement: not entity or composite" );
|
||||
}
|
||||
|
|
|
@ -6,25 +6,6 @@
|
|||
*/
|
||||
package org.hibernate.tool.enhance;
|
||||
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtField;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.DirectoryScanner;
|
||||
import org.apache.tools.ant.Project;
|
||||
import org.apache.tools.ant.Task;
|
||||
import org.apache.tools.ant.types.FileSet;
|
||||
|
||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
||||
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Transient;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
|
@ -32,6 +13,25 @@ import java.io.FileOutputStream;
|
|||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtField;
|
||||
|
||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.DirectoryScanner;
|
||||
import org.apache.tools.ant.Project;
|
||||
import org.apache.tools.ant.Task;
|
||||
import org.apache.tools.ant.types.FileSet;
|
||||
|
||||
/**
|
||||
* Ant task for performing build-time enhancement of entities and component/embeddable classes.
|
||||
|
@ -184,6 +184,11 @@ public class EnhancementTask extends Task implements EnhancementContext {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doFieldAccessEnhancement(CtClass classDescriptor) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
|
||||
return true;
|
||||
|
|
|
@ -14,6 +14,8 @@ import org.hibernate.test.bytecode.enhancement.association.OneToManyAssociationT
|
|||
import org.hibernate.test.bytecode.enhancement.association.OneToOneAssociationTestTask;
|
||||
import org.hibernate.test.bytecode.enhancement.basic.BasicEnhancementTestTask;
|
||||
import org.hibernate.test.bytecode.enhancement.dirty.DirtyTrackingTestTask;
|
||||
import org.hibernate.test.bytecode.enhancement.field.FieldAccessBidirectionalTestTasK;
|
||||
import org.hibernate.test.bytecode.enhancement.field.FieldAccessEnhancementTestTask;
|
||||
import org.hibernate.test.bytecode.enhancement.join.HHH3949TestTask1;
|
||||
import org.hibernate.test.bytecode.enhancement.join.HHH3949TestTask2;
|
||||
import org.hibernate.test.bytecode.enhancement.join.HHH3949TestTask3;
|
||||
|
@ -51,6 +53,12 @@ public class EnhancerTest extends BaseUnitTestCase {
|
|||
EnhancerTestUtils.runEnhancerTestTask( LazyLoadingIntegrationTestTask.class );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFieldAccess() {
|
||||
EnhancerTestUtils.runEnhancerTestTask( FieldAccessEnhancementTestTask.class );
|
||||
EnhancerTestUtils.runEnhancerTestTask( FieldAccessBidirectionalTestTasK.class );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-3949" )
|
||||
@FailureExpected( jiraKey = "HHH-3949" )
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
|
||||
Path debugOutput = Paths.get( workingDir + File.separator + getFilenameForClassName( name ) );
|
||||
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 );
|
||||
}
|
||||
}
|
||||
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
|
||||
*/
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
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<String> someStrings;
|
||||
|
||||
@OneToMany public Set<Integer> someInts;
|
||||
}
|
||||
}
|
|
@ -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 ) );
|
||||
|
|
Loading…
Reference in New Issue