Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.
EclipseLink/UserGuide/JPA/Advanced JPA Development/Extensible Entities
For current information, please see "JPA Entities and JAXB Beans Extensible" in the EclipseLink Solutions Guide: http://www.eclipse.org/eclipselink/documentation/latest/solutions/extensible.htm
EclipseLink JPA
EclipseLink | |
Website | |
Download | |
Community | |
Mailing List • Forums • IRC • mattermost | |
Issues | |
Open • Help Wanted • Bug Day | |
Contribute | |
Browse Source |
Key API
Examples
Extensible Entities
.
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,
- Configure the entity. See Configuring the Entity.
- Include flexible columns in the database table to store the additional data. See Designing the Schema.
- Specify extended mappings in the eclipselink-orm.xml file. See Providing Additional Mappings
- Configure persistence.xml. See Configuring persistence.xml.
Configuring the Entity
To configure the entity,
Annotate with @VirtualAccessMethods
Annotate the entity with @VirtualAccessMethods to specify that it is extensible and to define virtual properties.
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 and a java.lang.Object 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.
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) extensions.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 get-method="get" set-method="set"/>
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" access="VIRTUAL" attribute-type="String"> <column name="FLEX_COL1"/> <access-methods get-method="get" set-method="set"/> </basic>
Configuring Persistence Properties and the Data Repository
Configure persistence unit properties to indicate that the application should retrieve the flexible mappings from the eclipselink-orm.xml file. You can set persistence unit properties using persistence.xml or by setting properties on the EntityManagerFactory, as described in the following sections.
For more information about external mappings, see External Mappings.
Configuring persistence.xml
In persistence.xml, use the eclipselink.metadata-source property to use the default eclipselink-orm.xml file. Use the eclipselink.metadata-source.xml.url property to use a different file at the specified location. For example,
<property name="eclipselink.metadata-source" value="XML"/> <property name="eclipselink.metadata-source.xml.url" value="foo://bar"/>
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 includes a metadata repository implementation that 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 MetadataSource interface 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.jpa.metadata.MetadataSourceAdapter *org.eclipse.persistence.jpa.metadata.XMLMetadataSource
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.
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" access="VIRTUAL" 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); } ...
EclipseLink Home
JPA User Guide: Table of Contents, Search |
||
How to contribute to this guide... |