Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "EclipseLink/Development/Dynamic"

(Functional Requirements)
(Dynamic Model)
Line 284: Line 284:
 
This custom accessor requires a correlation be made between an attribute and its position (<tt>fieldIdx</tt>) in the store.
 
This custom accessor requires a correlation be made between an attribute and its position (<tt>fieldIdx</tt>) in the store.
  
<b><u>NB</u></b> TBD - should the store mechanism be abstracted to an interface, as in SDO? What about alternate store
+
<b><u>Questions</u></b><br />
implementations, such as a HashMap?
+
* Should the store mechanism be abstracted to an interface, as in SDO?
 +
* What about alternate store implementations, such as a HashMap?
 +
* What about path-style attributes/
 +
** MOXy - DOM XPath attributes: '/a/b/c'
 +
** ORM - POJO navigation: address.city.zipcode
  
 
=== Metadata Processing ===
 
=== Metadata Processing ===

Revision as of 15:11, 2 September 2009

EclipseLink Dynamic Persistence

This page captures the functional requirements and design of a dynamic persistence solution that will allow consumers to specify their mappings. The purpose of dynamic persistence is to enable simplified data access where only mapping information is required and no concrete Java model is required.

The work is being tracked by bug 200045 and is scheduled for inclusion in EclipseLink 1.2

Terminology

Dynamic Persistence is defined as the ability to create a dynamic persistent entity and use it within an application without a-priori the entity's Java .class being present on the classpath (or in some .jar/.war archive).

The purpose of Dynamic Persistence is to enable simplified data access where only mapping information is required and no concrete Java model is required. This allows applications with dynamic storage requirements to avoid coupling to static classes or require specialized handling of new types. The application uses standard EclipseLink APIs to read, create, modify, and remove dynamic persistent entities from their data stores based on types that are either defined in XML mapping files or are constructed within the running application.


Static Configuration

A static configuration defines the dynamic persistent entity's structure and mappings in XML packaged with the application. This is used when the entity is generated before/during deployment and no runtime alteration of its structure is required.

Dynamic Configuration

A dynamic configuration defines the dynamic persistent entity's structure and mappings at runtime, as part of the running application.

Functional Requirements

  1. Ability to use dynamic persistence through standard JPA metadata and interfaces as well as leveraging advanced EclipseLink JPA features
    • Specify mapping metadata using persistence.xml and eclipselink-orm.xml.
    • Create dynamic types at runtime
    • Access dynamic types through standard (JPA2) metamodel + helpers
    • Support removal of dynamic types (MWN - huh?)
  2. Ability to use dynamic persistence through MOXy mappings
  3. Ability to use dynamic persistence within DBWS to share common infrastructure
  4. Ability to use dynamic persistence within SDO to share common infrastructure - problem with mis-matched APIs:
public interface DataObject {
 
    void set(String s, Object o);
 
}
public interface DynamicEntity {
 
    public DynamicEntity set(String s, Object o);
 
}
public class BaseEntityImpl {
 
    public Object set(String s, Object o) {
 
}
public class SDOBaseEntityImpl extends BaseEntityImpl implements DataObject {
 
    public void set(String s, Object o) {
           ^^^^^
          return type is incompatible with BaseEntityImpl.set
}

Future Features (Current Limitations)

In order to control the scope of this feature the following limitations have been intended and are listed here as potential future enhancements.

  1. Fixed Base Class: This feature assumes the base entity class. In the future users may want the flexibility to specify a class of their own.
  2. LimitedMapping Capability: This feature does not provide support for all mapping types. The mappings currently not supported include:
    • Inheritance - Note JPA2 meta-data supports inheritance, so this limitation has to go away soon!
    • Embedded/EmbeddedId
    • Composite primary keys
  3. Hybrid Classes: This feature only supports DynamicEntity persistent classes. In the future developers may want the ability to combine static java classes with one or more attributes accessed dynamically. The DynamicEntityImpl class provides support for lazy loading, fetch-groups, and attribute change-tracking. Combining this with traditional static attributes and weaving complex.

Incubator Exit Criteria

In order to exit the incubator and become part of the Core/JPA/MOXy/DBWS components we must get consensus on the usage of this functionality and it must be able to function as the base dynamic solution in all components.

Design

The design of this feature involves several enhancements to the Core of EclipseLink (foundation component) as well as to JPA. The high level design involves:

