Jump to: navigation, search

EclipseLink/Examples/JPA/Dynamic/CustomizeAttributes

< EclipseLink‎ | Examples‎ | JPA‎ | Dynamic
Revision as of 19:03, 15 December 2012 by Jasonzhang2002.gmail.com (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Eclipse link Dynamic is really a neat feature. It could be very useful in some situations. Unfortunately, it is not actively used. It is slow to have bugs fixed and have new features incorporated. Here I illustrate how you can customize the property manager in DyanmicEntity


How DynamicEntity Hanldes Properties

Each generated virtual class has a static field named: DynamicPropertiesManager.PROPERTIES_MANAGER_FIELD. It holds an instanceof DynamicPropertiesManager. DynamicPropertiesManager judges what properties are valid for the Dynamic type. It also contains DynamicPropertiesInitializatonPolicy which initializes the default value for property.

So you can change the DynamicPropertiesInitializatonPolicy in generated class and have your own attributes management and default initialization policy.

Customize Dynamic Attribute Handling

I found some issues/bugs in DynamicPropertiesManager and DynamicPropertiesInitializatonPolicy.

My purpose is to fix these issues by customization.

Step 1: create your own DynamicPropertiesInitializatonPolicy

//https://bugs.eclipse.org/bugs/show_bug.cgi?id=393500
public class NullPrimitiveInitializerPolicy extends
		DynamicPropertiesInitializatonPolicy
{
 
	/*
	 * After entity is created, initialize all required attributes.
	 * 
	 * @param type
	 * 
	 * @param entity
	 */
	public void initializeProperties(DynamicTypeImpl type,
			DynamicEntityImpl entity)
	{
		if (type != null)
		{
			for (DatabaseMapping mapping : type
					.getMappingsRequiringInitialization())
			{
				initializeDefaultValue(mapping, entity);
			}
		}
	}
 
	/**
	 * Initialize the default value handling primitives, collections and
	 * indirection.
	 * 
	 * @param mapping
	 * @param entity
	 */
	private void initializeDefaultValue(DatabaseMapping mapping,
			DynamicEntityImpl entity)
	{
		Object value = null;
		if (mapping.isDirectToFieldMapping()
				&& mapping.getAttributeClassification().isPrimitive())
		{
			Class<?> primClass = mapping.getAttributeClassification();
			if (primClass == ClassConstants.PBOOLEAN)
			{
				value = false;
			} else
			{
				value = null;
			}
 
			// Do not implement primitive type
			/*
			 * else if (primClass == ClassConstants.PINT) { value = 0; } else if
			 * (primClass == ClassConstants.PLONG) { value = 0L; } else if
			 * (primClass == ClassConstants.PCHAR) { value =
			 * Character.MIN_VALUE; } else if (primClass ==
			 * ClassConstants.PDOUBLE) { value = 0.0d; } else if (primClass ==
			 * ClassConstants.PFLOAT) { value = 0.0f; } else if (primClass ==
			 * ClassConstants.PSHORT) { value = Short.MIN_VALUE; } else if
			 * (primClass == ClassConstants.PBYTE) { value = Byte.MIN_VALUE; }
			 */
		} else if (mapping.isForeignReferenceMapping())
		{
			ForeignReferenceMapping refMapping = (ForeignReferenceMapping) mapping;
			if (refMapping.usesIndirection()
					&& refMapping.getIndirectionPolicy() instanceof BasicIndirectionPolicy)
			{
				value = new ValueHolder(value);
			} else if (refMapping.isCollectionMapping())
			{
				value = ((CollectionMapping) refMapping).getContainerPolicy()
						.containerInstance();
			}
		} else if (mapping.isAggregateObjectMapping())
		{
			value = mapping.getReferenceDescriptor().getObjectBuilder()
					.buildNewInstance();
		}
		PropertyWrapper propertyWrapper = entity.getPropertiesMap().get(
				mapping.getAttributeName());
		// NB - only the value is set, not the 'isSet' boolean
		propertyWrapper.setValue(value);
	}
}

Step2 : create your own DynamicPropertiesManager

//https://bugs.eclipse.org/bugs/show_bug.cgi?id=390613
public class InheritedPropertiesManager extends DynamicPropertiesManager
{
 
	public InheritedPropertiesManager()
	{
		super();
		dpInitializatonPolicy = new NullPrimitiveInitializerPolicy();
	}
 
	protected void initializeSlotValues(DynamicEntityImpl entity)
	{
		DynamicTypeImpl ctype = type;
		while (ctype != null)
		{
			getInitializatonPolicy().initializeProperties(ctype, entity);
			ctype = (DynamicTypeImpl) ctype.getParentType();
		}
	}
 
	// delegate to descriptor
	public boolean contains(String propertyName)
	{
		boolean contains = false;
		DynamicTypeImpl ctype = type;
		while (ctype != null)
		{
			if (ctype != null && ctype.getDescriptor() != null)
			{
				for (DatabaseMapping dm : ctype.getDescriptor().getMappings())
				{
					if (dm.getAttributeName().equals(propertyName))
					{
						contains = true;
						break;
					}
				}
			}
			ctype = (DynamicTypeImpl) ctype.getParentType();
		}
		return contains;
	}
 
	@Override
	public List<String> getPropertyNames()
	{
		List<String> propertyNames = new ArrayList<String>();
		DynamicTypeImpl ctype = type;
		while (ctype != null)
		{
			if (ctype != null && ctype.getDescriptor() != null)
			{
				for (DatabaseMapping dm : ctype.getDescriptor().getMappings())
				{
					propertyNames.add(dm.getAttributeName());
				}
			}
			ctype = (DynamicTypeImpl) ctype.getParentType();
		}
		return propertyNames;
	}
 
	//this is the code copied from DynamicTyeImpl
	//https://bugs.eclipse.org/bugs/show_bug.cgi?id=393500
	protected void internalcheckSet(String propertyName, Object value, DynamicTypeImpl ctype) throws DynamicException
	{
	        DatabaseMapping mapping = ctype.getMapping(propertyName);
	        /*
	        if (value == null) {
	            if (mapping.isCollectionMapping() || 
	                (mapping.getAttributeClassification() != null && 
	                 mapping.getAttributeClassification().isPrimitive())) {
	                throw DynamicException.invalidSetPropertyType(mapping, value);
	            }
	            return;
	        }
	        */
	        //remove restriction on primitive type
	        if (value == null) {
	            if (mapping.isCollectionMapping() ) {
	                throw DynamicException.invalidSetPropertyType(mapping, value);
	            }
	            return;
	        }
	        Class<?> expectedType = mapping.getAttributeClassification();
	        if (mapping.isForeignReferenceMapping()) {
	            if (mapping.isCollectionMapping()) {
	                if (((CollectionMapping) mapping).getContainerPolicy().isMapPolicy()) {
	                    expectedType = Map.class;
	                } else {
	                    expectedType = Collection.class;
	                }
	            } else {
	                expectedType = ((ForeignReferenceMapping)mapping).getReferenceClass();
	            }
	        }
	        if (expectedType != null && expectedType.isPrimitive() && !value.getClass().isPrimitive()) {
	            expectedType = Helper.getObjectClass(expectedType);
	        }
	        if (expectedType != null && !expectedType.isAssignableFrom(value.getClass())) {
	            throw DynamicException.invalidSetPropertyType(mapping, value);
	        }
 
 
	}
 
	public void checkSet(String propertyName, Object value)
	{
		DynamicTypeImpl ctype = type;
		boolean checked = false;
		while (ctype != null)
		{
			if (ctype.containsProperty(propertyName))
			{
				checked = true;
				internalcheckSet(propertyName, value, ctype);
				//ctype.checkSet(propertyName, value);
			}
 
			ctype = (DynamicTypeImpl) ctype.getParentType();
		}
		if (!checked)
		{
			throw DynamicException.invalidPropertyName(type, propertyName);
		}
	}
 
}

Step 3: use your DynamicPropertiesManager in your Dynamic Entity Class

DynamicTypeImpl timpl = (DynamicTypeImpl) builder.getType();
try
{
 
	Field dpmField = timpl
			.getDescriptor()
			.getJavaClass()
			.getField(
	DynamicPropertiesManager.PROPERTIES_MANAGER_FIELD);
	InheritedPropertiesManager dpm = new InheritedPropertiesManager();
	dpm.setType(timpl);
	timpl.setDynamicPropertiesManager(dpm);
	dpmField.set(null, dpm);
} catch (Exception e)
{
	e.printStackTrace();
}