Skip to content

Commit

Permalink
HHH-9985 - remove hack based on exposed state and route all field acc…
Browse files Browse the repository at this point in the history
…ess to enhanced methods, if present
  • Loading branch information
barreiro committed Aug 5, 2015
1 parent ed185b9 commit c622d39
Show file tree
Hide file tree
Showing 13 changed files with 541 additions and 45 deletions.
Expand Up @@ -15,7 +15,6 @@
import org.hibernate.StaleObjectStateException;
import org.hibernate.WrongClassException;
import org.hibernate.boot.registry.selector.spi.StrategySelector;
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoader;
import org.hibernate.bytecode.instrumentation.spi.FieldInterceptor;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.internal.Cascade;
Expand All @@ -24,8 +23,6 @@
import org.hibernate.engine.spi.CascadingActions;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
import org.hibernate.engine.spi.SelfDirtinessTracker;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
Expand Down Expand Up @@ -346,26 +343,15 @@ private void markInterceptorDirty(final Object entity, final Object target, Enti
}
}

// for enhanced entities, copy over the dirty attributes and the lazy/loaded fields in the interceptor
// for enhanced entities, copy over the dirty attributes
if ( entity instanceof SelfDirtinessTracker && target instanceof SelfDirtinessTracker ) {
// clear, because setting the embedded attributes dirties them
( (SelfDirtinessTracker) target ).$$_hibernate_clearDirtyAttributes();

for ( String fieldName : ( (SelfDirtinessTracker) entity ).$$_hibernate_getDirtyAttributes() ) {
( (SelfDirtinessTracker) target ).$$_hibernate_trackChange( fieldName );
}
}
if ( entity instanceof PersistentAttributeInterceptable
&& target instanceof PersistentAttributeInterceptable
&& ( (PersistentAttributeInterceptable) entity ).$$_hibernate_getInterceptor() != null
&& ( (PersistentAttributeInterceptable) target ).$$_hibernate_getInterceptor() != null ) {

PersistentAttributeInterceptor entityInterceptor = ( (PersistentAttributeInterceptable) entity ).$$_hibernate_getInterceptor();
PersistentAttributeInterceptor targetInterceptor = ( (PersistentAttributeInterceptable) target ).$$_hibernate_getInterceptor();

if ( entityInterceptor instanceof LazyAttributeLoader && targetInterceptor instanceof LazyAttributeLoader ) {
for ( String fieldName : ( (LazyAttributeLoader) entityInterceptor ).getiInitializedFields() ) {
( (LazyAttributeLoader) targetInterceptor ).setLoaded( fieldName );
}
}
}
}

