Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gradle/libraries.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ ext {
weldVersion = '3.0.0.Final'

javassistVersion = '3.23.1-GA'
byteBuddyVersion = '1.8.17' // Now with JDK10 compatibility and preliminary support for JDK11
byteBuddyVersion = '1.9.4'

geolatteVersion = '1.3.0'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@

import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;

import org.hibernate.bytecode.enhance.internal.bytebuddy.EnhancerImpl.AnnotatedFieldDescription;
import org.hibernate.bytecode.enhance.spi.EnhancementException;
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
import org.hibernate.internal.CoreLogging;
Expand All @@ -34,14 +38,14 @@
import net.bytebuddy.jar.asm.Opcodes;
import net.bytebuddy.jar.asm.Type;

class BiDirectionalAssociationHandler implements Implementation {
final class BiDirectionalAssociationHandler implements Implementation {

private static final CoreMessageLogger log = CoreLogging.messageLogger( BiDirectionalAssociationHandler.class );

static Implementation wrap(
TypeDescription managedCtClass,
ByteBuddyEnhancementContext enhancementContext,
FieldDescription persistentField,
AnnotatedFieldDescription persistentField,
Implementation implementation) {
if ( !enhancementContext.doBiDirectionalAssociationManagement( persistentField ) ) {
return implementation;
Expand All @@ -67,33 +71,33 @@ static Implementation wrap(
.getType()
.asErasure();

if ( EnhancerImpl.isAnnotationPresent( persistentField, OneToOne.class ) ) {
if ( persistentField.hasAnnotation( OneToOne.class ) ) {
implementation = Advice.withCustomMapping()
.bind( CodeTemplates.FieldValue.class, persistentField )
.bind( CodeTemplates.FieldValue.class, persistentField.getFieldDescription() )
.bind( CodeTemplates.MappedBy.class, mappedBy )
.to( CodeTemplates.OneToOneHandler.class )
.wrap( implementation );
}

if ( EnhancerImpl.isAnnotationPresent( persistentField, OneToMany.class ) ) {
if ( persistentField.hasAnnotation( OneToMany.class ) ) {
implementation = Advice.withCustomMapping()
.bind( CodeTemplates.FieldValue.class, persistentField )
.bind( CodeTemplates.FieldValue.class, persistentField.getFieldDescription() )
.bind( CodeTemplates.MappedBy.class, mappedBy )
.to( persistentField.getType().asErasure().isAssignableTo( Map.class )
? CodeTemplates.OneToManyOnMapHandler.class
: CodeTemplates.OneToManyOnCollectionHandler.class )
.wrap( implementation );
}

if ( EnhancerImpl.isAnnotationPresent( persistentField, ManyToOne.class ) ) {
if ( persistentField.hasAnnotation( ManyToOne.class ) ) {
implementation = Advice.withCustomMapping()
.bind( CodeTemplates.FieldValue.class, persistentField )
.bind( CodeTemplates.FieldValue.class, persistentField.getFieldDescription() )
.bind( CodeTemplates.MappedBy.class, mappedBy )
.to( CodeTemplates.ManyToOneHandler.class )
.wrap( implementation );
}

if ( EnhancerImpl.isAnnotationPresent( persistentField, ManyToMany.class ) ) {
if ( persistentField.hasAnnotation( ManyToMany.class ) ) {

if ( persistentField.getType().asErasure().isAssignableTo( Map.class ) || targetType.isAssignableTo( Map.class ) ) {
log.infof(
Expand All @@ -105,7 +109,7 @@ static Implementation wrap(
}

implementation = Advice.withCustomMapping()
.bind( CodeTemplates.FieldValue.class, persistentField )
.bind( CodeTemplates.FieldValue.class, persistentField.getFieldDescription() )
.bind( CodeTemplates.MappedBy.class, mappedBy )
.to( CodeTemplates.ManyToManyHandler.class )
.wrap( implementation );
Expand All @@ -114,12 +118,12 @@ static Implementation wrap(
return new BiDirectionalAssociationHandler( implementation, targetEntity, targetType, mappedBy );
}

public static TypeDescription getTargetEntityClass(TypeDescription managedCtClass, FieldDescription persistentField) {
public static TypeDescription getTargetEntityClass(TypeDescription managedCtClass, AnnotatedFieldDescription persistentField) {
try {
AnnotationDescription.Loadable<OneToOne> oto = EnhancerImpl.getAnnotation( persistentField, OneToOne.class );
AnnotationDescription.Loadable<OneToMany> otm = EnhancerImpl.getAnnotation( persistentField, OneToMany.class );
AnnotationDescription.Loadable<ManyToOne> mto = EnhancerImpl.getAnnotation( persistentField, ManyToOne.class );
AnnotationDescription.Loadable<ManyToMany> mtm = EnhancerImpl.getAnnotation( persistentField, ManyToMany.class );
AnnotationDescription.Loadable<OneToOne> oto = persistentField.getAnnotation( OneToOne.class );
AnnotationDescription.Loadable<OneToMany> otm = persistentField.getAnnotation( OneToMany.class );
AnnotationDescription.Loadable<ManyToOne> mto = persistentField.getAnnotation( ManyToOne.class );
AnnotationDescription.Loadable<ManyToMany> mtm = persistentField.getAnnotation( ManyToMany.class );

if ( oto == null && otm == null && mto == null && mtm == null ) {
return null;
Expand Down Expand Up @@ -157,23 +161,23 @@ else if ( !targetClass.resolve( TypeDescription.class ).represents( void.class )
return entityType( target( persistentField ) );
}

private static TypeDescription.Generic target(FieldDescription persistentField) {
private static TypeDescription.Generic target(AnnotatedFieldDescription persistentField) {
AnnotationDescription.Loadable<Access> access = persistentField.getDeclaringType().asErasure().getDeclaredAnnotations().ofType( Access.class );
if ( access != null && access.loadSilent().value() == AccessType.FIELD ) {
return persistentField.getType();
}
else {
MethodDescription getter = EnhancerImpl.getterOf( persistentField );
if ( getter == null ) {
return persistentField.getType();
Optional<MethodDescription> getter = persistentField.getGetter();
if ( getter.isPresent() ) {
return getter.get().getReturnType();
}
else {
return getter.getReturnType();
return persistentField.getType();
}
}
}

private static String getMappedBy(FieldDescription target, TypeDescription targetEntity, ByteBuddyEnhancementContext context) {
private static String getMappedBy(AnnotatedFieldDescription target, TypeDescription targetEntity, ByteBuddyEnhancementContext context) {
String mappedBy = getMappedByNotManyToMany( target );
if ( mappedBy == null || mappedBy.isEmpty() ) {
return getMappedByManyToMany( target, targetEntity, context );
Expand All @@ -183,19 +187,19 @@ private static String getMappedBy(FieldDescription target, TypeDescription targe
}
}

private static String getMappedByNotManyToMany(FieldDescription target) {
private static String getMappedByNotManyToMany(AnnotatedFieldDescription target) {
try {
AnnotationDescription.Loadable<OneToOne> oto = EnhancerImpl.getAnnotation( target, OneToOne.class );
AnnotationDescription.Loadable<OneToOne> oto = target.getAnnotation( OneToOne.class );
if ( oto != null ) {
return oto.getValue( new MethodDescription.ForLoadedMethod( OneToOne.class.getDeclaredMethod( "mappedBy" ) ) ).resolve( String.class );
}

AnnotationDescription.Loadable<OneToMany> otm = EnhancerImpl.getAnnotation( target, OneToMany.class );
AnnotationDescription.Loadable<OneToMany> otm = target.getAnnotation( OneToMany.class );
if ( otm != null ) {
return otm.getValue( new MethodDescription.ForLoadedMethod( OneToMany.class.getDeclaredMethod( "mappedBy" ) ) ).resolve( String.class );
}

AnnotationDescription.Loadable<ManyToMany> mtm = EnhancerImpl.getAnnotation( target, ManyToMany.class );
AnnotationDescription.Loadable<ManyToMany> mtm = target.getAnnotation( ManyToMany.class );
if ( mtm != null ) {
return mtm.getValue( new MethodDescription.ForLoadedMethod( ManyToMany.class.getDeclaredMethod( "mappedBy" ) ) ).resolve( String.class );
}
Expand All @@ -206,11 +210,12 @@ private static String getMappedByNotManyToMany(FieldDescription target) {
return null;
}

private static String getMappedByManyToMany(FieldDescription target, TypeDescription targetEntity, ByteBuddyEnhancementContext context) {
private static String getMappedByManyToMany(AnnotatedFieldDescription target, TypeDescription targetEntity, ByteBuddyEnhancementContext context) {
for ( FieldDescription f : targetEntity.getDeclaredFields() ) {
if ( context.isPersistentField( f )
&& target.getName().equals( getMappedByNotManyToMany( f ) )
&& target.getDeclaringType().asErasure().isAssignableTo( entityType( f.getType() ) ) ) {
AnnotatedFieldDescription annotatedF = new AnnotatedFieldDescription( context, f );
if ( context.isPersistentField( annotatedF )
&& target.getName().equals( getMappedByNotManyToMany( annotatedF ) )
&& target.getDeclaringType().asErasure().isAssignableTo( entityType( annotatedF.getType() ) ) ) {
log.debugf(
"mappedBy association for field [%s#%s] is [%s#%s]",
target.getDeclaringType().asErasure().getName(),
Expand Down Expand Up @@ -327,4 +332,24 @@ else if ( name.equals( "setterNull" ) ) {
}, implementationContext, instrumentedMethod );
}
}

@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if ( o == null || BiDirectionalAssociationHandler.class != o.getClass() ) {
return false;
}
final BiDirectionalAssociationHandler that = (BiDirectionalAssociationHandler) o;
return Objects.equals( delegate, that.delegate ) &&
Objects.equals( targetEntity, that.targetEntity ) &&
Objects.equals( targetType, that.targetType ) &&
Objects.equals( mappedBy, that.mappedBy );
}

@Override
public int hashCode() {
return Objects.hash( delegate, targetEntity, targetType, mappedBy );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,33 @@
*/
package org.hibernate.bytecode.enhance.internal.bytebuddy;

import static net.bytebuddy.matcher.ElementMatchers.isGetter;

import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.hibernate.MappingException;
import org.hibernate.bytecode.enhance.internal.bytebuddy.EnhancerImpl.AnnotatedFieldDescription;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.bytecode.enhance.spi.UnloadedField;

import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.scaffold.MethodGraph;
import net.bytebuddy.matcher.ElementMatcher;

class ByteBuddyEnhancementContext {

private static final ElementMatcher.Junction<MethodDescription> IS_GETTER = isGetter();

private final EnhancementContext enhancementContext;

private final ConcurrentHashMap<TypeDescription, Map<String, MethodDescription>> getterByTypeMap = new ConcurrentHashMap<>();

ByteBuddyEnhancementContext(EnhancementContext enhancementContext) {
this.enhancementContext = enhancementContext;
}
Expand All @@ -36,10 +53,6 @@ public boolean isMappedSuperclassClass(TypeDescription classDescriptor) {
return enhancementContext.isMappedSuperclassClass( new UnloadedTypeDescription( classDescriptor ) );
}

public boolean doBiDirectionalAssociationManagement(FieldDescription field) {
return enhancementContext.doBiDirectionalAssociationManagement( new UnloadedFieldDescription( field ) );
}

public boolean doDirtyCheckingInline(TypeDescription classDescriptor) {
return enhancementContext.doDirtyCheckingInline( new UnloadedTypeDescription( classDescriptor ) );
}
Expand All @@ -52,28 +65,54 @@ public boolean hasLazyLoadableAttributes(TypeDescription classDescriptor) {
return enhancementContext.hasLazyLoadableAttributes( new UnloadedTypeDescription( classDescriptor ) );
}

public boolean isPersistentField(FieldDescription ctField) {
return enhancementContext.isPersistentField( new UnloadedFieldDescription( ctField ) );
public boolean isPersistentField(AnnotatedFieldDescription field) {
return enhancementContext.isPersistentField( field );
}

public FieldDescription[] order(FieldDescription[] persistentFields) {
UnloadedField[] unloadedFields = new UnloadedField[persistentFields.length];
for ( int i = 0; i < unloadedFields.length; i++ ) {
unloadedFields[i] = new UnloadedFieldDescription( persistentFields[i] );
}
UnloadedField[] ordered = enhancementContext.order( unloadedFields );
FieldDescription[] orderedFields = new FieldDescription[persistentFields.length];
for ( int i = 0; i < orderedFields.length; i++ ) {
orderedFields[i] = ( (UnloadedFieldDescription) ordered[i] ).fieldDescription;
}
return orderedFields;
public AnnotatedFieldDescription[] order(AnnotatedFieldDescription[] persistentFields) {
return (AnnotatedFieldDescription[]) enhancementContext.order( persistentFields );
}

public boolean isLazyLoadable(AnnotatedFieldDescription field) {
return enhancementContext.isLazyLoadable( field );
}

public boolean isLazyLoadable(FieldDescription field) {
return enhancementContext.isLazyLoadable( new UnloadedFieldDescription( field ) );
public boolean isMappedCollection(AnnotatedFieldDescription field) {
return enhancementContext.isMappedCollection( field );
}

public boolean isMappedCollection(FieldDescription field) {
return enhancementContext.isMappedCollection( new UnloadedFieldDescription( field ) );
public boolean doBiDirectionalAssociationManagement(AnnotatedFieldDescription field) {
return enhancementContext.doBiDirectionalAssociationManagement( field );
}

Optional<MethodDescription> resolveGetter(FieldDescription fieldDescription) {
Map<String, MethodDescription> getters = getterByTypeMap
.computeIfAbsent( fieldDescription.getDeclaringType().asErasure(), declaringType -> {
return MethodGraph.Compiler.DEFAULT.compile( declaringType )
.listNodes()
.asMethodList()
.filter( IS_GETTER )
.stream()
.collect( Collectors.toMap( MethodDescription::getActualName, Function.identity() ) );
} );

String capitalizedFieldName = Character.toUpperCase( fieldDescription.getName().charAt( 0 ) )
+ fieldDescription.getName().substring( 1 );

MethodDescription getCandidate = getters.get( "get" + capitalizedFieldName );
MethodDescription isCandidate = getters.get( "is" + capitalizedFieldName );

if ( getCandidate != null ) {
if ( isCandidate != null ) {
// if there are two candidates, the existing code considered there was no getter.
// not sure it's such a good idea but throwing an exception apparently throws exception
// in cases where Hibernate does not usually throw a mapping error.
return Optional.empty();
}

return Optional.of( getCandidate );
}

return Optional.ofNullable( isCandidate );
}
}
Loading