Skip to main content

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.

Jump to: navigation, search

Difference between revisions of "EclipseLink/UserGuide/sandbox/gelernter/Extensible Domain Models"

m (New page: <div style="float:right;width:300px"> __TOC__ </div> See [https://bugs.eclipse.org/bugs/show_bug.cgi?id=335601 Bug 335601] == Extensible Entity Types == The goal of this feature is to a...)
 
m
 
(46 intermediate revisions by 2 users not shown)
Line 1: Line 1:
<div style="float:right;width:300px">
+
{{EclipseLink_UserGuide
__TOC__
+
|info=y
</div>
+
|toc=n
 +
|eclipselink=y
 +
|eclipselinktype=JPA
 +
|api=y
 +
|apis=
 +
* [http://www.eclipse.org/eclipselink/api/latest/org/eclipse/persistence/annotations/VirtualAccessMethods.html @VirtualAccessMethods]
 +
* [[http://www.eclipse.org/eclipselink/api/latest/org/eclipse/persistence/jpa/JpaEntityManagerFactory.html#refreshMetadata(java.util.Map) RefreshMetadata()]]
 +
|examples=y
 +
|example=
 +
*[[EclipseLink/Examples/JPA/Extensibility|Extensible Entities]]
 +
}}
 +
===******SANDBOX VERSION******===
  
See [https://bugs.eclipse.org/bugs/show_bug.cgi?id=335601 Bug 335601]
+
''This topic is currently under review.''
  
== Extensible Entity Types ==
+
=Extensible Entities=
  
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 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.
+
{{EclipseLink_NewIn
 +
|version=2.3}}
 +
<br><br>.
  
* '''Static:''' extensions defined for the extensible entity types prior to the application starting.
+
Use the <tt>@VirtualAccessMethods</tt> annotation to specify that an entity is extensible. By using virtual properties in an extensible entity, you can specify mappings external to the entity. This allows you to modify the mappings without modifying the entity source file and without redeploying the entity's persistence unit.  
* '''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.
+
Extensible entities are useful in a multi-tenant (or Software-as-a-Service) environment where a shared, generic application can be used by multiple clients (tenants). Tenants have private access to their own data, as well as to data shared with other tenants. See also [[EclipseLink/UserGuide/JPA/Advanced_JPA_Development/Single-Table_Multi-Tenancy |Single-Table Multi-Tenancy]].
  
=== Requirements ===
+
Using extensible entities, you can:
  
# 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.
+
* Build an application where some mappings are common to all users and some mappings are user-specific.
#* FLEX Storage: Support storage of the extension properties within the same table using existing columns or dynamically added columns
+
* Add mappings to an application after it is made available to a customer (even post-deployment).
#* VALUES Storage: Support storage of the extension values within a secondary table where each row is a value for its associated entity
+
* Use the same <tt>EntityManagerFactory</tt> to work with data after mappings have changed.
# Share and Persist Extension Definitions
+
* Provide an additional source of metadata to be used by an application.
#* Extension definitions must be shared between all nodes of the same application deployed in the same cluster
+
#* 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
+
#* 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 create and support an extensible entity,
  
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.
+
#Configure the entity. See [[#Configuring the Entity| Configuring the Entity]].
 +
# Include flexible columns in the database table to store the additional data. See [[#Designing the Schema| Designing the Schema]].
 +
# Specify extended mappings in the <tt>eclipselink-orm.xml</tt> file. See [[#Providing Additional Mappings| Providing Additional Mappings]]
 +
# Configure persistence.xml. See [[#Configuring persistence.xml|Configuring persistence.xml]].
  
'''Actors'''
+
===Configuring the Entity===
 +
To configure the entity,
  
* Application Provider:
+
# [[#Annotate with @VirtualAccessMethods| Annotate with <tt>@VirtualAccessMethods</tt>]]
** Provides the base application.
+
#  [[#Add get() and set() Methods| Add <tt>get()</tt> and <tt>set()</tt> methods]]
** Has a strong understanding of the general domain
+
# [[#Add a Data Structure|Add a data structure]]
** 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.
+
====Annotate with @VirtualAccessMethods====
  
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.
+
Annotate the entity with <tt>@VirtualAccessMethods</tt> to specify that it is extensible and to define virtual properties.
 +
{{EclipseLink_AttributeTable
 +
|caption=@VirtualAccessMethods Attributes
 +
|content=
 +
<tr>
 +
<td><tt>get</tt></td>
 +
<td> The name of the getter method to use for the virtual property This method must take a single <tt>java.lang.String</tt> parameter and return a <tt>java.lang.Object</tt>. </td>
 +
<td><tt>get</tt></td>
 +
<td>Yes</td>
 +
</tr>
 +
<tr>
 +
<td>'''<tt>set</tt>'''</td>
 +
<td>The name of the setter method to use for the virtual property This method must take a <tt>java.lang.String</tt> parameter and return a <tt>java.lang.Object</tt> parameter.</td>
 +
<td><tt>set</tt></td>
 +
<td>Yes</td>
 +
</tr>
 +
}}
 +
<br>
  
== Application Development using Extensibility Feature Set ==
+
==== Add get() and set() Methods====
 +
Add <tt>get(String)</tt> and <tt>set(String, Object)</tt> methods to the entity.
  
When we have completed our extensibility work, it will be possible for a persistence unit to be designed in two phases.
+
The <tt>get()</tt> method returns a value by property name and the <tt>set()</tt> method stores a value by property name. The default names for these methods are <tt>get</tt> and <tt>set</tt>, and they can be overridden with the <tt>@VirtualAccessMethods</tt> annotation.  
  
=== Application Provider Phase ===
+
EclipseLink weaves these methods if weaving is enabled, which provides support for lazy loading, change tracking, fetch groups, and internal optimizations. You must use the the <tt>get(String)</tt> and <tt>set(String, Object)</tt> signatures, or else weaving will not work.
  
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.
+
{{EclipseLink_Note
 +
|note=Weaving is not supported when using virtual access methods with OneToOne mappings. If attempted, an exception will be thrown.
 +
}}
  
There will be some differences from typical persistence unit design, and they will potentially include:
+
==== Add a Data Structure ====
  
* Creation of additional tables in the schema
+
Add a data structure to store the extended attributes and values, that is, the virtual mappings. These can then be mapped to the database. See [[#Providing Additional Mappings| Providing Additional Mappings]].
* Adding additional columns to tables in the schema
+
* Providing additional privileges for schema alteration
+
* Specification of which Entities are extensibible
+
  
==== Example ====
+
A common way to store the virtual mappings is in a <tt>Map</tt> (as shown in the examples in this topic), but you can use other ways, as well. For example you could store the virtual mappings in a directory system.
  
The user designs a system for processing orders. They define the following Entities:
+
When using field-based access, annotate the data structure with <tt>@Transient</tt> so it cannot use it for another mapping. When using property-based access, <tt>@Transient'</tt> is unnecessary.
  
* Customer
+
====Example====
** @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 following example shows an entity that uses property access.  
  
The user designs a database schema to hold their EntitiesThe schema includes the following tables:
+
<source lang="java">
 +
@Entity
 +
  @VirtualAccessMethods
 +
  public class Customer{
 +
 +
    @Id
 +
    private int id;
 +
...
 +
   
 +
    @Transient
 +
    private Map<String, Object> extensions;
  
* CUSTOMER
+
    public <T> T get(String name) {
** INTEGER ID
+
        return (T) extentions.get(name);
** VARCHAR NAME
+
    }
* ORDER
+
** INTEGER ID
+
    public Object set(String name, Object value) {
** INTEGER ITEM_ID
+
        return extensions.put(name, value);
** INTEGER QUANTITY
+
    }
** INTEGER CUST_ID
+
</source>
* 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.
+
====Using XML====
 +
As an alternative to, or in addition to, using <tt>@VirtualAccessMethods</tt>, you can use the <tt><access></tt> and <tt><access-methods></tt> elements, for example:
  
The persistence unit is assembled and deployed.  Not only is an interface provided for actual application use, but a management interface is provided to allow extension of the persistence unit by the user.
+
<source lang="xml">
 +
<access>VIRTUAL</access>
 +
<access-methods set-method="get" get-method="set"/>
 +
</source>
  
=== Application User Phase ===
+
''''''REVIEWERS: // Is this correct? Is this adequate?//''''''
  
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.
+
===Designing the Schema===
  
==== Example ====
+
Provide database tables with extra columns for storing flexible mapping data. For example, the following Customer table includes two predefined columns, ID and NAME, and three flexible columns, FLEX_COL1, FLEX_COL2, FLEX_COL3:
  
The user of the order processing system above decides to extend it.  They wish to add an "address" attribute to Customer.
+
*CUSTOMER
 +
**INTEGER ID
 +
**VARCHAR NAME
 +
**VARCHAR FLEX_COL1
 +
**VARCHAR FLEX_COL2
 +
**VARCHAR FLEX_CO31
  
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.
+
You can then specify which of those flex columns should be used to persist an extended attribute, as described below, in [[#Providing Additional Mappings| Providing Additional Mappings]].
  
=== Additional Use Case Support ===
+
===Providing Additional Mappings===
  
The following use cases should also be addressed, but are not required to be supported without application shut-down.
+
To provide additional mappings, add the mappings to the <tt>eclipselink-orm.xml</tt> 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>
  
* Removal of mappings
+
'''''//REVIEWERS: Are there any limitations on the types of mappings that support flexible mappings? Also, do you think anything more should be said about what you have to do in eclipselink-orm.xml?//'''''
* Updating mappings
+
  
== Schema Design for Extensibility ==
+
* ''' I think that part of this will be addressed by anything we do to document using <access-methods> to specify Virtual mappings'''--[[User:Tom.ware.oracle.com|Tom.ware.oracle.com]] 16:04, 28 June 2011 (UTC)
 +
* ''' The XML file simply gets treated as another XML file in the list of XML files. As long as you obey all the rules related to what can be overridden, you can use any kind of mapping.  The challenge in using non-virtual mappings is how to have the data structures that support them make sense when the document is not there. e.g. if you're going to have an extension that uses an instance variable, for the instances of the application that don't use that extension file, how is that instance variable treated - JPA will likely try to use it for a mapping using its defaulting-rules''' --[[User:Tom.ware.oracle.com|Tom.ware.oracle.com]] 16:04, 28 June 2011 (UTC)
  
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.
+
===Configuring persistence.xml===
  
=== Flex Columns ===
+
Configure persistence unit properties in <tt>persistence.xml</tt> to indicate that the application should retrieve the flexible mappings from the <tt>eclipselink-orm.xml</tt> file,. For example:
  
See full desing document for [[EclipseLink/DesignDocs/340192|ER 340192: Provide Extensibility through Flexible Columns]].
+
'''''//REVIEWERS Did I get that intro right? Would different wording be better here? The design doc said “Use persistence unit properties to get your application to use the file."//'''''
  
Schema is designed to include preallocated columns that can be used to map additional dataIn this example, Customer table might look like this:
+
* '''Both persistence unit propeties and persistence.xml are legitimate use cases.  We should describe both.  persistence.xml allows either a default, or a single-user file that can be changedpersistence unit properties allow specification of the file at runtime and provides a more dynamic experience.'''--[[User:Tom.ware.oracle.com|Tom.ware.oracle.com]] 16:06, 28 June 2011 (UTC)
  
* CUSTOMER
+
<source lang="xml">
** INTEGER ID
+
<property name="eclipselink.metadata-source" value="XML"/>
** VARCHAR NAME
+
<property name="eclipselink.metadata-source.xml.url" value="foo://bar"/>
** VARCHAR FLEX_COL1
+
</source>
** 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.
+
'''''//REVIEWERS What more can be  said about these? See my related questions below, under [[#Configuring the EntityManagerFactory and the Metadata Repository | Configuring the EntityManagerFactory and the Metadata Repository]].//'''''
  
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 databaseFor 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.
+
* '''Maybe the two sections should go togetherWe could mention that by default we support using a file at a URL, but it is possible to also override how the repository works and then go into details.'''--[[User:Tom.ware.oracle.com|Tom.ware.oracle.com]] 17:20, 28 June 2011 (UTC)
  
* Label (e.g. address)
+
==Examples ==
* Type (e.g. java.lang.String)
+
The following examples illustrate variations on configuring extensible entities.
  
For instance, for the above example, an Application Provider might choose to define a single table containing the columns
+
====Example 1====
  
* CUSTOMER_METADATA
+
''Example 1''  illustrates the following:
** FLEX_COL1_LABEL
+
*Field access is used for non-extension fields.
** FLEX_COL1_TYPE
+
*Virtual access is used for extension fields, using defaults <tt>(get(String)</tt> and <tt>set(String, Object))</tt> .
** FLEX_COL2_LABEL
+
*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>@VirtualAccessMethods</tt>.
** FLEX_COL2_TYPE
+
*Extensions are mapped in a portable way by specifying <tt>@Transient</tt>.
** FLEX_COL3_LABEL
+
** FLEX_COL3_TYPE
+
  
Alternatively, they might choose to define 3 tables
+
Example 1
  
* CUSTOMER_COL1_METADATA
+
<source lang="java">
** FLEX_COL_LABEL
+
@Entity
** FLEX_COL_TYPE
+
  @VirtualAccessMethods
* CUSTOMER_COL2METADATA
+
  public class Address {
** FLEX_COL_LABEL
+
** FLEX_COL_TYPE
+
    @Id
* CUSTOMER_COL3METADATA
+
    private int id;
** FLEX_COL_LABEL
+
** FLEX_COL_TYPE
+
    @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>
  
Initially these tables would be quite sparse, but with the addition of Multi-tenant features they would become better populated
+
====Example 2====
  
=== Flex Tables ===
+
Example 2  illustrates the following:
  
Extensions use EclipseLink's secondary table feature. Additional tables are provided that can be used for extensions.
+
*Field access is used for non-extension fields.
 +
* The <tt>@VirtualAccessMethods</tt> annotation overrides methods to be used for getting and setting.
 +
*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>@VirtualAccessMethods</tt>.<br>
 +
*Extensions are mapped in a portable way by specifying <tt>@Transient</tt>.
 +
* The XML for extended mapping indicates which <tt>get()</tt> and <tt>set()</tt> method to use.
  
For instance, as above, CUSTOMER table is designed as follows:
+
Example 2
  
* CUSTOMER
+
<source lang="java">
** INTEGER ID
+
@Entity
** VARCHAR NAME
+
  @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);
 +
    }
 +
 +
...
  
An additional table is provided in the database
+
</source>
  
* CUSTOMER_EXT1
+
<source lang="xml">
** INTEGER CUST_ID
+
<basic name="name" attribute-type="String">
** VARCHAR FLEX_COL1
+
      <column name="FLEX_1"/>
** VARCHAR FLEX_COL2
+
      <access-methods get-method="getExtension" set-method="setExtension"/>
** VARCHAR FLEX_COL3
+
    </basic>
 +
</source>
  
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.
+
====Example 3====
  
As in the above example an arbitrary number of columns prefixed with "FLEX_COL" could be provided.
+
Example 3  illustrates the following:
  
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.
+
* Property access is used for non-extension fields.
 +
*Virtual access is used for extension fields, using defaults <tt>(get(String)</tt> and <tt>set(String, Object)) </tt>
 +
* The extensions are mapped in a portable way. <tt>@Transient</tt> is not required, because property access is used.
 +
* 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>@VirtualAccessMethods</tt>.
 +
<br>
  
This, also is supported by EclispeLink today, but we would want to consider adding DDL generation for this kind of scenario.
+
<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>
  
=== Custom Columns ===
+
<span id="this">
 +
== Configuring the EntityManagerFactory and the Metadata Repository ==
  
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
+
Extensions are added at bootstrap time through access to a metadata repository. The metadata repository is accessed through a class that provides methods to retrieve the metadata it holds. The current release supports XML repositories.
  
e.g. Customer table is designed as follows:
+
Specify the class to use and any configuration information for the metadata repository through persistence unit properties. The entity manager factory integrates additional mapping information from the metadata repository into the metadata it uses to bootstrap.  
  
* CUSTOMER
+
You can provide your own implementation of the class to access the metadata repository.
** INTEGER ID
+
Each metadata repository access class must specify an individual set of properties to use to connect to the repository.
** VARCHAR NAME
+
  
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.
+
You can subclass either of the following: *<tt>org.eclipse.persistence.internal.jpa.extensions.MetadataRepository</tt> *<tt>org.eclipse.persistence.internal.jpa.extensions.XMLMetadataRepository</tt>
  
This design requires some new capabilities in EclipseLink
+
====Example====
 +
In the following example, the properties that begin with <tt>com.foo</tt> are defined by the implementor.
  
# A way to detect existing metadata, preferably one that works with multiple DBs
+
<source lang="xml">
# A way to construct and issue ALTER TABLE statements
+
<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"/>
 +
</source>
  
=== Value Rows ===
+
====Refreshing the Metadata Repository====
  
Schema is designed with a table that represents a map structure for mappings. One Map table is used for all additional mappings.
+
If you change the metadata and you want an <tt>EntityManager</tt> based on the new metadata, you must call <tt>refreshMetadata()</tt> on the <tt>EntityManagerFactory</tt> to refresh the data. The next <tt>EntityManager</tt> will be based on the new metadata.
  
e.g. Customer table is designed as follows:
+
<tt>refreshMetadata</tt> takes a <tt>Map</tt> of properties, and that map of properties can be used to override the properties previously defined for the metadata-source.
  
* CUSTOMER
 
** INTEGER ID
 
** VARCHAR NAME
 
  
An addtional table is defined:
+
{{EclipseLink_JPA }}
 
+
* CUST_ATTR
+
** NAME
+
** VALUE
+
** CUST_ID
+
 
+
We would likely also need a way of determining what metadata to expect.  That could be represented in yet another table
+
 
+
* CUST_METADATA
+
** ATTR_NAME
+
** 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.
+
 
+
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.
+
 
+
This design requires some new capabilities in EclipseLink:
+
 
+
# A mapping that allows multiple attributes of the same entity to be mapped to the same map.
+
# 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 = <URL>
+
 
+
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 -> 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]]
+

Latest revision as of 12:46, 4 July 2011

EclipseLink JPA

Eclipselink-logo.gif
EclipseLink
Website
Download
Community
Mailing ListForumsIRCmattermost
Issues
OpenHelp WantedBug Day
Contribute
Browse Source

******SANDBOX VERSION******

This topic is currently under review.

Extensible Entities

EL NewIn.png New in version 2.3.



.

Use the @VirtualAccessMethods annotation to specify that an entity is extensible. By using virtual properties in an extensible entity, you can specify mappings external to the entity. This allows you to modify the mappings without modifying the entity source file and without redeploying the entity's persistence unit.

Extensible entities are useful in a multi-tenant (or Software-as-a-Service) environment where a shared, generic application can be used by multiple clients (tenants). Tenants have private access to their own data, as well as to data shared with other tenants. See also Single-Table Multi-Tenancy.

Using extensible entities, you can:

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

To create and support an extensible entity,

  1. Configure the entity. See Configuring the Entity.
  2. Include flexible columns in the database table to store the additional data. See Designing the Schema.
  3. Specify extended mappings in the eclipselink-orm.xml file. See Providing Additional Mappings
  4. Configure persistence.xml. See Configuring persistence.xml.

Configuring the Entity

To configure the entity,

  1. Annotate with @VirtualAccessMethods
  2. Add get() and set() methods
  3. Add a data structure

Annotate with @VirtualAccessMethods

Annotate the entity with @VirtualAccessMethods to specify that it is extensible and to define virtual properties.

@VirtualAccessMethods Attributes
Attribute Description Default Required?
get 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. get Yes
set The name of the setter method to use for the virtual property This method must take a java.lang.String parameter and return a java.lang.Object parameter. set Yes


Add get() and set() Methods

Add get(String) and set(String, Object) methods to the entity.

The get() method returns a value by property name and the set() method stores a value by property name. The default names for these methods are get and set, and they can be overridden with the @VirtualAccessMethods annotation.

EclipseLink weaves these methods if weaving is enabled, which provides support for lazy loading, change tracking, fetch groups, and internal optimizations. You must use the the get(String) and set(String, Object) signatures, or else weaving will not work.

Elug note icon.png

Note: Weaving is not supported when using virtual access methods with OneToOne mappings. If attempted, an exception will be thrown.

Add a Data Structure

Add a data structure to store the extended attributes and values, that is, the virtual mappings. These can then be mapped to the database. See Providing Additional Mappings.

A common way to store the virtual mappings is in a Map (as shown in the examples in this topic), but you can use other ways, as well. For example you could store the virtual mappings in a directory system.

When using field-based access, annotate the data structure with @Transient so it cannot use it for another mapping. When using property-based access, @Transient' is unnecessary.

Example

The following example shows an entity that uses property access.

@Entity
  @VirtualAccessMethods
  public class Customer{
 
    @Id
    private int id;
...
 
    @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);
    }

Using XML

As an alternative to, or in addition to, using @VirtualAccessMethods, you can use the <access> and <access-methods> elements, for example:

<access>VIRTUAL</access>
<access-methods set-method="get" get-method="set"/>

'REVIEWERS: // Is this correct? Is this adequate?//'

Designing the Schema

Provide database tables with extra columns for storing flexible mapping data. For example, the following Customer table includes two predefined columns, ID and NAME, and three flexible columns, FLEX_COL1, FLEX_COL2, FLEX_COL3:

  • CUSTOMER
    • INTEGER ID
    • VARCHAR NAME
    • VARCHAR FLEX_COL1
    • VARCHAR FLEX_COL2
    • VARCHAR FLEX_CO31

You can then specify which of those flex columns should be used to persist an extended attribute, as described below, in Providing Additional Mappings.

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>

//REVIEWERS: Are there any limitations on the types of mappings that support flexible mappings? Also, do you think anything more should be said about what you have to do in eclipselink-orm.xml?//

  • I think that part of this will be addressed by anything we do to document using <access-methods> to specify Virtual mappings--Tom.ware.oracle.com 16:04, 28 June 2011 (UTC)
  • The XML file simply gets treated as another XML file in the list of XML files. As long as you obey all the rules related to what can be overridden, you can use any kind of mapping. The challenge in using non-virtual mappings is how to have the data structures that support them make sense when the document is not there. e.g. if you're going to have an extension that uses an instance variable, for the instances of the application that don't use that extension file, how is that instance variable treated - JPA will likely try to use it for a mapping using its defaulting-rules --Tom.ware.oracle.com 16:04, 28 June 2011 (UTC)

Configuring persistence.xml

Configure persistence unit properties in persistence.xml to indicate that the application should retrieve the flexible mappings from the eclipselink-orm.xml file,. For example:

//REVIEWERS Did I get that intro right? Would different wording be better here? The design doc said “Use persistence unit properties to get your application to use the file."//

  • Both persistence unit propeties and persistence.xml are legitimate use cases. We should describe both. persistence.xml allows either a default, or a single-user file that can be changed. persistence unit properties allow specification of the file at runtime and provides a more dynamic experience.--Tom.ware.oracle.com 16:06, 28 June 2011 (UTC)
<property name="eclipselink.metadata-source" value="XML"/>
<property name="eclipselink.metadata-source.xml.url" value="foo://bar"/>

//REVIEWERS What more can be said about these? See my related questions below, under Configuring the EntityManagerFactory and the Metadata Repository.//

  • Maybe the two sections should go together. We could mention that by default we support using a file at a URL, but it is possible to also override how the repository works and then go into details.--Tom.ware.oracle.com 17:20, 28 June 2011 (UTC)

Examples

The following examples illustrate variations on configuring extensible entities.

Example 1

Example 1 illustrates the following:

  • Field access is used for non-extension fields.
  • Virtual access is used for extension fields, using defaults (get(String) and set(String, Object)) .
  • The get(String) and set(String, Object) methods will be woven, even if no mappings use them, because of the presence of @VirtualAccessMethods.
  • Extensions are mapped in a portable way by specifying @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

Example 2 illustrates the following:

  • Field access is used for non-extension fields.
  • The @VirtualAccessMethods annotation overrides methods to be used for getting and setting.
  • The get(String) and set(String, Object) methods will be woven, even if no mappings use them, because of the presence of @VirtualAccessMethods.
  • Extensions are mapped in a portable way by specifying @Transient.
  • 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

Example 3 illustrates the following:

  • Property access is used for non-extension fields.
  • Virtual access is used for extension fields, using defaults (get(String) and set(String, Object))
  • The extensions are mapped in a portable way. @Transient is not required, because property access is used.
  • The get(String) and set(String, Object) methods will be woven, even if no mappings use them, because of the presence of @VirtualAccessMethods.


@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);
    }
 
...

Configuring the EntityManagerFactory and the Metadata Repository

Extensions are added at bootstrap time through access to a metadata repository. The metadata repository is accessed through a class that provides methods to retrieve the metadata it holds. The current release supports XML repositories.

Specify the class to use and any configuration information for the metadata repository through persistence unit properties. The entity manager factory integrates additional mapping information from the metadata repository into the metadata it uses to bootstrap.

You can provide your own implementation of the class to access the metadata repository. Each metadata repository access class must specify an individual set of properties to use to connect to the repository.

You can subclass either of the following: *org.eclipse.persistence.internal.jpa.extensions.MetadataRepository *org.eclipse.persistence.internal.jpa.extensions.XMLMetadataRepository

Example

In the following example, the properties that begin with com.foo are defined by the implementor.

<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"/>

Refreshing the Metadata Repository

If you change the metadata and you want an EntityManager based on the new metadata, you must call refreshMetadata() on the EntityManagerFactory to refresh the data. The next EntityManager will be based on the new metadata.

refreshMetadata takes a Map of properties, and that map of properties can be used to override the properties previously defined for the metadata-source.


Eclipselink-logo.gif
Version: DRAFT
Other versions...

Back to the top