private boolean isVersionChanged(Object entity, EventSource source, EntityPersister persister, Object target) {
Expand Down Expand Up @@ -415,7 +401,6 @@ protected void copyValues(
final Object[] copiedValues = TypeHelper.replace(
persister.getPropertyValues( entity ),
persister.getPropertyValues( target ),
persister.getPropertyNames(),
persister.getPropertyTypes(),
source,
target,
Expand Down Expand Up @@ -453,7 +438,6 @@ protected void copyValues(
copiedValues = TypeHelper.replace(
persister.getPropertyValues( entity ),
persister.getPropertyValues( target ),
persister.getPropertyNames(),
persister.getPropertyTypes(),
source,
target,
Expand Down
Expand Up @@ -330,6 +330,7 @@ public PropertyAccessStrategy getPropertyAccessStrategy(Class clazz) throws Mapp
: EntityMode.POJO;

return resolveServiceRegistry().getService( PropertyAccessStrategyResolver.class ).resolvePropertyAccessStrategy(
clazz,
accessName,
entityMode
);
Expand Down
@@ -0,0 +1,152 @@
/*
* 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.property.access.internal;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import org.hibernate.PropertyNotFoundException;
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.property.access.spi.EnhancedGetterMethodImpl;
import org.hibernate.property.access.spi.EnhancedSetterMethodImpl;
import org.hibernate.property.access.spi.Getter;
import org.hibernate.property.access.spi.GetterFieldImpl;
import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.property.access.spi.PropertyAccessBuildingException;
import org.hibernate.property.access.spi.PropertyAccessStrategy;
import org.hibernate.property.access.spi.Setter;
import org.hibernate.property.access.spi.SetterFieldImpl;

/**
* A PropertyAccess for byte code enhanced entities. Enhanced getter / setter methods ( if available ) are used for
* field access. Regular getter / setter methods are used for property access. In both cases, delegates calls to
* EnhancedMethodGetterImpl / EnhancedMethodGetterImpl. Based upon PropertyAccessMixedImpl.
*
* @author Steve Ebersole
* @author Luis Barreiro
*/
public class PropertyAccessEnhancedImpl implements PropertyAccess {
private final PropertyAccessStrategyEnhancedImpl strategy;

private final Getter getter;
private final Setter setter;

public PropertyAccessEnhancedImpl(
PropertyAccessStrategyEnhancedImpl strategy,
Class containerJavaType,
String propertyName) {
this.strategy = strategy;

final Field field = fieldOrNull( containerJavaType, propertyName );
final Method getterMethod = getterMethodOrNull( containerJavaType, propertyName );

final Class propertyJavaType;

// need one of field or getterMethod to be non-null
if ( field == null && getterMethod == null ) {
String msg = String.format( "Could not locate field nor getter method for property named [%s#%s]",
containerJavaType.getName(),
propertyName );
throw new PropertyAccessBuildingException( msg );
}
else if ( field != null ) {
propertyJavaType = field.getType();
this.getter = resolveGetterForField( containerJavaType, propertyName, field );
}
else {
propertyJavaType = getterMethod.getReturnType();
this.getter = new EnhancedGetterMethodImpl( containerJavaType, propertyName, getterMethod );
}

final Method setterMethod = setterMethodOrNull( containerJavaType, propertyName, propertyJavaType );

// need one of field or setterMethod to be non-null
if ( field == null && setterMethod == null ) {
String msg = String.format( "Could not locate field nor getter method for property named [%s#%s]",
containerJavaType.getName(),
propertyName );
throw new PropertyAccessBuildingException( msg );
}
else if ( field != null ) {
this.setter = resolveSetterForField( containerJavaType, propertyName, field );
}
else {
this.setter = new EnhancedSetterMethodImpl( containerJavaType, propertyName, setterMethod );
}
}

private static Field fieldOrNull(Class containerJavaType, String propertyName) {
try {
return ReflectHelper.findField( containerJavaType, propertyName );
}
catch (PropertyNotFoundException e) {
return null;
}
}

private static Method getterMethodOrNull(Class containerJavaType, String propertyName) {
try {
return ReflectHelper.findGetterMethod( containerJavaType, propertyName );
}
catch (PropertyNotFoundException e) {
return null;
}
}

private static Method setterMethodOrNull(Class containerJavaType, String propertyName, Class propertyJavaType) {
try {
return ReflectHelper.findSetterMethod( containerJavaType, propertyName, propertyJavaType );
}
catch (PropertyNotFoundException e) {
return null;
}
}

//

private static Getter resolveGetterForField(Class<?> containerClass, String propertyName, Field field) {
try {
String enhancedGetterName = EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + propertyName;
Method enhancedGetter = containerClass.getDeclaredMethod( enhancedGetterName );
enhancedGetter.setAccessible( true );
return new EnhancedGetterMethodImpl( containerClass, propertyName, enhancedGetter );
}
catch (NoSuchMethodException e) {
// enhancedGetter = null --- field not enhanced: fallback to reflection using the field
return new GetterFieldImpl( containerClass, propertyName, field );
}
}

private static Setter resolveSetterForField(Class<?> containerClass, String propertyName, Field field) {
try {
String enhancedSetterName = EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + propertyName;
Method enhancedSetter = containerClass.getDeclaredMethod( enhancedSetterName, field.getType() );
enhancedSetter.setAccessible( true );
return new EnhancedSetterMethodImpl( containerClass, propertyName, enhancedSetter );
}
catch (NoSuchMethodException e) {
// enhancedSetter = null --- field not enhanced: fallback to reflection using the field
return new SetterFieldImpl( containerClass, propertyName, field );
}
}

@Override
public PropertyAccessStrategy getPropertyAccessStrategy() {
return strategy;
}

@Override
public Getter getGetter() {
return getter;
}

@Override
public Setter getSetter() {
return setter;
}
}
Expand Up @@ -22,8 +22,8 @@
*/
public class PropertyAccessFieldImpl implements PropertyAccess {
private final PropertyAccessStrategyFieldImpl strategy;
private final GetterFieldImpl getter;
private final SetterFieldImpl setter;
private final Getter getter;
private final Setter setter;

public PropertyAccessFieldImpl(
PropertyAccessStrategyFieldImpl strategy,
Expand All @@ -50,4 +50,5 @@ public Getter getGetter() {
public Setter getSetter() {
return setter;
}

}
@@ -0,0 +1,29 @@
/*
* 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.property.access.internal;

import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.property.access.spi.PropertyAccessStrategy;

/**
* Defines a strategy for accessing property values via a get/set pair, which may be nonpublic. This
* is the default (and recommended) strategy.
*
* @author Steve Ebersole
* @author Gavin King
*/
public class PropertyAccessStrategyEnhancedImpl implements PropertyAccessStrategy {
/**
* Singleton access
*/
public static final PropertyAccessStrategyEnhancedImpl INSTANCE = new PropertyAccessStrategyEnhancedImpl();

@Override
public PropertyAccess buildPropertyAccess(Class containerJavaType, final String propertyName) {
return new PropertyAccessEnhancedImpl( this, containerJavaType, propertyName );
}
}
Expand Up @@ -6,9 +6,12 @@
*/
package org.hibernate.property.access.internal;

import java.lang.reflect.Method;

import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.boot.registry.selector.spi.StrategySelector;
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies;
import org.hibernate.property.access.spi.PropertyAccessStrategy;
Expand All @@ -29,8 +32,14 @@ public PropertyAccessStrategyResolverStandardImpl(ServiceRegistry serviceRegistr

@Override
public PropertyAccessStrategy resolvePropertyAccessStrategy(
Class containerClass,
String explicitAccessStrategyName,
EntityMode entityMode) {

if ( hasBytecodeEnhancedAttributes( containerClass ) ) {
return PropertyAccessStrategyEnhancedImpl.INSTANCE;
}

if ( StringHelper.isNotEmpty( explicitAccessStrategyName ) ) {
return resolveExplicitlyNamedPropertyAccessStrategy( explicitAccessStrategyName );
}
Expand Down Expand Up @@ -65,4 +74,15 @@ protected StrategySelector strategySelectorService() {
}
return strategySelectorService;
}

private boolean hasBytecodeEnhancedAttributes(Class<?> containerClass) {
for ( Method m : containerClass.getDeclaredMethods() ) {
if ( m.getName().startsWith( EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX ) ||
m.getName().startsWith( EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX ) ) {
return true;
}
}
return false;
}

}

0 comments on commit c622d39

Please sign in to comment.