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/UserGuide/sandbox/gelernter/Extensible Domain Models"

m
m
Line 1: Line 1:
orig from http://wiki.eclipse.org/EclipseLink/DesignDocs/Extensibility
+
=Extensible Entities=
<div style="float: right; width: 300px;">
+
__TOC__
+
</div>
+
See [https://bugs.eclipse.org/bugs/show_bug.cgi?id=335601 Bug 335601]
+
  
== Extensible Entity Types  ==
+
This feature is new in EclipseLink 2.3.
  
The goal of this feature is to allow a user to start with a persistence unit containing entity types that are configured to support extensions'''''////what exactly is an extension in this context?////''''' and add mappings to the entities in that persistence unit without the need for redeployment of the persistence unit. These extended entities will then read and write the extended values when set and allow the application to execute queries involving these properties in the criteria.  
+
You can define and use extensible entities where mappings can be added without redeploying the persistence unit. The entity stores extended attributes in a map. Values from this map are mapped to the database using an eclipselink-orm.xml mapping file. These extended mappings are always? stored and managed externally, which allows you to define new mappings while the application is running.
  
*'''Static:''' extensions defined for the extensible entity types prior to the application starting.
+
==Flex Columns Extension ==
*'''Dynamic:''' are extensions defined while the application is operating.
+
  
The set of features we build for Extensible Entity Types will provide a foundation that can be build on by users that want to build multi-tenant applications can specify tenant specific extensions.  
+
Entities can be extended using flexible columns. You can use such extended entities to do the following:
  
=== Requirements  ===
+
o * Build an application where some mappings are common to all users and some mappings are user-specific.
 +
o * Add mappings to an application after it is made available to a customer (even post-deployment).
 +
o * Use the same EntityManagerFactory to work with data after mappings have changed
 +
o * Provide an additional source of metadata to be used by an application.
  
#Provide a mechanism so that developers can develop and map a persistent entity type that supports static and/or dynamic extensions where the extensions are additional properties on the entity type that are read and written with the entity.
+
This feature is useful in a Software-as-a-Service (SaaS) environment where a shared, generic application can be customized by users to make use of data that is particular to their domain.
#*FLEX Storage: Support storage of the extension properties within the same table using existing columns or dynamically added columns
+
#*VALUES Storage: Support storage of the extension values within a secondary table where each row is a value for its associated entity '''''////How does that get specified?////'''''
+
#Share and Persist Extension Definitions
+
#*Extension definitions must be shared between all nodes of the same application deployed in the same cluster '''''////what is a node of an application? the instance running on a clustered server instance?////'''''
+
#*Extension definitions must be persisted so that future instances of the application can leverage the extensions
+
#**XML: Capture in ORM mapping file/fragment
+
#**Relational: Store the definitions within the application's schema
+
#*Extension definitions can be defined statically or dynamically using a provided API
+
#Expose extensions through JPA queries and metamodel&nbsp; ''The JPA metamodel (and underlying native metamodel) will expose the extensions ''
+
#**An Api will also be provided to allow static and extension properties to be differentiated
+
#*Applications can leverage extensions in queries using native expressions, JPQL, and String based criteria queries
+
  
=== High Level Use Case  ===
+
To extend the entities and take advantage of flexible columns, you must do the following:
 +
o Annotate entities to allow flexible mappings.
 +
o Provide facilities to store the additional data.
 +
o more
  
This feature set is designed to support a scenario where the bulk of an application is developed by one party and provided to a second party who can make certain types of modifications to that application.
+
===Annotating Entities===
 +
Use @VirtualAccessMethods to annotate entities to allow flexible mappings, for example:
  
'''Actors'''
+
<source lang="java">
 +
@Entity
 +
  @VirtualAccessMethods
 +
  public class Customer{
 +
 +
...
 +
 +
    @Transient
 +
    private Map<String, Object> extensions;
 +
 +
    public <T> T get(String name) {
 +
        return (T) extentions.get(name);
 +
    }
 +
 +
    public Object set(String name, Object value) {
 +
        return extensions.put(name, value);
 +
    }
 +
</source>
  
*Application Provider:
+
===Designing the Schema===
**Provides the base application.
+
**Has a strong understanding of the general domain
+
**Has access to Java Development Skills
+
**Has access to database development skills
+
*Application User
+
**Uses the base application
+
**Strong understanding of general domain
+
**Needs to extend the amount of data stored in the general domain
+
**No Java Development Skills needed
+
**No DBA skills needed
+
  
The Application Provider uses their understanding of the general problem domain to design an application that covers the majority of the functionality that is required by application users. That application contains a persistence component that maps the data required for the application. That persistence component is mapped in a JPA persistence unit. The application provider provides, not only the application, but a management interface that allows the Application user to extend the data in the persistence unit.  
+
When you design your schema, provide enough extra columns in the tables to accommodate the number of flexible mappings to allow. For example, the following table has three predefined columns and three columns for adding mappings later.  
  
The Application user makes use of the application provided by the Application Provider. When the Application User wants to expand the data set that is stored by the application beyond what the Application Provider has provided, they make use of the management interface to add to the mappings in the persistence unit provided by the Application Provider using the management interface. They are not required to write any Java code, or SQL and are not required to redeploy the application on the application server.
+
For example, consider the following Customer table that includes three flexible columns, FLEX_COL1, FLEX_COL2, FLEX_COL3:
  
== Application Development using Extensibility Feature Set  ==
+
CUSTOMER
 +
INTEGER ID
 +
VARCHAR NAME
 +
VARCHAR FLEX_COL1
 +
VARCHAR FLEX_COL2
 +
VARCHAR FLEX_CO31
 +
===Providing Additional Mappings===
  
When we have completed our extensibility work, it will be possible for a persistence unit to be designed in two phases.  
+
To provide additional mappings, add the mappings to the eclipselink-orm.xml file, for example:
 +
 +
<source lang="xml">
 +
<basic name="idNumber" attribute-type="String">
 +
  <column name="FLEX_COL1"/>
 +
  <access-methods get-method="get" set-method="set"/>
 +
</basic>
 +
</source>
  
=== Application Provider Phase  ===
+
Are there any limitations on the types of mappings that support this? I think I saw this in the original design doc.
 +
===Configure persistence.xml===
  
The first phase will be nearly identical to the way a persistence unit is designed in EclipseLink today. In this phase of design the base schema, mappings, and properties of the persistence unit are defined. These parts of the persistence unit will exist for all running applications.  
+
Configure persistence unit properties in persistence.xml to indicate that the application should use the file. For example:
  
There will be some differences from typical persistence unit design, and they will potentially include:  
+
“use the file”? what wording would be better here? orig said “Use persistence unit properties to get your application to use the file:
  
*Creation of additional tables in the schema
+
<source lang="xml">
*Adding additional columns to tables in the schema
+
<property name="eclipselink.metadata-source" value="XML"/>
*Providing additional privileges for schema alteration
+
<property name="eclipselink.metadata-source.xml.url" value="foo://bar"/>
*Specification of which Entities are extensibible
+
</source>
  
==== Example  ====
+
Say more about what you are doing here. It looks like it is just defining a name/value pair. I think this is standard stuff: you just define this and then use it somewhere else.
  
The user designs a system for processing orders. They define the following Entities:
 
  
*Customer
+
Deleted Requirements section. Come back here at the end and see if you’ve handled it all.
**@Id id
+
**@Basic name
+
**@OneToMany Orders
+
*Order
+
**@Id id
+
**@OneToOne Item
+
**@Basic quantity
+
**@ManyToOne customer
+
*Item
+
**@Id id
+
**@Basic name
+
**@Basic unitPrice
+
  
The mappings are made using JPA annotations, orm.xml or a combination, in the same way as other JPA applications. A persistence.xml is provided to configure the persistence unit.
 
  
The user designs a database schema to hold their Entities. The schema includes the following tables:
+
==Configuration ==
  
*CUSTOMER
+
===Metadata Configuration===
**INTEGER ID
+
**VARCHAR NAME
+
*ORDER
+
**INTEGER ID
+
**INTEGER ITEM_ID
+
**INTEGER QUANTITY
+
**INTEGER CUST_ID
+
*ITEM
+
**INTEGER ID
+
**VARCHAR NAME
+
**FLOAT PRICE
+
  
Depending on their extensibility strategy, they may define other columns in those tables or other tables. This will be discussed more in Schema Design Section below.  
+
Extensions are supported using the VIRTUAL access type. Virtual Access type allows properties to be set through a getter that takes an attribute name as an argument and a setter that takes an argument name and a value as an argument.  
  
The persistence unit is assembled and deployed. Not only is an interface provided for actual application use, but a management interface '''''////I assume this means an API for (runtime?) cusomizaton? - how is this surfaced? in something like JDev?////''''' is provided to allow extension of the persistence unit by the user.
 
  
=== Application User Phase  ===
+
These annotations will have the same effect as using the eclipselink-orm.xml construct <access-methods> at a class level. They default which methods are used by virtual mappings.
  
The second phase of design involves customization of the persistence unit. With the persistence unit already deployed, the user doing customization adds mappings to the persistence unit. Because the schema has been designed to accomodate the new mappings, EclipseLink can either make use of predefined tables and columns to store the data for the mappings, or alter the schema to add new columns to existing tables.  
+
EclipseLink weaves these methods if weaving is enabled. This weaving will provide equivalent functionality to weaving for PROPERTY and FIELD access. (e.g. change tracking, fetch groups, etc) An Equivalent <extensible> tag will be added at the class level in XML.  
  
==== Example  ====
+
====Examples ====
  
The user of the order processing system above decides to extend it. They wish to add an "address" attribute to Customer.
+
Example 1 illustrates the following:
 +
o Field Access for non extension fields
 +
o Virtual Access for extension fields uses defaults (get(String), set(String, Object))
 +
o The get(String) and set(String, Object) method will be woven, even if no mappings use them, because of the presence of @Extensible << I don’t see @Extensible
 +
o Extensions are mapped in a portable way by specifying  @Transient  << I don’t see @Transient
  
The user calls the management interface. The management interface calls EclipseLink API to add a mapping to the metadata. EclipseLink adjusts the users underlying session to make use of the new mapping without the need for redeployment on the application server. Future retreivals using Customer make use of the new "address" mapping.
+
Example 1
  
=== Additional Use Case Support ===
+
<source lang="java">
 +
@Entity
 +
  @VirtualAccessMethods
 +
  public class Address {
 +
   
 +
    @Id
 +
    private int id;
 +
 +
    @Transient
 +
    private Map<String, Object> extensions;
 +
 +
    public int getId(){
 +
        return id;
 +
    }
 +
 +
    public <T> T get(String name) {
 +
        return (T) extentions.get(name);
 +
    }
 +
 +
    public Object set(String name, Object value) {
 +
        return extensions.put(name, value);
 +
    }
 +
 +
...
 +
</source>
 +
Example 2 illustrates the following:
  
The following use cases should also be addressed, but are not required to be supported without application shut-down.  
+
o Field Access for non extension fields
 +
o Extensions mapped in a portable way - @Transient
 +
o The @VirtualAccessMethods annotation overrides methods to be used for getting and setting.
 +
o The getExtension(String) and setExtension(String, Object) methods will be woven, even if no mappings use them, because of the presence of @Extensible << I don’t see @Extensible
 +
o The XML for extended mapping indicates which get and set method to use .
  
*Removal of mappings
 
*Updating mappings
 
  
== Schema Design for Extensibility  ==
+
Example 2
  
As mentioned above, one of the main differences in how an extensible application is designed as opposed to a typical application is in how the database schema is designed. There are several strategies that can be employed.  
+
<source lang="java">
 +
@Entity
 +
  @VirtualAccessMethods(get="getExtension", set="setExtension")
 +
  public class Address {
 +
 +
    @Id
 +
    private int id;
 +
 +
    @Transient
 +
    private Map<String, Object> extensions;
 +
 +
    public int getId(){
 +
        return id;
 +
    }
 +
 +
    public <T> T getExtension(String name) {
 +
        return (T) extensions.get(name);
 +
    }
 +
 +
    public Object setExtension(String name, Object value) {
 +
        return extensions.put(name, value);
 +
    }
 +
 +
...
  
=== Flex Columns  ===
+
</source>
  
See full desing document for [[EclipseLink/DesignDocs/340192|ER 340192: Provide Extensibility through Flexible Columns]].
+
<source lang="xml">
 +
<basic name="name" attribute-type="String">
 +
      <column name="FLEX_1"/>
 +
      <access-methods get-method="getExtension" set-method="setExtension"/>
 +
    </basic>
 +
</source>
  
Schema is designed to include preallocated columns that can be used to map additional data. In this example, Customer table might look like this:  
+
Example 3 illustrates the following:  
  
*CUSTOMER
+
o * Property Access for non extension fields
**INTEGER ID
+
o * Virtual Access for extension fields uses defaults <tt>(get(String), set(String, Object))</tt>
**VARCHAR NAME
+
o * The extensions are mapped in a portable way; no @Transient is required because of Property access
**VARCHAR FLEX_COL1
+
o * The <tt>get(String)</tt> and <tt>set(String, Object) </tt> methods will be woven, even if no mappings use them, because of the presence of <tt>@Extensible</tt>
**VARCHAR FLEX_COL2
+
**VARCHAR FLEX_CO31
+
  
An arbitrary number of the columns prefixed with "FLEX_COL" could be defined. A user mapping the "address" property of Customer could simply map it to FLEX_COL1.  
+
<source lang="java">
 +
@Entity
 +
  @VirtualAccessMethods
 +
  public class Address {
 +
 +
    private int id;
 +
 +
    private Map<String, Object> extensions;
 +
 +
    @Id
 +
    public int getId(){
 +
        return id;
 +
    }
 +
 +
    public <T> T get(String name) {
 +
        return (T) extensions.get(name);
 +
    }
 +
 +
    public Object set(String name, Object value) {
 +
        return extensions.put(name, value);
 +
    }
 +
 +
...
 +
</source>
  
In order to support multiple data types, all flex-columns are defined as strings. Metadata about the expected type of the field is defined in the database. For each flex column, there is a set of entries in a database table. The table and column names storing these items could be Application provider determined and therefore they could all exist on the same table or they could exist on different tables.
+
===EntityManagerFactory and Metadata Repository ===
  
*Label (e.g. address)  
+
Extensions will be added at bootstrap time through access to a metadata repository. A Metadata Repository will accessed through a class that provides methods to retrieve the metadata it holds.  
*Type (e.g. java.lang.String)
+
The user will specifiy the class to use and any configuration information for the metadata repository through persistence unit properties. As an EntityManagerFactory bootstraps, if metadata repository information is provided, the EMF will check the metadata repository for additional mapping information and integrate it into the metadata it uses to bootstrap.
 +
EclipseLink will initially ship with the capability of connecting to two types of metadata repository.
 +
XML (high priority) - information about extensions is stored in XML
 +
Database Table (medium priority) - information about extensions is stored in a database table
 +
Additionally, the user will be able to provide an implementation of the class that access the metadata repository.  
 +
Each metadata repository access class will specify an individual set of properties to use to connect to the repository
 +
Examples
 +
XML File
 +
 +
<source lang="xml">
 +
<property name="eclipselink.metadata-source" value="XML"/>
 +
  <property name="eclipselink.metadata-source.xml.url" value="foo://bar"/>
 +
</source>
  
For instance, for the above example, an Application Provider might choose to define a single table containing the columns
+
User-Specified
  
*CUSTOMER_METADATA
+
<source lang="xml">
**FLEX_COL1_LABEL
+
<property name="eclipselink.metadata-source" value="com.foo.MetadataRepository"/>
**FLEX_COL1_TYPE
+
  <property name="com.foo.MetadataRepository.location" value="foo://bar"/>
**FLEX_COL2_LABEL
+
  <property name="com.foo.MetadataRepository.extra-data" value="foo-bar"/>
**FLEX_COL2_TYPE
+
</source>
**FLEX_COL3_LABEL
+
**FLEX_COL3_TYPE
+
  
Alternatively, they might choose to define 3 tables
+
Note: The implementer of <tt>com.foo.MetadataRepository</tt> will be free to choose the properties that their implementation requires.
 +
==Design ==
 +
====Configuration ====
 +
Please use the examples above as a guideline configuration XML and annotations. Note that the configuration options to allow a mapping to use VIRTUAL access have been available in EclipseLink for several releases. We will be using those configuration options as they exist and any changes to those will be handled as bugs rather than through this design document.
 +
====Weaving ====
 +
The intial VIRTUAL access feature did not include weaving of the get and set methods. As part of the extensions feature will will add weaving of get and set methods that use virtual access. The initial implementation will not support OneToOne mappings and throw an exception at Transformer construction time if weaving is requested for a VIRTUAL mapping that is OneToOne.
  
*CUSTOMER_COL1_METADATA
+
Get method
**FLEX_COL_LABEL
+
**FLEX_COL_TYPE
+
*CUSTOMER_COL2METADATA
+
**FLEX_COL_LABEL
+
**FLEX_COL_TYPE
+
*CUSTOMER_COL3METADATA
+
**FLEX_COL_LABEL
+
**FLEX_COL_TYPE
+
  
Initially these tables would be quite sparse, but with the addition of Multi-tenant features they would become better populated
+
Original
 +
<source lang="java">
 +
public <T> T get(String name) {
 +
        return (T) getExtensions().get(name);
 +
    }
 +
</source>
 +
Weaved
 +
<source lang="java">
 +
public Object get(String name)
 +
    {
 +
        _persistence_checkFetched(name);
 +
        return getExtensions().get(name);
 +
    }
 +
</source>
  
=== Flex Tables  ===
+
Set method
 +
Original
 +
<source lang="java">
 +
public Object set(String name, Object value)
 +
    {
 +
        return getExtensions().put(name, value);
  
Extensions use EclipseLink's secondary table feature. Additional tables are provided that can be used for extensions.  
+
    }
 +
</source>
 +
Weaved
 +
<source lang="java">
 +
public Object set(String name, Object value)
 +
    {
 +
        Object obj = null;
 +
        if(_persistence_listener != null)
 +
        {
 +
            obj = get(name);
 +
        } else
 +
        {
 +
            _persistence_checkFetchedForSet(name);
 +
        }
 +
        _persistence_propertyChange(name, obj, value);
 +
        return getExtensions().put(name, value);
 +
    }
 +
</source>
  
For instance, as above, CUSTOMER table is designed as follows:
+
To allow weaving, RelationalDescriptor will have a list virtual methods added. This list will be used at transformer-construction time to allow EclipseLink to know which methods it should weave.
  
*CUSTOMER
+
<source lang="java">
**INTEGER ID
+
**VARCHAR NAME
+
  
An additional table is provided in the database
+
/** The methods that are used by virtual attributes as getter methods and setter methods. 
 +
    * These will be used by our weaver to properly weave those methods
 +
    **/
 +
    protected List<VirtualAccessMethods> virtualMethods = null;
 +
</source>
  
*CUSTOMER_EXT1
+
===EntityManagerFactory ===
**INTEGER CUST_ID
+
Bootstrap
**VARCHAR FLEX_COL1
+
EntityManagerFactory bootstrapping occurs withing EntityManagerSetupImpl. In the predeploy method, there is code that obtains the orm.xml files that contain metadata. At that point, the metadata repository will be consulted. It will provide additional metadata information in the same format as is obtained from the orm.xml file.
**VARCHAR FLEX_COL2
+
Refresh
**VARCHAR FLEX_COL3
+
A mechanism will be provided that allows a user to tell a Metadata repository to refresh. That mechanism will take two forms.
 +
A direct refresh API call
 +
A RemoteCommandManager command that causes all subscribed EntityManagerFactories to refresh themselves as described above.
 +
Refresh will be supported by adding an additional proxy to our EntityManagerFactory archtecture.
 +
Current: EntityManagerFactoryImpl -> ServerSession
 +
New: EntityManagerFactoryWrapper implements EntityManagerFactory -> EntityManagerFactoryImpl -> ServerSession
 +
EntityManagerFactoryWrapper will implement:
 +
public void refreshMetadata()
 +
In both cases, a live EntityManager holds a reference to EntityManagerFactoryImpl
 +
When a call is made to refreshMetadata(), EntityManagerFactoryWrapper will bootstrap a new EntityManagerFactoryImpl and use it as the basis for any new EntityManagers. The old EntityManagerFactoryImpl will continue to be available until the last EntityManager is no longer used, at which point we will rely on garbage collection to clean it up.
 +
Metadata Source
 +
An implementation of MetadataSoruce will access metadata for extensions. Metadata is accessed in the form of an eclipselink-orm.xml file.
 +
package org.eclipse.persistence.jpa.metadata;
 +
 +
public interface MetadataSource{
 +
 +
    /**
 +
    * ADVANCED:
 +
    * In most cases, this method should not be overridden.  The implementation of
 +
    * this method uses getEntityMappingsReader() to obtain a reader that will
 +
    * that reads a stream in the eclipselink-orm.xml format
 +
    * Advanced implementations of MetadataRepository have to option of overriding
 +
    * this method.
 +
    * @return XMLEntityMappings which are then merged in using existing metadata
 +
    * processing at bootstrap time when creating an entityManager
 +
    */
 +
    public XMLEntityMappings getMetadata(Properties properties, ClassLoader loader)
 +
Additionally, an adapter class will be provided that implements MetadataSource containing stubbed out methods. Customers will be encouraged to implement a MetadataSource by subclasing the adapter class rather than directly implementing the interface. This strategy will allow them to tranaparently absorb any new versions of the interface in new EclipseLink versions.
 +
XMLMetadataSource
 +
The first implementation of MetadataSource provided by EclipseLink will access a simple XML File.
 +
It will provide an implementation of getMetadata(properties, classlaoder) that uses the property "eclipselink.metadata-repository.xml-file.url", specified like this:
  
When the first extended mapping is added, CUSTOMER_EXT1 is added as a secondary table for Customer. The address field, can then be mapped to FLEX_COL1.  
+
<source lang="xml">
 +
<property name="eclipselink.metadata-source.xml.url" value="foo://bar"/>
  
As in the above example an arbitrary number of columns prefixed with "FLEX_COL" could be provided.
+
To create an input stream on the eclipselink-orm.xml file at URL: "foo://bar"/" and build an XMLEntityMappings using our existing EclipseLink ORM parsing code.  
 
+
Writing to a metadata repository
In this case, in addition, each extended application could, optionally, provide their own extension table, and as a result, applications would not be forced to share unused data with other appliciations using the same base application, nor would metadata have to be held about types seach each EXT table could have the appropriate types.  
+
In the initial implementation writing to the metadata repository will be left up to the user.  
 
+
Remote Command Manager
This, also is supported by EclispeLink today, but we would want to consider adding DDL generation for this kind of scenario.
+
A Command for RemoteCommandManager will be implemented that triggers a refreshMetadata() call on all subscribed EntityManagerFactories.  
 
+
Design is in progress and will be added as it becomes available
=== Custom Columns  ===
+
Future Enhancements
 
+
Weaving of OneToOne mappings
Schema is designed with only the tables and columns used by the predefined mappings. A mechanism is build into EclipseLink to detect the metadata from the tables and alter those tables to add columns for the mappings that are added as extensions. EclipseLink must somehow have persmission to call ALTER TABLE
+
Implement support for weaving of non-basic VIRTUAL mappings
 
+
Handle OneToOneMappings
e.g. Customer table is designed as follows:
+
Handle indirection
 
+
Allow metadata to be updated with an in-memory structure
*CUSTOMER
+
From GlassFish team:
**INTEGER ID
+
Programmatic API to call into EclipseLink to "push" the extension definitions.  
**VARCHAR NAME
+
The data exchanged between the caller and EclipseLink via API should be in a format that just refers to extension information and not a generic data structure.  
 
+
The API call should be on an EclipseLink artifact that does not trigger deploy. That is it should be on an artifact at EMF level.  
When the user adds an "address" mapping for Customer, EclipseLink inspects the metadata from the CUSTOMER table and calls an ALTER TABLE statement to add an ADDRESS field to the CUSTOMER table.  
+
Database Metadata Source
 
+
Provide an implementation of Metadata Source that reads from a database.
This design requires some new capabilities in EclipseLink
+
Writing to a metadata Source
 
+
Provide API to write to the XMLFile and DatabaseMetadata Sources.  
#A way to detect existing metadata, preferably one that works with multiple DBs
+
Configuration of muliple getter and setter methods
#A way to construct and issue ALTER TABLE statements
+
Allow annotations and xml to specify a list of methods for VIRTUAL mappings to weave
 
+
Appendices
=== Value Rows  ===
+
Appendix 1: Alternatives Considered
 
+
http://wiki.eclipse.org/EclipseLink/DesignDocs/335601
Schema is designed with a table that represents a map structure for mappings. One Map table is used for all additional mappings.  
+
Appendix 2: Annotations
 
+
<source lang="java">
e.g. Customer table is designed as follows:
+
/**
 
+
  * Specifies that this class contains virtual attributes.
*CUSTOMER
+
  * This annotation is used in an EclipseLink-specific way to define
**INTEGER ID
+
  * access methods used by mappings with accessType=VIRTUAL.
**VARCHAR NAME
+
  * The xml-equivalent is the <access-methods> tag
 
+
  */
An addtional table is defined:
+
@Documented
 
+
@Target(TYPE)
*CUST_ATTR
+
@Retention(RUNTIME)
**NAME
+
public @interface VirtualAccessMethods {
**VALUE
+
   
**CUST_ID
+
    /**
 
+
    * (Optional) The name of the getter method to use for the virtual property
We would likely also need a way of determining what metadata to expect. That could be represented in yet another table
+
    * This method must take a single java.lang.String parameter and return a java.lang.Object.
 
+
    * If setMethod is specified, getMethod must be specified
*CUST_METADATA
+
    */
**ATTR_NAME
+
    String get() default "get";
**ATTR_TYPE
+
 
+
    /**
If an "address" field is added to CUSTOMER, a new row could be added to the CUST_METADATA table with ATTR_NAME=address and and ATTR_TYPE=VARCHAR. Any value for that attribute will be added to the CUST_ATTR table with the NAME="address", the CUST_ID=the foreign key to customer and the value.  
+
    * (Optional) The name of the setter method to use for the virtual property
 
+
    * This method must take a java.lang.String parameter and a java.lang.Object parameter.
Queries for customer involve querying the CUST_METADATA table for a list of extensions, then the CUSTOMER table for the predefined mappings and finally either multiple queries or multiple joins the the CUST_ATTR table for the extended mappings.
+
    * If getMethod is specified, setMethod must be specified
 
+
    */
This design requires some new capabilities in EclipseLink:
+
    String set() default "set";
 
+
}
#A mapping that allows multiple attributes of the same entity to be mapped to the same map.
+
</source>
#A way to store and retrieve metadata the metadata we are storing
+
#We would want to consider a DDL generation for the new tables
+
 
+
== Extensible Descriptors  ==
+
 
+
The next component to an extendable application is the ability to actually add new mappings. New mappings must be persistent. i.e. They must be stored in a persistent media.
+
 
+
=== File-based-extension  ===
+
 
+
It would be possible to enable extension by allowing the user to provide an additional orm.xml or eclipselink-orm.xml file that contains the additional mappings. That file could be allowed through a persistence unit property passed in when either the EntityManagerFactory or the EntityManager was created.  
+
 
+
e.g. eclipselink.extended-mappings = &lt;URL&gt;
+
 
+
At processing-time this URL would be examined for an orm.xml file containing additional mappings and those additional mappings would be appended to existing descriptors.
+
 
+
A great deal of this strategy is already supported in EclipseLink. To allow this method of extension we would have to implement:
+
 
+
#Support for the property that points to the XML file.  
+
#A mechanism to add mappings at either EntityManagerFactory deployment time, or EntityManager creation time
+
 
+
=== Persistent Extensions  ===
+
 
+
Extended metadata information is stored in the database and retreived immediately after login. A table structure like time one in the Value Rows Schema section could be used to store the metadata.  
+
 
+
To allow this method of extension we would have to implement
+
 
+
#A mechanism of storing and getting metadata information from a database table
+
#A mechanism to extend mappings in a descriptor based on the information in that table
+
#We might want a way to DDL generate that table
+
 
+
== Entity classes  ==
+
 
+
Given a descriptor for dynamic mappings, the entity it refers to needs fields that can store the actual data. We need to keep in mind the following two restrictions:
+
 
+
#Extension must occur without redeploy. This means any weaving we do, must occur before we know exactly what mappings will be added
+
#We want to avoid requiring the user to have any references to EclipseLink-specific classes in their domain classes.
+
 
+
=== Predefined attributes  ===
+
 
+
Just as the database could hold predefined FLEX_COLUMNS, the Entities could hold predefined fields to hold the data. These fields would have a one-to-one coorespondance to the FLEX_COLUMNS. The weakness of this type of column is that we would have to predefine the Object type for it. As a result, it would have to hold a composite data structure that could read a String value and apply conversion based on the expected type stored in the database.
+
 
+
=== Attributes as a Map  ===
+
 
+
The most obvious way to store a list of dynamically added attributes is in a Map. The map could be provided in the Entity class by the Application Provider and described in the metadata for the Entity, or it could be woven into the class for any Entity that allows dynamic mappings.
+
 
+
==== Name - Value Map  ====
+
 
+
Here the key of the map is the name of the attribute and the value is its value.  
+
 
+
==== Metadata Holder - Value Map  ====
+
 
+
Here the key of the map is a data structure that holds metadata about the attribute including its name, its data type and any other required information.  
+
 
+
== API  ==
+
 
+
The majority of the API documentation should come in design documents for the individual features we choose from this document. This section will capture any general comments about the API
+
 
+
=== Descriptor Extension ===
+
 
+
For Persistent Extensions above, we could provide a sample persistence unit that maps to our metadata tables and the API for changes would simply be the JPA API. Application providers could choose to use that persistence unit, or write there own.  
+
 
+
Descriptors would need to be extended to know about the data structure of the tables and could even use that same persistence unit to read extended mappings.
+
 
+
=== Metadata Access ===
+
 
+
We should provide an API to access any metadata for the extensions. This API should maintain the EclipseLink policy of not requiring the user to use any EclipseLink-specific classes in thier implementation.
+
 
+
Perhaps the user could furnish an interface.
+
 
+
=== Persistence Unit Properties ===
+
 
+
== Extensible Application Architectures ==
+
 
+
Note: This section starts to cross the line between our Extensibility feature set and any feature set we might develop for Multi-tenancy. It is included since we will have to keep in mind how applications are deployed when we figure out the limitations for adding mappings.
+
 
+
=== Deployment per client ===
+
 
+
In this type of deployment each client has their own application deployment. Database tables could be shared or in separate schemas.
+
 
+
=== EntityManagerFactory per Client ===
+
 
+
In this type of deployment, the same persistence unit is used by multiple clients. This persistence unit can be used by the same application, or by separate applications. Database schema can be shared or specified by each client.
+
 
+
=== EntityManager per Client  ===
+
 
+
In this type of deployment, extensions are chosen at EntityManager creation time. An EntityManager contains constructs that let it make use of extended mappings that are not accessible to its EntityManagerFactory. Database schema can be shared or specified at EntityManager creation time.  
+
 
+
== Initial Feature Set And Limitations  ==
+
 
+
''This section will be updated after some discussion. At the moment, I am using it as a whiteboard and therefore it changes frequently.''
+
 
+
The document above specifies a number of options for providing extensible functionality.
+
 
+
'''Feature List''' - initial proposal
+
 
+
*DirectToMapMapping
+
**Similar to the way attributes are mapped in Dynamic JPA
+
**preexisting map in the Entity
+
**map holds propertyName -&gt; propertyValue mapping
+
*Extensions to DirectToMapMapping to allow the DB side of the mapping to be a key-value structure as well
+
*Extensible descriptor that can have mappings added post-deployment
+
*Extensible descriptor that retreives additional mappings from a given table in the database and adds them
+
*Weaving of map-structure for DirectToMapMapping into objects with extensible descriptors
+
*Metadata storage and retreival strategy for mappings that are stored in the database that exposes the metadata to the customer
+
*DDL Generation for Value Rows
+
*Support for Removal and alteration of extended mappings
+
*Support for detection of current schema and calls to Alter table to ammend schema to fit mappings
+
*DDL Generation for Flex Columns and Flex Tables
+
 
+
'''Limitations'''
+
 
+
*Initial Target for all features: Deployment-per-Client Application architecture
+
*Initial feature list will only consider adding Basic mappings
+
 
+
== Multi-tenancy  ==
+
 
+
Multi-tenancy is not covered by this document, but the features defined in this document could be used in Multi-tenant systems.
+
 
+
The following document predates this document and discusses some of the multi-tenant options in EclipseLink. It will eventually be altered to discuss any multi-tenant features we hope to implement.
+
 
+
*[[EclipseLink/DesignDocs/MultiTenantFeatures|Multi Tenant Features: Description of features available and being considered for Multi Tenant APPs]]
+

Revision as of 09:26, 24 June 2011

Extensible Entities

This feature is new in EclipseLink 2.3.

You can define and use extensible entities where mappings can be added without redeploying the persistence unit. The entity stores extended attributes in a map. Values from this map are mapped to the database using an eclipselink-orm.xml mapping file. These extended mappings are always? stored and managed externally, which allows you to define new mappings while the application is running.

Flex Columns Extension

Entities can be extended using flexible columns. You can use such extended entities to do the following:

o * Build an application where some mappings are common to all users and some mappings are user-specific. o * Add mappings to an application after it is made available to a customer (even post-deployment). o * Use the same EntityManagerFactory to work with data after mappings have changed o * Provide an additional source of metadata to be used by an application.

This feature is useful in a Software-as-a-Service (SaaS) environment where a shared, generic application can be customized by users to make use of data that is particular to their domain.

To extend the entities and take advantage of flexible columns, you must do the following: o Annotate entities to allow flexible mappings. o Provide facilities to store the additional data. o more

Annotating Entities

Use @VirtualAccessMethods to annotate entities to allow flexible mappings, for example:

@Entity
  @VirtualAccessMethods
  public class Customer{
 
...
 
    @Transient
    private Map<String, Object> extensions;
 
    public <T> T get(String name) {
        return (T) extentions.get(name);
    }
 
    public Object set(String name, Object value) {
        return extensions.put(name, value);
    }

Designing the Schema

When you design your schema, provide enough extra columns in the tables to accommodate the number of flexible mappings to allow. For example, the following table has three predefined columns and three columns for adding mappings later.

For example, consider the following Customer table that includes three flexible columns, FLEX_COL1, FLEX_COL2, FLEX_COL3:

CUSTOMER INTEGER ID VARCHAR NAME VARCHAR FLEX_COL1 VARCHAR FLEX_COL2 VARCHAR FLEX_CO31

Providing Additional Mappings

To provide additional mappings, add the mappings to the eclipselink-orm.xml file, for example:

<basic name="idNumber" attribute-type="String">
  <column name="FLEX_COL1"/>
  <access-methods get-method="get" set-method="set"/>
</basic>

Are there any limitations on the types of mappings that support this? I think I saw this in the original design doc.

Configure persistence.xml

Configure persistence unit properties in persistence.xml to indicate that the application should use the file. For example:

“use the file”? what wording would be better here? orig said “Use persistence unit properties to get your application to use the file:”

<property name="eclipselink.metadata-source" value="XML"/>
<property name="eclipselink.metadata-source.xml.url" value="foo://bar"/>

Say more about what you are doing here. It looks like it is just defining a name/value pair. I think this is standard stuff: you just define this and then use it somewhere else.


Deleted Requirements section. Come back here at the end and see if you’ve handled it all.


Configuration

Metadata Configuration

Extensions are supported using the VIRTUAL access type. Virtual Access type allows properties to be set through a getter that takes an attribute name as an argument and a setter that takes an argument name and a value as an argument.


These annotations will have the same effect as using the eclipselink-orm.xml construct <access-methods> at a class level. They default which methods are used by virtual mappings.

EclipseLink weaves these methods if weaving is enabled. This weaving will provide equivalent functionality to weaving for PROPERTY and FIELD access. (e.g. change tracking, fetch groups, etc) An Equivalent <extensible> tag will be added at the class level in XML.

Examples

Example 1 illustrates the following: o Field Access for non extension fields o Virtual Access for extension fields uses defaults (get(String), set(String, Object)) o The get(String) and set(String, Object) method will be woven, even if no mappings use them, because of the presence of @Extensible << I don’t see @Extensible o Extensions are mapped in a portable way by specifying @Transient << I don’t see @Transient

Example 1

@Entity
  @VirtualAccessMethods
  public class Address {
 
    @Id
    private int id;
 
    @Transient
    private Map<String, Object> extensions;
 
    public int getId(){
        return id;
    }
 
    public <T> T get(String name) {
        return (T) extentions.get(name);
    }
 
    public Object set(String name, Object value) {
        return extensions.put(name, value);
    }
 
...

Example 2 illustrates the following:

o Field Access for non extension fields o Extensions mapped in a portable way - @Transient o The @VirtualAccessMethods annotation overrides methods to be used for getting and setting. o The getExtension(String) and setExtension(String, Object) methods will be woven, even if no mappings use them, because of the presence of @Extensible << I don’t see @Extensible o The XML for extended mapping indicates which get and set method to use .


Example 2

@Entity
  @VirtualAccessMethods(get="getExtension", set="setExtension")
  public class Address {
 
    @Id
    private int id;
 
    @Transient
    private Map<String, Object> extensions;
 
    public int getId(){
        return id;
    }
 
    public <T> T getExtension(String name) {
        return (T) extensions.get(name);
    }
 
    public Object setExtension(String name, Object value) {
        return extensions.put(name, value);
    }
 
...
<basic name="name" attribute-type="String">
      <column name="FLEX_1"/>
      <access-methods get-method="getExtension" set-method="setExtension"/>
    </basic>

Example 3 illustrates the following:

o * Property Access for non extension fields o * Virtual Access for extension fields uses defaults (get(String), set(String, Object)) o * The extensions are mapped in a portable way; no @Transient is required because of Property access o * The get(String) and set(String, Object) methods will be woven, even if no mappings use them, because of the presence of @Extensible

@Entity
  @VirtualAccessMethods
  public class Address {
 
    private int id;
 
    private Map<String, Object> extensions;
 
    @Id
    public int getId(){
        return id;
    }
 
    public <T> T get(String name) {
        return (T) extensions.get(name);
    }
 
    public Object set(String name, Object value) {
        return extensions.put(name, value);
    }
 
...

EntityManagerFactory and Metadata Repository

Extensions will be added at bootstrap time through access to a metadata repository. A Metadata Repository will accessed through a class that provides methods to retrieve the metadata it holds. The user will specifiy the class to use and any configuration information for the metadata repository through persistence unit properties. As an EntityManagerFactory bootstraps, if metadata repository information is provided, the EMF will check the metadata repository for additional mapping information and integrate it into the metadata it uses to bootstrap. EclipseLink will initially ship with the capability of connecting to two types of metadata repository. XML (high priority) - information about extensions is stored in XML Database Table (medium priority) - information about extensions is stored in a database table Additionally, the user will be able to provide an implementation of the class that access the metadata repository. Each metadata repository access class will specify an individual set of properties to use to connect to the repository Examples XML File

<property name="eclipselink.metadata-source" value="XML"/>
  <property name="eclipselink.metadata-source.xml.url" value="foo://bar"/>

User-Specified

<property name="eclipselink.metadata-source" value="com.foo.MetadataRepository"/>
  <property name="com.foo.MetadataRepository.location" value="foo://bar"/>
  <property name="com.foo.MetadataRepository.extra-data" value="foo-bar"/>

Note: The implementer of com.foo.MetadataRepository will be free to choose the properties that their implementation requires.

Design

Configuration

Please use the examples above as a guideline configuration XML and annotations. Note that the configuration options to allow a mapping to use VIRTUAL access have been available in EclipseLink for several releases. We will be using those configuration options as they exist and any changes to those will be handled as bugs rather than through this design document.

Weaving

The intial VIRTUAL access feature did not include weaving of the get and set methods. As part of the extensions feature will will add weaving of get and set methods that use virtual access. The initial implementation will not support OneToOne mappings and throw an exception at Transformer construction time if weaving is requested for a VIRTUAL mapping that is OneToOne.

Get method

Original

public <T> T get(String name) {
        return (T) getExtensions().get(name);
    }

Weaved

public Object get(String name)
    {
        _persistence_checkFetched(name);
        return getExtensions().get(name);
    }

Set method Original

public Object set(String name, Object value)
    {
        return getExtensions().put(name, value);
 
    }

Weaved

public Object set(String name, Object value)
    {
        Object obj = null;
        if(_persistence_listener != null)
        {
            obj = get(name);
        } else
        {
            _persistence_checkFetchedForSet(name);
        }
        _persistence_propertyChange(name, obj, value);
        return getExtensions().put(name, value);
    }

To allow weaving, RelationalDescriptor will have a list virtual methods added. This list will be used at transformer-construction time to allow EclipseLink to know which methods it should weave.

/** The methods that are used by virtual attributes as getter methods and setter methods.  
     * These will be used by our weaver to properly weave those methods 
     **/
    protected List<VirtualAccessMethods> virtualMethods = null;

EntityManagerFactory

Bootstrap EntityManagerFactory bootstrapping occurs withing EntityManagerSetupImpl. In the predeploy method, there is code that obtains the orm.xml files that contain metadata. At that point, the metadata repository will be consulted. It will provide additional metadata information in the same format as is obtained from the orm.xml file. Refresh A mechanism will be provided that allows a user to tell a Metadata repository to refresh. That mechanism will take two forms. A direct refresh API call A RemoteCommandManager command that causes all subscribed EntityManagerFactories to refresh themselves as described above. Refresh will be supported by adding an additional proxy to our EntityManagerFactory archtecture. Current: EntityManagerFactoryImpl -> ServerSession New: EntityManagerFactoryWrapper implements EntityManagerFactory -> EntityManagerFactoryImpl -> ServerSession EntityManagerFactoryWrapper will implement: public void refreshMetadata() In both cases, a live EntityManager holds a reference to EntityManagerFactoryImpl When a call is made to refreshMetadata(), EntityManagerFactoryWrapper will bootstrap a new EntityManagerFactoryImpl and use it as the basis for any new EntityManagers. The old EntityManagerFactoryImpl will continue to be available until the last EntityManager is no longer used, at which point we will rely on garbage collection to clean it up. Metadata Source An implementation of MetadataSoruce will access metadata for extensions. Metadata is accessed in the form of an eclipselink-orm.xml file. package org.eclipse.persistence.jpa.metadata;

public interface MetadataSource{

   /**
    * ADVANCED:
    * In most cases, this method should not be overridden.  The implementation of
    * this method uses getEntityMappingsReader() to obtain a reader that will
    * that reads a stream in the eclipselink-orm.xml format
    * Advanced implementations of MetadataRepository have to option of overriding
    * this method.
    * @return XMLEntityMappings which are then merged in using existing metadata 
    * processing at bootstrap time when creating an entityManager
    */
   public XMLEntityMappings getMetadata(Properties properties, ClassLoader loader)

Additionally, an adapter class will be provided that implements MetadataSource containing stubbed out methods. Customers will be encouraged to implement a MetadataSource by subclasing the adapter class rather than directly implementing the interface. This strategy will allow them to tranaparently absorb any new versions of the interface in new EclipseLink versions. XMLMetadataSource The first implementation of MetadataSource provided by EclipseLink will access a simple XML File. It will provide an implementation of getMetadata(properties, classlaoder) that uses the property "eclipselink.metadata-repository.xml-file.url", specified like this:

<property name="eclipselink.metadata-source.xml.url" value="foo://bar"/>
 
To create an input stream on the eclipselink-orm.xml file at URL: "foo://bar"/" and build an XMLEntityMappings using our existing EclipseLink ORM parsing code. 
Writing to a metadata repository 
In the initial implementation writing to the metadata repository will be left up to the user. 
Remote Command Manager 
A Command for RemoteCommandManager will be implemented that triggers a refreshMetadata() call on all subscribed EntityManagerFactories. 
Design is in progress and will be added as it becomes available 
Future Enhancements 
Weaving of OneToOne mappings 
Implement support for weaving of non-basic VIRTUAL mappings 
Handle OneToOneMappings 
Handle indirection 
Allow metadata to be updated with an in-memory structure 
From GlassFish team: 
Programmatic API to call into EclipseLink to "push" the extension definitions. 
The data exchanged between the caller and EclipseLink via API should be in a format that just refers to extension information and not a generic data structure. 
The API call should be on an EclipseLink artifact that does not trigger deploy. That is it should be on an artifact at EMF level. 
Database Metadata Source 
Provide an implementation of Metadata Source that reads from a database. 
Writing to a metadata Source 
Provide API to write to the XMLFile and DatabaseMetadata Sources. 
Configuration of muliple getter and setter methods 
Allow annotations and xml to specify a list of methods for VIRTUAL mappings to weave 
Appendices 
Appendix 1: Alternatives Considered 
http://wiki.eclipse.org/EclipseLink/DesignDocs/335601 
Appendix 2: Annotations 
<source lang="java">
/**
 * Specifies that this class contains virtual attributes.
 * This annotation is used in an EclipseLink-specific way to define
 * access methods used by mappings with accessType=VIRTUAL.
 * The xml-equivalent is the <access-methods> tag
 */
@Documented
@Target(TYPE)
@Retention(RUNTIME)
public @interface VirtualAccessMethods {
 
    /**
     * (Optional) The name of the getter method to use for the virtual property
     * This method must take a single java.lang.String parameter and return a java.lang.Object.
     * If setMethod is specified, getMethod must be specified
     */
    String get() default "get";
 
    /**
     * (Optional) The name of the setter method to use for the virtual property
     * This method must take a java.lang.String parameter and a java.lang.Object parameter.
     * If getMethod is specified, setMethod must be specified
     */
    String set() default "set";
}

Back to the top