Difference between revisions of "EclipseLink/Examples/JPA/Dynamic"

From Eclipsepedia

Jump to: navigation, search
(Define a new Dynamic Entity Type)
 
(17 intermediate revisions by 5 users not shown)
Line 1: Line 1:
<div style="border:1px solid #999999;background-color:#ffffff;align:center">
+
= How to use Dynamic EclipseLink JPA =
<table border="0" cellpadding="5" align="center"><tr><td width="20">[[image:Catnicon.gif]]</td><td>This page is under construction. See bug [http://bugs.eclipse.org/225026 225026] for more information</td></tr></table>
+
</div>
+
  
= How to use Dynamic JPA persistence =
+
EclipseLink JPA supports '''Dynamic Persistence''' allowing users to map relational tables to virtual classes which are created dynamically at runtime. This support was added in [[EclipseLink/Release/2.1.0|EclipseLink 2.1.0]]. This example illustrates dynamic persistence using the [http://www.eclipse.org/eclipselink/api/2.1/org/eclipse/persistence/dynamic/DynamicEntity.html DynamicEntity] interface for all of the persistent types.
  
This how-to illustrates how EclipseLink can be extended to deliver all of its JPA functionality in a dynamic scenario where no Java class exists for the entity and optionally where no XML is provided. This allows the EclipseLink JPA solution to be used in more dynamic environments where either the mode is unknown at development time or the application is simply dynamic in nature.
+
== Configuration ==
  
== Dynamic Entity Extension ==
+
With dynamic persistence the mappings and implied entity types can be defined using API within the application code or they can be specified using the eclipselink-orm.xml mapping file.
  
The extension to EclipseLink to enable dynamic entities and the associated helpers involves the following classes.
+
=== eclipselink-orm.xml ===
  
TBD
+
There are a couple of key differences versus a standard JPA mapping file:
 +
* access type of '''VIRTUAL''' is used
 +
* attribute-type must be provided
  
== Example ==
+
<source lang="xml">
 +
<?xml version="1.0" encoding="UTF-8"?>
 +
<entity-mappings version="2.1"
 +
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/orm"
 +
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  
The dynamic entity extension is currently illustrated through test cases in the example.
+
<package>example.jpa.dynamic.model.employee</package>
  
 +
<object-type-converter name="gender-converter"
 +
object-type="String" data-type="String">
 +
<conversion-value object-value="Male" data-value="M" />
 +
<conversion-value object-value="Female" data-value="F" />
 +
</object-type-converter>
  
=== Creating and Entity on the Fly ===
+
<named-query name="Employee.findAll">
 +
<query>SELECT e FROM Employee e ORDER BY e.lastName, e.firstName</query>
 +
<hint name="eclipselink.query-results-cache" value="True" />
 +
</named-query>
  
In this example (<pre>testing.PUWithoutXML#createSimpleDynamicEntity()</pre>) an EntityManagerFactory is bootstrapped without a persistence.xml and then a dynamic entity type is defined. After this the dynamic entity can be used through standard JPA queries and transactions.
+
<named-query name="Employee.findMin">
 +
<query>SELECT e FROM Employee e WHERE e.id IN (SELECT MIN(ee.id) FROM
 +
Employee ee)</query>
 +
</named-query>
  
==== Creating the EMF ====
+
<entity class="Address" access="VIRTUAL">
 +
<attributes>
 +
<id name="id" attribute-type="Integer">
 +
<column name="ADDRESS_ID" />
 +
<generated-value strategy="SEQUENCE" generator="address-generator" />
 +
<sequence-generator name="address-generator"
 +
sequence-name="ADDR_SEQ" />
 +
</id>
 +
<basic name="city" attribute-type="String" />
 +
<basic name="country" attribute-type="String" />
 +
<basic name="province" attribute-type="String" />
 +
<basic name="postalCode" attribute-type="String">
 +
<column name="P_CODE" />
 +
</basic>
 +
<basic name="street" attribute-type="String" />
 +
</attributes>
 +
</entity>
  
<source lang=java>
+
<entity class="PhoneNumber" access="VIRTUAL">
DynamicClassLoader loader = new DynamicClassLoader(null,
+
<table name="PHONE" />
DynamicEntity.class);
+
<attributes>
SEPersistenceUnitInfo puInfo = new SEPersistenceUnitInfo();
+
<id name="id" attribute-type="Integer">
puInfo.setClassLoader(loader);
+
<column name="PHONE_NUM_ID" />
puInfo.setPersistenceUnitName("dynamic");
+
<generated-value strategy="SEQUENCE" />
// This root URL config should go away when bug 225321  is fixed
+
</id>
puInfo
+
<basic name="type" attribute-type="String">
.setPersistenceUnitRootUrl(new URL(
+
<column updatable="false" />
"file:/C:/Eclipse/EclipseLink/trunk/examples/org.eclipse.persistence.example.jpa.dynamic/classes/"));
+
</basic>
 +
<basic name="areaCode" attribute-type="String">
 +
<column name="AREA_CODE" />
 +
</basic>
 +
<basic name="number" attribute-type="String">
 +
<column name="P_NUMBER" />
 +
</basic>
 +
<many-to-one name="owner" fetch="EAGER" target-entity="Employee">
 +
<join-column name="EMP_ID" />
 +
</many-to-one>
 +
</attributes>
 +
</entity>
  
EntityManagerSetupImpl setup = new EntityManagerSetupImpl();
+
<entity class="Employee" access="VIRTUAL">
setup.predeploy(puInfo, null);
+
<secondary-table name="SALARY" />
// This replaces Persistence.createEntityManagerFactory
+
<attributes>
EntityManagerFactory emf = new EntityManagerFactoryImpl(setup,
+
<id name="id" attribute-type="Integer">
EclipseLinkJPATest.getEMFProperties());
+
<column name="EMP_ID" />
</source>
+
<generated-value strategy="SEQUENCE" generator="employee-generator" />
 +
<sequence-generator name="employee-generator"
 +
sequence-name="EMP_SEQ" />
 +
</id>
 +
<basic name="firstName" attribute-type="String">
 +
<column name="F_NAME" />
 +
</basic>
 +
<basic name="lastName" attribute-type="String">
 +
<column name="L_NAME" />
 +
</basic>
 +
<basic name="startTime" attribute-type="String">
 +
<column name="START_TIME" />
 +
</basic>
 +
<basic name="endTime" attribute-type="String">
 +
<column name="END_TIME" />
 +
</basic>
 +
<basic name="gender" attribute-type="String">
 +
<column name="GENDER" />
 +
<convert>gender-converter</convert>
 +
</basic>
 +
<basic name="salary" attribute-type="Integer">
 +
<column table="SALARY" />
 +
</basic>
  
==== Define a new Dynamic Entity Type ====
+
<version name="version" attribute-type="Long" />
  
The EntityTypeFactory class provides some useful methods to simplify creating a dynamic type avoiding some of the complexities.
+
<many-to-one name="manager" fetch="EAGER" optional="true"
 +
target-entity="Employee">
 +
<join-column name="MANAGER_ID" />
 +
</many-to-one>
  
<source lang=java>
+
<one-to-many name="managedEmployees" mapped-by="manager"
EntityType employeeType = EntityTypeFactory.create("Employee",
+
target-entity="Employee" attribute-type="java.util.List" />
"DynamicEntity$Employee", "DYNAMIC_EMP", loader);
+
EntityTypeFactory.addBasicProperty(employeeType, "id", "EMP_ID",
+
Integer.class, true);
+
EntityTypeFactory.addBasicProperty(employeeType, "firstName", "F_NAME",
+
String.class, false);
+
EntityTypeFactory.addBasicProperty(employeeType, "lastName", "L_NAME",
+
String.class, false);
+
EntityTypeFactory.addBasicProperty(employeeType, "salary", "SALARY",
+
double.class, false);
+
EntityTypeFactory.addToPersistenceUnit(emf, employeeType);
+
</source>
+
  
==== Creating the Tables ====
+
<one-to-many name="phoneNumbers" mapped-by="owner"
 +
target-entity="PhoneNumber" attribute-type="java.util.List">
 +
<cascade>
 +
<cascade-all />
 +
</cascade>
 +
<private-owned />
 +
</one-to-many>
  
EclipseLink's SchemaManager can be used to drop/create the tables. Use with caution.
+
<one-to-one name="address" fetch="EAGER" target-entity="Address">
 +
<join-column name="ADDR_ID" />
 +
<cascade>
 +
<cascade-all />
 +
</cascade>
 +
<private-owned />
 +
</one-to-one>
  
<source lang=java>
+
<many-to-many name="projects" target-entity="Project"
new SchemaManager(JpaHelper.getServerSession(emf))
+
attribute-type="java.util.List">
.replaceDefaultTables();
+
<join-table name="PROJ_EMP">
 +
<join-column name="EMP_ID" />
 +
<inverse-join-column name="PROJ_ID" />
 +
</join-table>
 +
</many-to-many>
 +
 
 +
<element-collection name="responsibilities"
 +
attribute-type="java.util.List" target-class="String">
 +
<column name="RESPON_DESC" />
 +
<collection-table name="RESPONS" />
 +
</element-collection>
 +
 
 +
<embedded name="period" attribute-type="EmploymentPeriod" />
 +
 
 +
</attributes>
 +
</entity>
 +
 
 +
<entity class="Project" access="VIRTUAL">
 +
<inheritance strategy="JOINED" />
 +
<discriminator-column name="PROJ_TYPE"
 +
discriminator-type="CHAR" />
 +
<attributes>
 +
<id name="id" attribute-type="Integer">
 +
<column name="PROJ_ID" />
 +
<generated-value strategy="SEQUENCE" generator="project-generator" />
 +
<sequence-generator name="project-generator"
 +
sequence-name="PROJ_SEQ" />
 +
</id>
 +
<basic name="description" attribute-type="String">
 +
<column name="DESCRIP" />
 +
</basic>
 +
<basic name="name" attribute-type="String">
 +
<column name="PROJ_NAME" />
 +
</basic>
 +
<version name="version" attribute-type="Long" />
 +
<many-to-one name="teamLeader" fetch="EAGER"
 +
target-entity="Employee">
 +
<join-column name="LEADER_ID" />
 +
</many-to-one>
 +
</attributes>
 +
</entity>
 +
 
 +
<entity class="SmallProject" parent-class="Project" access="VIRTUAL">
 +
<table name="PROJECT" />
 +
<discriminator-value>S</discriminator-value>
 +
</entity>
 +
 
 +
<entity class="LargeProject" parent-class="Project" access="VIRTUAL">
 +
<table name="LPROJECT" />
 +
<discriminator-value>L</discriminator-value>
 +
<attributes>
 +
<basic name="budget" attribute-type="Double" />
 +
<basic name="milestone" attribute-type="java.util.Calendar">
 +
<temporal>TIMESTAMP</temporal>
 +
</basic>
 +
</attributes>
 +
</entity>
 +
 
 +
<embeddable class="EmploymentPeriod" access="VIRTUAL">
 +
<attributes>
 +
<basic name="startDate" attribute-type="java.util.Date">
 +
<column name="START_DATE" />
 +
<temporal>DATE</temporal>
 +
</basic>
 +
<basic name="endDate" attribute-type="java.util.Date">
 +
<column name="END_DATE" />
 +
<temporal>DATE</temporal>
 +
</basic>
 +
</attributes>
 +
</embeddable>
 +
 
 +
</entity-mappings>
 
</source>
 
</source>
  
The SQL generated when run against and Oracle database appears as:
+
=== Dynamic Configuration using API ===
  
<pre>
+
Alternatively developers can construct dynamic persistence units using API. A complete example of the same employee demo is available
[EPS Fine]: Connection(1741620)--DROP TABLE DYNAMIC_EMP
+
[http://dev.eclipse.org/svnroot/rt/org.eclipse.persistence/branches/2.1/trunk/examples/jpa.employee/eclipselink.example.jpa.employee.dynamic/src/example/EmployeeDynamicMappings.java here].
[EPS Fine]: Connection(1741620)--CREATE TABLE DYNAMIC_EMP (ID NUMBER(10) NOT NULL, F_NAME VARCHAR2(255) NULL, L_NAME VARCHAR2(255) NULL, PRIMARY KEY (ID))
+
</pre>
+
  
The schema definition types can be customized if required.
+
The basic building blocks of constructing dynamic entities using API are:
  
==== Using the Dynamic Entity ====
+
==== 1. Create the Dynamic Class ====
  
After the dynamic entity type is properly defined and initialized you can use it. The only real tricks to using a dynamic type versus a static type is that:
+
The dynamic class is an ASM generated subclass of Dynamic entity and it is created with a DynamicClassLoader as:
* you must use the ''DynamicEntity'' API to access and manipulate your data.
+
* any time you need the Java class you must use the generated class. This generated class is held within the EclipseLink mapping metadata but is more easily accessible using the ''EntityType''. The ''DynamicJpaHelper'' can also be used to lookup the type.
+
* creating a new instance requires the usage of a factory method on ''EntityType''
+
  
<source lang=java>
+
<source lang="java">
EntityManager em = emf.createEntityManager();
+
DynamicClassLoader dcl = new DynamicClassLoader(currentLoader);
  
DynamicEntity emp1 = (DynamicEntity) em.find(employeeType
+
Class<?> employeeClass = dcl.createDynamicClass(packagePrefix + "Employee");
.getJavaClass(), 1);
+
</source>
Assert.assertNull(emp1);
+
  
em.getTransaction().begin();
+
==== 2. Create Dynamic Type ====
  
emp1 = employeeType.newInstance();
+
Using a JPADynamicTypeBuilder  as a factory a new type can be constructed
emp1.set("id", 1);
+
emp1.set("firstName", "John");
+
emp1.set("lastName", "Doe");
+
  
em.persist(emp1);
+
<source lang="java">
em.getTransaction().commit();
+
JPADynamicTypeBuilder address = new JPADynamicTypeBuilder(addressClass, null, "D_ADDRESS");
 +
</source>
  
em.clear();
+
==== 3. Add the Mappings ====
  
emp1 = (DynamicEntity) em.find(employeeType.getJavaClass(), 1);
+
Finally the mappings are added:
  
 +
<source lang="java">
 +
address.setPrimaryKeyFields("ADDR_ID");
  
emp1 = (DynamicEntity) em
+
address.addDirectMapping("id", int.class, "ADDR_ID");
.createQuery(
+
address.addDirectMapping("street", String.class, "STREET");
"SELECT e FROM Employee e WHERE e.firstName = :FNAME AND e.lastName = :LNAME")
+
address.addDirectMapping("city", String.class, "CITY");
.setParameter("FNAME", "John").setParameter("LNAME", "Doe")
+
address.addDirectMapping("province", String.class, "PROV");
.getSingleResult();
+
address.addDirectMapping("postalCode", String.class, "P_CODE");
 +
address.addDirectMapping("country", String.class, "COUNTRY");
 +
 
 +
address.configureSequencing("ADDR_SEQ", "ADDR_ID");
 
</source>
 
</source>
  
=== Dynamic Entities using ORM.XML ===
+
==== Relationships ====
  
'''This does not yet work - see TO DO list'''
+
In addition to these basic steps you will also need to re-use the types when defining relationships between them. To create the Employee to address 1:1 both types are required and used as:
  
== Try It Out ==
+
<source lang="java">
 +
OneToOneMapping addressMapping = employee.addOneToOneMapping("address", address.getType(), "ADDR_ID");
 +
addressMapping.setCascadeAll(true);
 +
addressMapping.setIsPrivateOwned(true);
 +
</source>
  
The example is being developed with the EclipseLink Example component at:
+
== Usage Examples ==
  
[http://dev.eclipse.org/svnroot/technology/org.eclipse.persistence/trunk/examples/org.eclipse.persistence.example.jpa.dynamic/ /svnroot/technology/org.eclipse.persistence/trunk/examples/org.eclipse.persistence.example.jpa.dynamic]
+
=== Bootstrap ===
  
In the future a zip will be available download with setup and run instructions.
+
In order to bootstrap with dynamic persistence some additional persistence unit properties must be specified.  
  
== Outstanding Work ==
+
:''Note: Container managed persistence not supported''
  
This how-to is based on an extension to EclipseLink JPA. The functionality needs to be consolidated into the project leveraging the existing dynamic support introduced in EclipseLink SDO.
+
<source lang="java">
 +
Map<String, Object> properties = new HashMap<String, Object>();
  
TO DO:
+
properties.put(PersistenceUnitProperties.CLASSLOADER, dcl);
 +
properties.put(PersistenceUnitProperties.WEAVING, "static");
  
* Add support for using JPA ORM.XML and EclipseLink ORM.XML. At present the metadata processor always validates the existence of the field or standard get method on the class.  
+
Persistence.createEntityManagerFactory(persistenceUnit, properties);
 +
</source>
  
* Consolidate the SDO approach for a base dynamic entity class with this one in the Foundation component
+
=== DynamicEntity API ===
  
* Minimize the introduction of yet another meta model. This will be done either through usage of the existing Descriptor/Mapping model or through some generalization of the SDO meta-model basics within Foundation.
+
<source lang="java">
 +
DynamicEntity employee = em.createNamedQuery("Employee.findMin", DynamicEntity.class).getSingleResult();
 +
 
 +
String firstName = employee.<String>get("firstName");
 +
 
 +
DynamicEntity address = employee.<DynamicEntity>get("address");
 +
 
 +
Collection<DynamicEntity> phones = employee.<Collection<DynamicEntity>>get("phoneNumbers");
 +
</source>
  
* Simplify DynamicEntity usage by subclassing AbstractMap<String, Object>
+
== Customize Properties Management ==
 +
[[EclipseLink/Examples/JPA/Dynamic/CustomizeAttributes]]
 +
[[Category:EclipseLink/Example/JPA|Dynamic]]

Latest revision as of 19:05, 15 December 2012

Contents

[edit] How to use Dynamic EclipseLink JPA

EclipseLink JPA supports Dynamic Persistence allowing users to map relational tables to virtual classes which are created dynamically at runtime. This support was added in EclipseLink 2.1.0. This example illustrates dynamic persistence using the DynamicEntity interface for all of the persistent types.

[edit] Configuration

With dynamic persistence the mappings and implied entity types can be defined using API within the application code or they can be specified using the eclipselink-orm.xml mapping file.

[edit] eclipselink-orm.xml

There are a couple of key differences versus a standard JPA mapping file:

  • access type of VIRTUAL is used
  • attribute-type must be provided
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings version="2.1"
	xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/orm"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 
	<package>example.jpa.dynamic.model.employee</package>
 
	<object-type-converter name="gender-converter"
		object-type="String" data-type="String">
		<conversion-value object-value="Male" data-value="M" />
		<conversion-value object-value="Female" data-value="F" />
	</object-type-converter>
 
	<named-query name="Employee.findAll">
		<query>SELECT e FROM Employee e ORDER BY e.lastName, e.firstName</query>
		<hint name="eclipselink.query-results-cache" value="True" />
	</named-query>
 
	<named-query name="Employee.findMin">
		<query>SELECT e FROM Employee e WHERE e.id IN (SELECT MIN(ee.id) FROM
			Employee ee)</query>
	</named-query>
 
	<entity class="Address" access="VIRTUAL">
		<attributes>
			<id name="id" attribute-type="Integer">
				<column name="ADDRESS_ID" />
				<generated-value strategy="SEQUENCE" generator="address-generator" />
				<sequence-generator name="address-generator"
					sequence-name="ADDR_SEQ" />
			</id>
			<basic name="city" attribute-type="String" />
			<basic name="country" attribute-type="String" />
			<basic name="province" attribute-type="String" />
			<basic name="postalCode" attribute-type="String">
				<column name="P_CODE" />
			</basic>
			<basic name="street" attribute-type="String" />
		</attributes>
	</entity>
 
	<entity class="PhoneNumber" access="VIRTUAL">
		<table name="PHONE" />
		<attributes>
			<id name="id" attribute-type="Integer">
				<column name="PHONE_NUM_ID" />
				<generated-value strategy="SEQUENCE" />
			</id>
			<basic name="type" attribute-type="String">
				<column updatable="false" />
			</basic>
			<basic name="areaCode" attribute-type="String">
				<column name="AREA_CODE" />
			</basic>
			<basic name="number" attribute-type="String">
				<column name="P_NUMBER" />
			</basic>
			<many-to-one name="owner" fetch="EAGER" target-entity="Employee">
				<join-column name="EMP_ID" />
			</many-to-one>
		</attributes>
	</entity>
 
	<entity class="Employee" access="VIRTUAL">
		<secondary-table name="SALARY" />
		<attributes>
			<id name="id" attribute-type="Integer">
				<column name="EMP_ID" />
				<generated-value strategy="SEQUENCE" generator="employee-generator" />
				<sequence-generator name="employee-generator"
					sequence-name="EMP_SEQ" />
			</id>
			<basic name="firstName" attribute-type="String">
				<column name="F_NAME" />
			</basic>
			<basic name="lastName" attribute-type="String">
				<column name="L_NAME" />
			</basic>
			<basic name="startTime" attribute-type="String">
				<column name="START_TIME" />
			</basic>
			<basic name="endTime" attribute-type="String">
				<column name="END_TIME" />
			</basic>
			<basic name="gender" attribute-type="String">
				<column name="GENDER" />
				<convert>gender-converter</convert>
			</basic>
			<basic name="salary" attribute-type="Integer">
				<column table="SALARY" />
			</basic>
 
			<version name="version" attribute-type="Long" />
 
			<many-to-one name="manager" fetch="EAGER" optional="true"
				target-entity="Employee">
				<join-column name="MANAGER_ID" />
			</many-to-one>
 
			<one-to-many name="managedEmployees" mapped-by="manager"
				target-entity="Employee" attribute-type="java.util.List" />
 
			<one-to-many name="phoneNumbers" mapped-by="owner"
				target-entity="PhoneNumber" attribute-type="java.util.List">
				<cascade>
					<cascade-all />
				</cascade>
				<private-owned />
			</one-to-many>
 
			<one-to-one name="address" fetch="EAGER" target-entity="Address">
				<join-column name="ADDR_ID" />
				<cascade>
					<cascade-all />
				</cascade>
				<private-owned />
			</one-to-one>
 
			<many-to-many name="projects" target-entity="Project"
				attribute-type="java.util.List">
				<join-table name="PROJ_EMP">
					<join-column name="EMP_ID" />
					<inverse-join-column name="PROJ_ID" />
				</join-table>
			</many-to-many>
 
			<element-collection name="responsibilities"
				attribute-type="java.util.List" target-class="String">
				<column name="RESPON_DESC" />
				<collection-table name="RESPONS" />
			</element-collection>
 
			<embedded name="period" attribute-type="EmploymentPeriod" />
 
		</attributes>
	</entity>
 
	<entity class="Project" access="VIRTUAL">
		<inheritance strategy="JOINED" />
		<discriminator-column name="PROJ_TYPE"
			discriminator-type="CHAR" />
		<attributes>
			<id name="id" attribute-type="Integer">
				<column name="PROJ_ID" />
				<generated-value strategy="SEQUENCE" generator="project-generator" />
				<sequence-generator name="project-generator"
					sequence-name="PROJ_SEQ" />
			</id>
			<basic name="description" attribute-type="String">
				<column name="DESCRIP" />
			</basic>
			<basic name="name" attribute-type="String">
				<column name="PROJ_NAME" />
			</basic>
			<version name="version" attribute-type="Long" />
			<many-to-one name="teamLeader" fetch="EAGER"
				target-entity="Employee">
				<join-column name="LEADER_ID" />
			</many-to-one>
		</attributes>
	</entity>
 
	<entity class="SmallProject" parent-class="Project" access="VIRTUAL">
		<table name="PROJECT" />
		<discriminator-value>S</discriminator-value>
	</entity>
 
	<entity class="LargeProject" parent-class="Project" access="VIRTUAL">
		<table name="LPROJECT" />
		<discriminator-value>L</discriminator-value>
		<attributes>
			<basic name="budget" attribute-type="Double" />
			<basic name="milestone" attribute-type="java.util.Calendar">
				<temporal>TIMESTAMP</temporal>
			</basic>
		</attributes>
	</entity>
 
	<embeddable class="EmploymentPeriod" access="VIRTUAL">
		<attributes>
			<basic name="startDate" attribute-type="java.util.Date">
				<column name="START_DATE" />
				<temporal>DATE</temporal>
			</basic>
			<basic name="endDate" attribute-type="java.util.Date">
				<column name="END_DATE" />
				<temporal>DATE</temporal>
			</basic>
		</attributes>
	</embeddable>
 
</entity-mappings>

[edit] Dynamic Configuration using API

Alternatively developers can construct dynamic persistence units using API. A complete example of the same employee demo is available here.

The basic building blocks of constructing dynamic entities using API are:

[edit] 1. Create the Dynamic Class

The dynamic class is an ASM generated subclass of Dynamic entity and it is created with a DynamicClassLoader as:

DynamicClassLoader dcl = new DynamicClassLoader(currentLoader);
 
Class<?> employeeClass = dcl.createDynamicClass(packagePrefix + "Employee");

[edit] 2. Create Dynamic Type

Using a JPADynamicTypeBuilder as a factory a new type can be constructed

JPADynamicTypeBuilder address = new JPADynamicTypeBuilder(addressClass, null, "D_ADDRESS");

[edit] 3. Add the Mappings

Finally the mappings are added:

address.setPrimaryKeyFields("ADDR_ID");
 
address.addDirectMapping("id", int.class, "ADDR_ID");
address.addDirectMapping("street", String.class, "STREET");
address.addDirectMapping("city", String.class, "CITY");
address.addDirectMapping("province", String.class, "PROV");
address.addDirectMapping("postalCode", String.class, "P_CODE");
address.addDirectMapping("country", String.class, "COUNTRY");
 
address.configureSequencing("ADDR_SEQ", "ADDR_ID");

[edit] Relationships

In addition to these basic steps you will also need to re-use the types when defining relationships between them. To create the Employee to address 1:1 both types are required and used as:

OneToOneMapping addressMapping = employee.addOneToOneMapping("address", address.getType(), "ADDR_ID");
addressMapping.setCascadeAll(true);
addressMapping.setIsPrivateOwned(true);

[edit] Usage Examples

[edit] Bootstrap

In order to bootstrap with dynamic persistence some additional persistence unit properties must be specified.

Note: Container managed persistence not supported
Map<String, Object> properties = new HashMap<String, Object>();
 
properties.put(PersistenceUnitProperties.CLASSLOADER, dcl);
properties.put(PersistenceUnitProperties.WEAVING, "static");
 
Persistence.createEntityManagerFactory(persistenceUnit, properties);

[edit] DynamicEntity API

DynamicEntity employee = em.createNamedQuery("Employee.findMin", DynamicEntity.class).getSingleResult();
 
String firstName = employee.<String>get("firstName"); 
 
DynamicEntity address = employee.<DynamicEntity>get("address"); 
 
Collection<DynamicEntity> phones = employee.<Collection<DynamicEntity>>get("phoneNumbers");

[edit] Customize Properties Management

EclipseLink/Examples/JPA/Dynamic/CustomizeAttributes