  • XML Configuration: Extension of the eclipselink_orm_1_1.xsd
    • Add support for DYNAMIC access type in addition to FIELD and PROPERTY. Note: DYNAMIC can only be specified within this XML
    • Add support for @attribute-type on all mapping types
  • Dynamic Model
    • Public interfaces: org.eclipse.persistence.dynamic
    • Private implementation: org.eclipse.persistence.internal.dynamic
  • Metadata Processing Enhancements
    • Handling additional access type
    • Using attribute-type instead of reflective type checking on classes
    • Error handling for configuration limitations

XML Configuration

The change to the eclipselink_orm_1_1.xsd to enable dynamic persistence include:

  • Adding DYNAMIC Access Type. Since this implementation only supports DYNAMIC at the class level it must be set in the persistence unit defaults or individually on each entity. If DYNAMIC is specified for an entity then no attribute can specify a different access type or an exception will be thrown during validation of the metadata.
  • Adding of @access-type mappings to specify the Java type to be used
    • basic, id, version: specifies the 'basic' type to use
    • one-to-many, many-to-many, basic-collection, basic-map: In these mappings the attribute-type specifies the collection/map type to use
<?xml version="1.0"?>
<entity-mappings
	xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/orm"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0">
	<persistence-unit-metadata>
		<xml-mapping-metadata-complete />
		<exclude-default-mappings />
		<persistence-unit-defaults>
			<access>DYNAMIC</access> 
		</persistence-unit-defaults>
	</persistence-unit-metadata>
 
	<object-type-converter name="gender" object-type="java.lang.String"
		data-type="java.lang.String">
		<conversion-value object-value="Male" data-value="M" />
		<conversion-value object-value="Female" data-value="F" />
	</object-type-converter>
 
	<named-query name="Employee.findAll">
		<query>SELECT e FROM Employee e ORDER BY e.id</query>
	</named-query>
 
	<entity class="model.Address">
		<table name="D_ADDR" />
		<attributes>
			<id name="id" attribute-type="java.lang.Integer">
				<column name="ADDRESS_ID" />
				<generated-value />
			</id>
			<basic name="city" attribute-type="java.lang.String" />
			<basic name="country" attribute-type="java.lang.String" />
			<basic name="province" attribute-type="java.lang.String" />
			<basic name="postalCode" attribute-type="java.lang.String">
				<column name="P_CODE" />
			</basic>
			<basic name="street" attribute-type="java.lang.String" />
		</attributes>
	</entity>
	<entity class="model.PhoneNumber">
		<table name="D_PHONE" />
		<attributes>
			<id name="id" attribute-type="java.lang.Integer">
				<column name="EMP_ID" updatable="false" insertable="false" />
			</id>
			<id name="type" attribute-type="java.lang.String">
				<column updatable="false" />
			</id>
			<basic name="areaCode" attribute-type="java.lang.String">
				<column name="AREA_CODE" />
			</basic>
			<basic name="number" attribute-type="java.lang.String">
				<column name="P_NUMBER" />
			</basic>
			<many-to-one name="owner" target-entity="model.Employee" fetch="LAZY">
				<join-column name="EMP_ID" />
			</many-to-one>
		</attributes>
	</entity>
	<entity class="model.Employee">
		<table name="D_EMP" />
		<attributes>
			<id name="id" attribute-type="java.lang.Integer">
				<column name="EMP_ID" />
				<generated-value />
			</id>
			<basic name="firstName" attribute-type="java.lang.String">
				<column name="F_NAME" />
			</basic>
			<basic name="middleInitial" attribute-type="java.lang.String">
				<column name="INITIAL" />
			</basic>
			<basic name="lastName" attribute-type="java.lang.String">
				<column name="L_NAME" />
			</basic>
			<basic name="startTime" attribute-type="java.sql.Time" fetch="LAZY">
				<column name="START_TIME" />
			</basic>
			<basic name="endTime" attribute-type="java.sql.Time" fetch="LAZY">
				<column name="END_TIME" />
			</basic>
			<basic name="gender" attribute-type="java.lang.String">
				<column name="GENDER" />
				<convert>gender</convert>
			</basic>
			<basic name="salary" attribute-type="java.lang.Double" />
			<version name="version" attribute-type="java.lang.Long">
			</version>
			<many-to-one name="manager" fetch="LAZY" target-entity="model.Employee">
				<join-column name="MANAGER_ID" />
			</many-to-one>
			<one-to-many name="employees" mapped-by="manager"
				target-entity="model.Employee" attribute-type="java.util.ArrayList" />
			<one-to-many name="phoneNumbers" target-entity="model.PhoneNumber"
				mapped-by="owner" attribute-type="java.util.ArrayList">
				<cascade>
					<cascade-all />
				</cascade>
				<private-owned />
			</one-to-many>
			<one-to-one name="address" target-entity="model.Address"
				fetch="LAZY">
				<join-column name="ADDR_ID" />
				<cascade>
					<cascade-all />
				</cascade>
				<private-owned />
			</one-to-one>
		</attributes>
	</entity>
</entity-mappings>

Dynamic Model

Dynpersist.png

The dynamic model is the bulk of this feature. It provides dynamic classes to the application based on a common abstract base entity class which contains all of the EclipseLink specific support. In addition to the entity class itself there is also a simple Type-Property meta-model so that consuming applications can use the entities based on the mapping metadata.

Public Interfaces

The package org.eclipse.persistence.dynamic will provide a set of interfaces that consumers can use when interacting with dynamic entities.

