Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.
EclipseLink/Examples/JPA/Dynamic/CustomizeAttributes
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
Contents
How DynamicEntity Handles 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.
- it does not support properties from super Class: https://bugs.eclipse.org/bugs/show_bug.cgi?id=390613
- Primitive value is automactically given a default value: https://bugs.eclipse.org/bugs/show_bug.cgi?id=393501
- You can not set null to primitive value: https://bugs.eclipse.org/bugs/show_bug.cgi?id=393500
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(); }