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

EclipseLink/Examples/MySports

< EclipseLink‎ | Examples
Revision as of 11:56, 19 August 2011 by Douglas.clarke.oracle.com (Talk | contribs) (JPA Usage)

EclipseLink MySports Example

The MySports example is a simple web application which demonstrates several key features of EclipseLink including:

  • EclipseLink JPA
    • @Multitenant(SINGLE_TABLE)
    • Extensible entities with @VirtualAccessMethods

Example Overview

The MySports example includes the following projects:

  1. MySports: A dynamic web project which deploys the primary application, exposing a JSF and JAX-RS (REST) front-end.
  2. MySports Admin: Administration console for the project.
  3. MySports Tests: A Java project containing Junit tests that verifies the functionality of the example and creates the necessary database tables

Installation and Configuration

Currently, the MySports example is only available through SVN access. At a later date, a downloadable ZIP file that contains the examples will be made available.


Prequisites

To run the MySports example, you will need the following:

  1. GlassFish 3.1 (http://glassfishplugins.java.net/eclipse36/)
  2. Eclipse Java EE with GlassFish adapter (http://www.eclipse.org/downloads/)
  3. EclipseLink 2.3.0 OSGi Bundles (http://www.eclipse.org/eclipselink/downloads/)


Upgrade and Configure GlassFish

Use this procedure to upgrade GlassFish to use the current EclipseLink version and prepare the connection pool.

  1. Replace the following JAR files from <GLASSFISH_HOME>\glassfish\modules\ with the JAR files from the EclipseLink OSGI bundle:
    • org.eclipse.persistence.antlr.jar
    • org.eclipse.persistence.asm.jar
    • org.eclipse.persistence.core.jar
    • org.eclipse.persistence.jpa.jar
    • org.eclipse.persistence.jpa.modelgen.jar
    • org.eclipse.persistence.oracle.jar
  2. Copy the following JAR file from the EclipseLink OSGI bundle to <GLASSFISH_HOME>\glassfish\modules\:
    • org.eclipse.persistence.moxy_2.3.0.v20110604-r9504.jar
  3. Use the GlassFish Admin Console to create a new JDBC resource named jdbc/MySports and a new JDBC connection pool named MySportsPool. In this example, we will use the embedded Derby database:
    • Resource type: javax.sql.DataSource
    • Datasource classname: org.apache.derby.jdbc.EmbeddedDataSource40
      Note: Be sure to edit the JDBC Connection Pool properties to reflect your database connection information. In this example, we will use:
    • User: APP
    • Password: APP
    • ServerName: localhost
    • DatabaseName: gemini
    • PortNumber: 1527

Mysports jdbc.png

Configure Eclipse Environment

Now you can configure Eclipse to run the MySports example.

  1. Add a variable named eclispelink_2.3_jar with the value <ECLIPSELINK OSGI BUNDLE>\org.eclipse.persistence.core_2.3.0.v20110604-r9504.jar.
    This is used for the javaagent of the test project. You can add this variable by creating a String Substitution variable, as shown here:
    Mysports variable.png
  2. Obtain the three example projects from the EclipseLink SVN repository:
    Note: If you use the Eclipse Team Provider for SVN plugin (http://www.eclipse.org/subversive/downloads.php) Eclipse will can import the example projects.
  3. Start the Derby server. The GlassFish installation includes a Derby instance that can be used, in the <GLASSFISH_HOME>\javadb\ folder.
  4. Import the EclipseLink MySports projects into Eclipse.
  5. Create a new Derby connection profile. Use the same JDBC Connection Properties that you used when creating the GlassFish connection pool.
  6. Add the Connectivity Driver Definition for the Derby database to the project.
    Mysports derbyclientjar.png
  7. Edit the eclipselink-examples-mysports.properties file to match your database connection information.
    Mysports properties.png

Running the Example

Now, you're ready to run the MySports example.

  1. Run the MySports AllTests launch target. This will create the database tables and prepare an initial population
  2. Run the MySports Admin application
  3. Run the MySports application

Example Details

MySports Domain (example.mysports.model)

The domain model is that of any arbitrary sports league. The intent is to build an application that captures the state of an arbitrary league and use it in a multitenant application where multiple leagues (tenants) can be hosted. The domain model is intentionally unaware of the potential support for multiple leagues and therefore only models entities that exist within an individual league.

Class/Interface Description
Division
@Entity
@Table(name = "mys_div")
@Multitenant(MultitenantType.SINGLE_TABLE)
@TenantDiscriminatorColumn(name = "LEAGUE_ID", contextProperty = LEAGUE_CONTEXT)
@NamedQueries({
    @NamedQuery(name="Division.findAll", query="SELECT d FROM Division d ORDER BY d.name",
                hints={@QueryHint(name=QueryHints.QUERY_RESULTS_CACHE, value=HintValues.TRUE)}),
    @NamedQuery(name="Division.findByName", query="SELECT d FROM Division d WHERE d.name = :NAME")
})
public class Division implements Extensible {
Team
@Entity
@Table(name = "mys_team")
@Multitenant(MultitenantType.SINGLE_TABLE)
@TenantDiscriminatorColumn(name = "LEAGUE_ID", contextProperty = LEAGUE_CONTEXT)
@NamedQuery(name="Team.findByDivisionAndName", query="SELECT t FROM Team t WHERE t.name = :NAME AND t.division.name = :DIV")
public class Team implements Extensible {
Player
/**
 * In the MySports demo a Player entity represents an individual member of a
 * team. The Player entity is both multitenant and extensible.
 */
@Entity
@Table(name = "mys_player")
@Multitenant(MultitenantType.SINGLE_TABLE)
@TenantDiscriminatorColumn(name = "LEAGUE_ID", contextProperty = LEAGUE_CONTEXT)
@VirtualAccessMethods
public class Player implements Extensible {
Extensible An application interface used to indicate extensible types and enable their use in the presentation tier. This interface is not required or used by EclipseLink.
Divisions A non-entity container class used for returning multiple divisions from a JAX-RS call to enable the MOXy marshalling into XML.

Persistence

The persistence layer is the main purpose of this example application. The persistence layer makes use of application bootstrapped persistence contexts with support for an EntityManagerFactory and backing ServerSession (with Cache) per league (tenant). This all bootstrapped off of a single persistence-unit definition.

Runtime Architecture

Mysports persistence.png

LeagueRepository

The LeagueRepository is the primary persistence abstraction responsible for league (tenant) specific persistence access managing both JPA persistence units and the MOXy JAXBContext.

JPA Usage

The persistence layer makes use of standard JPA 2.0 API plus some EclipseLink specific extensions to support:

  • Multi-tenancy - augment the SQL to limit results to current tenant or leverage Oracle VPD to do SQL augmentation
  • Extensible Entities - support additional tenant specific attributes on entities
  • External Metadata Source - retrieve tenant specific extension definitions for JPA and JAXB from JAX-RS admin service
  • EMF per Tenant: EMF created per tenant with a single shared persistence unit definition
Multi-Tennacy
Extensible Entities
External Metadata Source

=EMF per Tenant

When developing an application that can handle requests from multiple tenants you can choose to have a single EntityManager Factory (EMF) with tenant scoped EntityManagers (EM) or you can choose to have a EMF per tenant. The decision really comes down to the number of tenants and the value of maintaining a cache that can be shared upon user requests for the same tenant. In this example application where there are a limited number of tenants the decision was made to use an EMF per tenant.

EMF per tenant leverages a shared persistence unit definition (persistence.xml):

<persistence-unit name="mysports" transaction-type="RESOURCE_LOCAL">
	<non-jta-data-source>jdbc/MySports</non-jta-data-source>
 
	<class>example.mysports.model.User</class>
	<class>example.mysports.model.Team</class>
	<class>example.mysports.model.Player</class>
	<class>example.mysports.model.Division</class>
 
	<validation-mode>NONE</validation-mode>

This definition is then used with separate shared EclipseLink sessions and caches being created per tenant using persistence unit properties passed into the call to create the EMF:

LeagueRepository.createEMF:

emfProps.put(SESSION_NAME, getConfig().getSessionName(leagueId));
emfProps.put(LEAGUE_CONTEXT, leagueId);
emfProps.put(MySportsConfig.class.getName(), getConfig());
 
this.emf = Persistence.createEntityManagerFactory(PU_NAME, emfProps);

The SESSION_NAME property is what uniquely identifies the shared session and cache used for the EMF. If multiple create requests are issues with the same value on the same class-loader they will each be given new EMF instances but will share the same shared session and cache. The LEAGUE_CONTEXT property is the value that is set on the EMF to identify the tenant required by @Multitenant on the entities. The third EMF property is simply a way to cache the MYSportsConfigf instance within the shared session.

MOXy (JAXB) Usage

The MySports application uses of EclipseLink MOXy to map its persistent entities into XML within the JAX-RS (RESTful) interface. The mapping is done using an eclipselink-oxm.xml mapping file instead of JAXB annotations.

<?xml version="1.0"?>
<xml-bindings version="2.3"
	xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
	package-name="example.mysports.model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.eclipse.org/eclipselink/xsds/persistence/oxm http://www.eclipse.org/eclipselink/xsds/eclipselink_oxm_2_3.xsd">
	<java-types>
		<java-type name="Divisions" xml-accessor-type="FIELD">
			<java-attributes>
				<xml-element java-attribute="divisions" xml-path="divisions" />
			</java-attributes>
		</java-type>
		<java-type name="Division" xml-accessor-type="FIELD">
			<xml-root-element />
			<xml-virtual-access-methods />
			<java-attributes>
				<xml-attribute java-attribute="id" />
				<xml-attribute java-attribute="version" />
				<xml-attribute java-attribute="name" />
				<xml-element java-attribute="teams" xml-path="teams/team" />
				<xml-transient java-attribute="attributes" />
				<xml-transient java-attribute="defaultDivision" />
			</java-attributes>
		</java-type>
		<java-type name="Team" xml-accessor-type="FIELD">
			<xml-root-element />
			<xml-virtual-access-methods />
			<java-attributes>
				<xml-attribute java-attribute="id" />
				<xml-attribute java-attribute="version" />
				<xml-element java-attribute="name" />
				<xml-element java-attribute="players" xml-path="players/player" />
				<xml-inverse-reference java-attribute="division"
					mapped-by="teams" />
				<xml-transient java-attribute="attributes" />
			</java-attributes>
		</java-type>
		<java-type name="Player" xml-accessor-type="FIELD">
			<xml-root-element />
			<xml-virtual-access-methods />
			<java-attributes>
				<xml-attribute java-attribute="id" />
				<xml-attribute java-attribute="version" />
				<xml-attribute java-attribute="userid" />
				<xml-element java-attribute="firstName" xml-path="name/@first" />
				<xml-element java-attribute="lastName" xml-path="name/@last" />
				<xml-inverse-reference java-attribute="team"
					mapped-by="players" />
				<xml-transient java-attribute="attributes" />
			</java-attributes>
		</java-type>
	</java-types>
</xml-bindings>

Back to the top