HHH-10801 - Bytecode enhancement of @MappedSuperclass

This commit is contained in:
barreiro 2016-06-03 17:02:07 +01:00 committed by Andrea Boriero
parent f0dfc1269b
commit cdc69a475b
13 changed files with 148 additions and 140 deletions

View File

@ -10,7 +10,6 @@ import javassist.CannotCompileException;
import javassist.CtClass;
import org.hibernate.bytecode.enhance.internal.tracker.CompositeOwnerTracker;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.bytecode.enhance.spi.Enhancer;
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
import org.hibernate.engine.spi.CompositeOwner;
import org.hibernate.engine.spi.CompositeTracker;
@ -21,7 +20,7 @@ import org.hibernate.engine.spi.ManagedComposite;
*
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
*/
public class CompositeEnhancer extends Enhancer {
public class CompositeEnhancer extends PersistentAttributesEnhancer {
public CompositeEnhancer(EnhancementContext context) {
super( context );
@ -37,7 +36,7 @@ public class CompositeEnhancer extends Enhancer {
addInLineDirtyHandling( managedCtClass );
}
new PersistentAttributesEnhancer( enhancementContext ).enhance( managedCtClass );
super.enhance( managedCtClass );
}
/* --- */

View File

@ -12,13 +12,10 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.persistence.MappedSuperclass;
import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.CtField;
import javassist.Modifier;
import javassist.NotFoundException;
import org.hibernate.bytecode.enhance.internal.tracker.DirtyTracker;
import org.hibernate.bytecode.enhance.internal.tracker.SimpleCollectionTracker;
@ -26,7 +23,6 @@ import org.hibernate.bytecode.enhance.internal.tracker.SimpleFieldTracker;
import org.hibernate.bytecode.enhance.spi.CollectionTracker;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.bytecode.enhance.spi.EnhancementException;
import org.hibernate.bytecode.enhance.spi.Enhancer;
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
import org.hibernate.engine.spi.EntityEntry;
@ -39,7 +35,7 @@ import org.hibernate.engine.spi.SelfDirtinessTracker;
*
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
*/
public class EntityEnhancer extends Enhancer {
public class EntityEnhancer extends PersistentAttributesEnhancer {
public EntityEnhancer(EnhancementContext context) {
super( context );
@ -63,7 +59,7 @@ public class EntityEnhancer extends Enhancer {
addInLineDirtyHandling( managedCtClass );
}
new PersistentAttributesEnhancer( enhancementContext ).enhance( managedCtClass );
super.enhance( managedCtClass );
}
private void addEntityInstanceHandling(CtClass managedCtClass) {
@ -227,7 +223,7 @@ public class EntityEnhancer extends Enhancer {
// HHH-10646 Add fields inherited from @MappedSuperclass
for ( CtField ctField : managedCtClass.getDeclaredFields() ) {
if ( !ctField.getDeclaringClass().hasAnnotation( MappedSuperclass.class ) || Modifier.isStatic( ctField.getModifiers() ) ) {
if ( !enhancementContext.isMappedSuperclassClass( ctField.getDeclaringClass() ) || Modifier.isStatic( ctField.getModifiers() ) ) {
continue;
}
if ( enhancementContext.isPersistentField( ctField ) ) {

View File

@ -0,0 +1,60 @@
/*
* 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.bytecode.enhance.internal;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
import org.hibernate.engine.spi.ManagedMappedSuperclass;
/**
* enhancer for mapped superclass
*
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
*/
public class MappedSuperclassEnhancer extends PersistentAttributesEnhancer {
public MappedSuperclassEnhancer(EnhancementContext context) {
super( context );
}
public void enhance(CtClass managedCtClass) {
// Add the Managed interface
managedCtClass.addInterface( loadCtClassFromClass( ManagedMappedSuperclass.class ) );
super.enhance( managedCtClass );
}
// Generate 'template' methods for each attribute. This will be overriden by the actual entities
@Override
protected CtMethod generateFieldReader(
CtClass managedCtClass,
CtField persistentField,
AttributeTypeDescriptor typeDescriptor) {
String fieldName = persistentField.getName();
String readerName = EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + fieldName;
return MethodWriter.addGetter( managedCtClass, fieldName, readerName );
}
@Override
protected CtMethod generateFieldWriter(
CtClass managedCtClass,
CtField persistentField,
AttributeTypeDescriptor typeDescriptor) {
String fieldName = persistentField.getName();
String writerName = EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + fieldName;
return MethodWriter.addSetter( managedCtClass, fieldName, writerName );
}
}

View File

@ -14,7 +14,6 @@ import javax.persistence.Embedded;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.MappedSuperclass;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
@ -94,7 +93,11 @@ public class PersistentAttributesEnhancer extends Enhancer {
// HHH-10646 Add fields inherited from @MappedSuperclass
// CtClass.getFields() does not return private fields, while CtClass.getDeclaredFields() does not return inherit
for ( CtField ctField : managedCtClass.getFields() ) {
if ( !ctField.getDeclaringClass().hasAnnotation( MappedSuperclass.class ) || Modifier.isStatic( ctField.getModifiers() ) ) {
if ( ctField.getDeclaringClass().equals( managedCtClass ) ) {
// Already processed above
continue;
}
if ( !enhancementContext.isMappedSuperclassClass( ctField.getDeclaringClass() ) || Modifier.isStatic( ctField.getModifiers() ) ) {
continue;
}
if ( enhancementContext.isPersistentField( ctField ) ) {
@ -124,7 +127,7 @@ public class PersistentAttributesEnhancer extends Enhancer {
}
}
private CtMethod generateFieldReader(
protected CtMethod generateFieldReader(
CtClass managedCtClass,
CtField persistentField,
AttributeTypeDescriptor typeDescriptor) {
@ -165,7 +168,7 @@ public class PersistentAttributesEnhancer extends Enhancer {
}
}
private CtMethod generateFieldWriter(
protected CtMethod generateFieldWriter(
CtClass managedCtClass,
CtField persistentField,
AttributeTypeDescriptor typeDescriptor) {

View File

@ -10,6 +10,7 @@ import javax.persistence.ElementCollection;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.ManyToMany;
import javax.persistence.MappedSuperclass;
import javax.persistence.OneToMany;
import javax.persistence.Transient;
@ -44,6 +45,13 @@ public class DefaultEnhancementContext implements EnhancementContext {
return classDescriptor.hasAnnotation( Embeddable.class );
}
/**
* look for @MappedSuperclass annotation
*/
public boolean isMappedSuperclassClass(CtClass classDescriptor) {
return classDescriptor.hasAnnotation( MappedSuperclass.class );
}
/**
* @return true
*/

View File

@ -52,6 +52,15 @@ public interface EnhancementContext {
*/
public boolean isCompositeClass(CtClass classDescriptor);
/**
* Does the given class name represent an MappedSuperclass class?
*
* @param classDescriptor The descriptor of the class to check.
*
* @return {@code true} if the class is an mapped super class; {@code false} otherwise.
*/
public boolean isMappedSuperclassClass(CtClass classDescriptor);
/**
* Should we manage association of bi-directional persistent attributes for this field?
*

View File

@ -20,9 +20,10 @@ 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.MappedSuperclassEnhancer;
import org.hibernate.bytecode.enhance.internal.PersistentAttributesEnhancer;
import org.hibernate.engine.spi.ManagedComposite;
import org.hibernate.engine.spi.ManagedEntity;
import org.hibernate.bytecode.enhance.internal.PersistentAttributesHelper;
import org.hibernate.engine.spi.Managed;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
import org.hibernate.internal.CoreLogging;
@ -33,6 +34,7 @@ import org.hibernate.internal.CoreMessageLogger;
*
* @author Steve Ebersole
* @author Jason Greene
* @author Luis Barreiro
*/
public class Enhancer {
private static final CoreMessageLogger log = CoreLogging.messageLogger( Enhancer.class );
@ -115,23 +117,25 @@ public class Enhancer {
return;
}
// skip already enhanced classes
for ( String interfaceName : managedCtClass.getClassFile2().getInterfaces() ) {
if ( ManagedEntity.class.getName().equals( interfaceName ) || ManagedComposite.class.getName().equals( interfaceName ) ) {
log.debugf( "Skipping enhancement of [%s]: already enhanced", managedCtClass.getName() );
return;
}
if ( PersistentAttributesHelper.isAssignable( managedCtClass, Managed.class.getName() ) ) {
log.debugf( "Skipping enhancement of [%s]: already enhanced", managedCtClass.getName() );
return;
}
if ( enhancementContext.isEntityClass( managedCtClass ) ) {
log.debugf( "Enhancing [%s] as Entity", managedCtClass.getName() );
log.infof( "Enhancing [%s] as Entity", managedCtClass.getName() );
new EntityEnhancer( enhancementContext ).enhance( managedCtClass );
}
else if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
log.debugf( "Enhancing [%s] as Composite", managedCtClass.getName() );
log.infof( "Enhancing [%s] as Composite", managedCtClass.getName() );
new CompositeEnhancer( enhancementContext ).enhance( managedCtClass );
}
else if ( enhancementContext.isMappedSuperclassClass( managedCtClass ) ) {
log.infof( "Enhancing [%s] as MappedSuperclass", managedCtClass.getName() );
new MappedSuperclassEnhancer( enhancementContext ).enhance( managedCtClass );
}
else if ( enhancementContext.doExtendedEnhancement( managedCtClass ) ) {
log.debugf( "Extended enhancement of [%s]", managedCtClass.getName() );
log.infof( "Extended enhancement of [%s]", managedCtClass.getName() );
new PersistentAttributesEnhancer( enhancementContext ).extendedEnhancement( managedCtClass );
}
else {
@ -174,11 +178,10 @@ public class Enhancer {
EnhancerConstants.INTERCEPTOR_SETTER_NAME );
}
/**
* @deprecated Should use enhance(String, byte[]) and a proper EnhancementContext
*/
@Deprecated( )
@Deprecated
public byte[] enhanceComposite(String className, byte[] originalBytes) throws EnhancementException {
return enhance( className, originalBytes );
}

View File

@ -0,0 +1,15 @@
/*
* 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.engine.spi;
/**
* Specialized {@link Managed} contract for MappedSuperclass classes.
*
* @author Luis Barreiro
*/
public interface ManagedMappedSuperclass extends Managed {
}

View File

@ -80,6 +80,11 @@ public class EnhancingClassTransformerImpl implements ClassTransformer {
return wrappedContext.isCompositeClass( classDescriptor );
}
@Override
public boolean isMappedSuperclassClass(CtClass classDescriptor) {
return wrappedContext.isMappedSuperclassClass( classDescriptor );
}
@Override
public boolean doBiDirectionalAssociationManagement(CtField field) {
return wrappedContext.doBiDirectionalAssociationManagement( field );

View File

@ -13,18 +13,11 @@ 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.DefaultEnhancementContext;
import org.hibernate.bytecode.enhance.spi.Enhancer;
import org.apache.tools.ant.BuildException;
@ -43,12 +36,12 @@ import org.apache.tools.ant.types.FileSet;
* @author Steve Ebersole
* @see org.hibernate.engine.spi.Managed
*/
public class EnhancementTask extends Task implements EnhancementContext {
public class EnhancementTask extends Task {
private List<FileSet> filesets = new ArrayList<FileSet>();
// Enhancer also builds CtClass instances. Might make sense to share these (ClassPool).
private final ClassPool classPool = new ClassPool( false );
private final Enhancer enhancer = new Enhancer( this );
private final Enhancer enhancer = new Enhancer( new DefaultEnhancementContext() );
public void addFileset(FileSet set) {
this.filesets.add( set );
@ -77,50 +70,16 @@ public class EnhancementTask extends Task implements EnhancementContext {
}
/**
* Atm only process files annotated with either @Entity or @Embeddable
*
* @param javaClassFile
*/
private void processClassFile(File javaClassFile) {
try {
final CtClass ctClass = classPool.makeClass( new FileInputStream( javaClassFile ) );
if ( this.isEntityClass( ctClass ) ) {
processEntityClassFile( javaClassFile, ctClass );
}
else if ( this.isCompositeClass( ctClass ) ) {
processCompositeClassFile( javaClassFile, ctClass );
}
}
catch (IOException e) {
throw new BuildException(
String.format( "Error processing included file [%s]", javaClassFile.getAbsolutePath() ), e
);
}
}
private void processEntityClassFile(File javaClassFile, CtClass ctClass) {
try {
byte[] result = enhancer.enhance( ctClass.getName(), ctClass.toBytecode() );
if ( result != null ) {
writeEnhancedClass( javaClassFile, result );
}
}
catch (Exception e) {
log( "Unable to enhance class [" + ctClass.getName() + "]", e, Project.MSG_WARN );
}
}
private void processCompositeClassFile(File javaClassFile, CtClass ctClass) {
try {
byte[] result = enhancer.enhanceComposite( ctClass.getName(), ctClass.toBytecode() );
if ( result != null ) {
writeEnhancedClass( javaClassFile, result );
}
}
catch (Exception e) {
log( "Unable to enhance class [" + ctClass.getName() + "]", e, Project.MSG_WARN );
log( "Unable to enhance class file [" + javaClassFile.getAbsolutePath() + "]", e, Project.MSG_WARN );
}
}
@ -158,69 +117,4 @@ public class EnhancementTask extends Task implements EnhancementContext {
}
}
// EnhancementContext impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public ClassLoader getLoadingClassLoader() {
return getClass().getClassLoader();
}
@Override
public boolean isEntityClass(CtClass classDescriptor) {
return classDescriptor.hasAnnotation( Entity.class );
}
@Override
public boolean isCompositeClass(CtClass classDescriptor) {
return classDescriptor.hasAnnotation( Embeddable.class );
}
@Override
public boolean doBiDirectionalAssociationManagement(CtField field) {
return false;
}
@Override
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
return true;
}
@Override
public boolean doExtendedEnhancement(CtClass classDescriptor) {
return false;
}
@Override
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
return true;
}
@Override
public boolean isLazyLoadable(CtField field) {
return true;
}
@Override
public boolean isPersistentField(CtField ctField) {
// current check is to look for @Transient
return !ctField.hasAnnotation( Transient.class );
}
@Override
public boolean isMappedCollection(CtField field) {
try {
return ( field.getAnnotation( OneToMany.class ) != null ||
field.getAnnotation( ManyToMany.class ) != null ||
field.getAnnotation( ElementCollection.class ) != null );
}
catch (ClassNotFoundException e) {
return false;
}
}
@Override
public CtField[] order(CtField[] persistentFields) {
// for now...
return persistentFields;
// eventually needs to consult the Hibernate metamodel for proper ordering
}
}

View File

@ -4,8 +4,6 @@ import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Version;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
@ -34,6 +32,13 @@ public class MappedSuperclassTestTask extends AbstractEnhancerTestTask {
// Check that both types of class attributes are being dirty tracked
EnhancerTestUtils.checkDirtyTracking( charles, "title", "oca" );
EnhancerTestUtils.clearDirtyTracking( charles );
// Let's give charles a promotion, this time using method references
charles.setOca( 99 );
charles.setTitle( "Manager" );
EnhancerTestUtils.checkDirtyTracking( charles, "title", "oca" );
}
protected void cleanup() {
@ -51,6 +56,10 @@ public class MappedSuperclassTestTask extends AbstractEnhancerTestTask {
}
protected Person() {}
protected void setOca(long l) {
this.oca = l;
}
}
@Entity private static class Employee extends Person {
@ -63,5 +72,9 @@ public class MappedSuperclassTestTask extends AbstractEnhancerTestTask {
}
public Employee() {}
public void setTitle(String title) {
this.title = title;
}
}
}

View File

@ -144,7 +144,9 @@ public class MavenEnhancePlugin extends AbstractMojo {
}
if ( !enableLazyInitialization ) {
if ( !enhancementContext.isEntityClass( ctClass ) && !enhancementContext.isCompositeClass( ctClass ) ) {
if ( !enhancementContext.isEntityClass( ctClass )
&& !enhancementContext.isCompositeClass( ctClass )
&& !enhancementContext.isMappedSuperclassClass( ctClass ) ) {
getLog().info( "Skipping class file [" + file.getAbsolutePath() + "], not an entity nor embeddable" );
continue;
}

View File

@ -133,7 +133,8 @@ public class HibernatePlugin implements Plugin<Project> {
final CtClass ctClass = toCtClass( file, classPool );
if ( !enhancementContext.isEntityClass( ctClass )
&& !enhancementContext.isCompositeClass( ctClass ) ) {
&& !enhancementContext.isCompositeClass( ctClass )
&& !enhancementContext.isMappedSuperclassClass( ctClass ) ) {
logger.info( "Skipping class [" + file.getAbsolutePath() + "], not an entity nor embeddable" );
continue;
}