  • DynamicEntity
  • DynamicEntityException
  • DynamicHelper
  • EntityType
  • EntityTypeBuilder

Internal Implementation

The package org.eclipse.persistence.internal.dynamic includes the concrete implementation of the base entity class as well as the meta-model bridge and the byte-code weaving support for creating the concrete dynamic entity classes on the fly.

  • BaseEntityImpl - the DynamicClassLoader generates sub-classes of BaseEntityImpl. This class defines the store for a dynamic object, an array of Objects that take the place of member fields for persistent attributes.
public abstract class BaseEntityImpl implements PersistenceEntity, Cloneable {
 
    public Object[] __fields; // NB - actually implementation hides __fields
...
}
  • DynamicEntityImpl
public class DynamicEntityImpl extends BaseEntityImpl  {
...
}
  • DynamicClassLoader
  • DynamicClassWriter
  • EntityTypeImpl
  • EntityTypeInstantiationPolicy
  • ValuesAccessor
Store Mechanism

EclipseLink persists an object by persisting an object's attribute; thus, EclipseLink must lookup the object's attribute. This lookup normally accesses the object's member fields for each persistent attribute; the access can be handled via reflection or via callbacks to designated get/set methods. To accommodate these different lookup mechanisms, an attribute's mapping owns an AttributeAccessor:

package org.eclipse.persistence.mappings;
 
public abstract class AttributeAccessor implements Cloneable, Serializable {
...
    /**
     * Return the attribute value from the object.
     */
    public abstract Object getAttributeValueFromObject(Object object) throws DescriptorException;
 
    /**
     * Set the attribute value into the object.
     */
    public abstract void setAttributeValueInObject(Object object, Object value) throws DescriptorException;
...
}

In the case of a dynamic object, actually member fields do not exist. Thus, the lookup must use a custom AttributeAccessor to access the store defined in BaseEntityImpl:

public class MyAttributeAccessor extends AttributeAccessor  {
 
    protected int fieldIdx;
 
    public MyAttributeAccessor(int fieldIdx) {
        this.fieldIdx = fieldIdx;
    }
 
    @Override
    public Object getAttributeValueFromObject(Object object) {
        BaseEntityImpl baseEntity = (BaseEntityImpl)object;
        return baseEntity.__fields[fieldIdx];
    }
 
    @Override
    public void setAttributeValueInObject(Object object, Object value) {
        BaseEntityImpl baseEntity = (BaseEntityImpl)object;
        baseEntity.__fields[fieldIdx] = value;
    }
}

(just an example, does not handle indirection or null/not-set semantics)

This custom accessor requires a correlation be made between an attribute and its position (fieldIdx) in the store.

Questions

  • Should the store mechanism be abstracted to an interface, as in SDO?
  • What about alternate store implementations, such as a HashMap?
  • What about path-style attributes/
    • MOXy - DOM XPath attributes: '/a/b/c'
    • ORM - POJO navigation: address.city.zipcode

Metadata Processing

The enhancements to the metadata processing include:

  • Reading the attribute-type into the XML meta-model and ensuring it is processed into the JPA meta-model
  • Handling of the 3 access types in the accessor model
  • Usage of the DynamicClassLoader wrapper when any class in a PU specifies DYNAMIC access
  • Creation of Dynamic class when needed
  • Avoid validation exceptions when dynamic types are used

Back to the top