The Java Persistence API (JPA) is a specification that sets a standard for persistently storing objects in enterprise and non-enterprise Java based environments. JPA provides an Object Relational Mapping (ORM) model that is configured through persistence descriptors. This Java Persistence Service specification defines how persistence units can be published in an OSGi framework, how client bundles can find these persistence units, how database drivers are found with the Data Service Specification for JDBC™ Technology, as well as how JPA providers can be made available within an OSGi framework.
Applications can be managed or they can be unmanaged. Managed applications run inside a Java EE Container and unmanaged applications run in a Java SE environment. The managed case requires a provider interface that can be used by the container, while in the unmanaged case the JPA provider is responsible for supporting the client directly. This specification is about the unmanaged model of JPA except in the areas where the managed model is explicitly mentioned. Additionally, multiple concurrent providers for the unmanaged case are not supported.
-
Dependencies - There must be a way for persistence clients, if they so require, to manage their dependencies on a compatible persistence unit.
-
Compatibility - The Persistence Unit service must be able to function in non-managed mode according to existing standards and interfaces outlined in the JPA specification.
-
Modularity - Persistent classes and their accompanying configuration can exist in a separate bundle from the client that is operating on them using the Persistence Unit service.
-
JDBC - Leverage the Data Service Specification for JDBC™ Technology for access to the database.
-
JPA - The Java Persistence API, [3] JPA 2.1.
-
JPA Provider - An implementation of JPA, providing the Persistence Provider and JPA Services to Java EE Containers and Client Bundles.
-
Interface Bundle - A bundle containing the interfaces and classes in the
javax.persistence
namespace (and its sub-namespaces) that are defined by the JPA specification. -
Persistence Bundle - A bundle that includes, a Meta-Persistence header, one or more Persistence Descriptor resources, and the entity classes specified by the Persistence Units in those resources.
-
Client Bundle - The bundle that uses the Persistence Bundle to retrieve and store objects.
-
Persistence Descriptor - A resource describing one or more Persistence Units.
-
Persistence Unit - A named configuration for the object-relational mappings and database access as defined in a Persistence Descriptor.
-
Entity Manager - The interface that provides the control point of retrieving and persisting objects in a relational database based on a single Persistence Unit for a single session.
-
Entity Manager Factory - A service that can create Entity Managers based on a Persistence Unit for different sessions.
-
Entity Manager Factory Builder - A service that can build an Entity Manager Factory for a specific Persistence Unit with extra configuration parameters.
-
Managed Client - A Client Bundle that is managed by a Container
-
Static Client - A Client that uses the static factory methods in the Persistence class instead of services.
-
Static Persistence - The actor that enables the use of the
Persistence
class static factory methods to obtain an Entity Manager Factory. -
JDBC Provider - The bundle providing a Data Source Factory service.
This specification requires a minimum JPA version of 2.1. Implementations may choose to support newer versions of JPA, for example version 2.2, but must offer the JavaJPA contract at version 2.1 as well as any future versions that they support.
A JPA Provider tracks Persistence Bundles; a Persistence Bundle contains a Meta-Persistence manifest header. This manifest header enumerates the Persistence Descriptor resources in the Persistence Bundle. Each resource's XML schema is defined by the JPA specification. The JPA Provider reads the resource accordingly and extracts the information for one or more Persistence Units. For each found Persistence Unit, the JPA Provider registers an Entity Manager Factory Builder service. If the database is defined in the Persistence Unit, then the JPA Provider registers an Entity Manager Factory service during the availability of the corresponding Data Source Factory.
The identification of these services is handled through a number of service properties. The Entity Manager Factory service is named by the standard JPA interface, the Builder version is OSGi specific; it is used when the Client Bundle needs to create an Entity Manager Factory based on configuration properties.
A Client Bundle that wants to persist or retrieve its entity classes depends on an Entity Manager Factory (Builder) service that corresponds to a Persistence Unit that lists the entity classes. If such a service is available, the client can use this service to get an Entity Manager, allowing the client to retrieve and persist objects as long as the originating Entity Manager Factory (Builder) service is registered.
In a non-OSGi environment, it is customary to get an Entity
Manager Factory through the Persistence
class. This
Persistence
class provides a number of static methods that
give access to any locally available JPA providers. This approach is not
recommended in an OSGi environment due to class loading and start
ordering issues. However, OSGi environments can support access through
this static factory with a Static Persistence bundle.
Java Persistence API (JPA) is a specification that is part of [4] Java EE 5. This OSGi Specification is based on [1] JPA 1.0, [2] JPA 2.0 and [3] JPA 2.1. This section provides an overview of JPA as specified in the JCP. The purpose of this section is to introduce the concepts behind JPA and define the terminology that will be used in the remainder of the chapter.
The purpose of JPA is to simplify access to relational databases for applications on the object-oriented Java platform. JPA provides support for storing and retrieving objects in a relational database. The JPA specification defines in detail how objects are mapped to tables and columns under the full control of the application. The core classes involved are depicted in Figure 127.2.
The JPA specifications define a number of concepts that are defined in this section for the purpose of this OSGi specification. However, the full syntax and semantics are defined in the JPA specifications.
Classes that are stored and retrieved through JPA are called the entity classes. In this specification, the concept of entity classes includes the embeddable classes, which are classes that do not have any persistent identity, and mapped super classes that allow mappings, but are not themselves persistent. Entity classes are not required to implement any interface or extend a specific superclass, they are Plain Old Java Objects (POJOs). It is the responsibility of the JPA Provider to connect to a database and map the store and retrieve operations of the entity classes to their tables and columns. For performance reasons, the entity classes are sometimes enhanced. This enhancement can take place during build time, deploy time, or during class loading time. Some enhancements use byte code weaving, some enhancements are based on sub-classing.
The JPA Provider cannot automatically perform its persistence tasks; it requires configuration information. This configuration information is stored in the Persistence Descriptor. A Persistence Descriptor is an XML file according of one of the two following namespaces:
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd
The JPA standard Persistence Descriptor must be stored in
META-INF/persistence.xml
. It is usually in the same class
path entry (like a JAR or directory) as the entity classes.
The JPA Provider parses the Persistence Descriptor and extracts one or more Persistence Units. A Persistence Unit includes the following aspects:
-
Name - Every Persistence Unit must have a name to identify it to clients. For example:
Accounting
. -
Provider Selection - Restriction to a specific JPA Provider, usually because there are dependencies in the application code on provider specific functionality.
-
JDBC Driver Selection - Selects the JDBC driver, the principal and the credentials for selecting and accessing a relational database. See JDBC Access in JPA.
-
Properties - Standard and JPA Provider specific properties.
The object-relational mappings are stored in special mapping resources or are specified in annotations.
A Persistence Unit can be complete or incomplete. A complete Persistence Unit identifies the database driver that is needed for the Persistence Unit, though it does not have to contain the credentials. An incomplete Persistence Unit lacks this information.
The relations between the class path, its entries, the entity classes, the Persistence Descriptor and the Persistence Unit is depicted in Figure 127.3 on page .
JPA recognizes the concept of a persistence
root. The persistence root is the root of the JAR (or
directory) on the class path that contains the
META-INF/persistence.xml
resource.
The JPA specifications provide support for multiple JPA Providers
in the same application. An Application selects a JPA Provider through
the Persistence
class, using static factory methods. One of
these methods accepts a map with configuration
properties. Configuration properties can override information
specified in a Persistence Unit or these properties add new information
to the Persistence Unit.
The default implementation of the Persistence
class
discovers providers through the Java EE services model, this model
requires a text resource in the class path entry called:
META-INF/services/javax.persistence.PersistenceProvider
This text resource contains the name of the JPA Provider implementation class.
The Persistence
class
createEntityManagerFactory
method provides the JPA Provider
with the name of a Persistence Unit. The JPA Provider must then scan the
class path for any META-INF/persistence.xml
entries, these
are the available Persistence Descriptors. It then extracts the
Persistence Units to find the requested Persistence Unit. If no such
Persistence Unit can be found, or the JPA Provider is restricted from
servicing this Persistence Unit, then null
is returned. The
Persistence class will then continue to try the next found or registered
JPA Provider.
A Persistence Unit can restrict JPA Providers by specifying a
JPA Provider class, this introduces a
provider dependency. The specified JPA Provider
class must implement the PersistenceProvider
interface.
This implementation class name must be available
from the JPA Provider's documentation. JPA Providers that do not own the
specified JPA Provider class must ignore such a Persistence Unit.
Otherwise, if the Persistence Unit is not restricted, the JPA
Provider is assigned to this Persistence Unit; it
must be ready to provide an EntityManagerFactory
object
when the application requests one.
The JPA Provider uses the Persistence Unit, together with any additional configuration properties, to construct an Entity Manager Factory. The application then uses this Entity Manager Factory to construct an Entity Manager, optionally providing additional configuration properties. The Entity Manager then provides the operations for the application to store and retrieve entity classes from the database.
The additional configuration properties provided with the creation of the Entity Manager Factory or the Entity Manager are often used to specify the database driver and the credentials. This allows the Persistence Unit to be specified without committing to a specific database, leaving the choice to the application at runtime.
The relations between the application, Entity Manager, Entity Manager Factory and the JPA Provider are depicted in Figure 127.4 on page .
The JPA specifications make a distinction between a
managed and an unmanaged mode.
In the managed mode the presence of a Java EE Container is assumed. Such
a container provides many services for its contained applications like
transaction handling, dependency injection, etc. One of these aspects
can be the interface to the relational database. The JPA specifications
therefore have defined a special method for Java EE Containers to manage
the persistence aspects of their Managed Clients. This method is the
createContainerEntityManagerFactory
method on the
PersistenceProvider
interface. This method is purely
intended for Java EE Containers and should not be used in other
environments.
The other method on the PersistenceProvider
interface
is intended to be used by the Persistence
class static
factory methods. The Persistence class searches for an appropriate JPA
Provider by asking all available JPA Providers to create an Entity
Manager Factory based on configuration properties. The first JPA
Provider that is capable of providing an Entity Manager Factory wins.
The use of these static factory methods is called the
unmanaged mode. It requires a JPA Provider to scan
the class path to find the assigned Persistence Units.
A Persistence Unit is configured to work with a relational database. JPA Providers communicate with a relational database through compliant JDBC database drivers. The database and driver parameters are specified in the Persistence Unit or configured during Entity Manager Factory or Entity Manager creation with the configuration properties. The configuration properties for selecting a database in non-managed mode were proprietary in JPA 1.0 but have been standardized in version 2.0 of JPA:
-
javax.persistence.jdbc.driver
- Fully-qualified name of the driver class -
javax.persistence.jdbc.url
- Driver-specific URL to indicate database information -
javax.persistence.jdbc.user
- User name to use when obtaining connections -
javax.persistence.jdbc.password
- Password to use when obtaining connections
The primary goal of this specification is to simplify the programming model for bundles that need persistence. In this specification there are two application roles:
-
Persistence Bundle - A Persistence Bundle contains the entity classes and one or more Persistence Descriptors, each providing one or more Persistence Units.
-
Client Bundle -A Client Bundle contains the code that manipulates the entity classes and uses an Entity Manager to store and retrieve these entity classes with a relational database. The Client Bundle obtains the required Entity Manager(s) via a service based model.
These roles can be combined in a single bundle.
A JPA Provider uses Persistence Units to provide Client Bundles with a configured Entity Manager Factory service and/or an Entity Manager Factory Builder service for each assigned Persistence Unit:
-
Entity Manager Factory service - Provides an
EntityManagerFactory
object that depends on a complete Persistence Unit. That is, it is associated with a registered Data Source Factory service. -
Entity Manager Factory Builder service - The Entity Manager Factory Builder service provides the capability of creating an
EntityManagerFactory
object with additional configuration properties. The Entity Manager Factory Builder service also provides information about the JPA Provider that will be used to create theEntityManagerFactory
object.
These services are collectively called the JPA Services. Entity Managers obtained from such JPA Services can only be used to operate on entity classes associated with their corresponding Persistence Unit.
A Persistence Bundle is a bundle that
specifies the Meta-Persistence header, see Meta Persistence Header.
This header refers to one or more Persistence Descriptors in the
Persistence Bundle. Commonly, this is the
META-INF/persistence.xml
resource. This location is the
standard for non-OSGi environments, however an OSGi bundle can also use
other locations as well as multiple resources.
For example, the contents of a simple Persistence Bundle with a
single Person
entity class could look like:
META-INF/
META-INF/MANIFEST.MF
OSGI-INF/address.xml
com/acme/Person.class
The corresponding manifest would then look like:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Meta-Persistence: OSGI-INF/address.xml
Bundle-SymbolicName: com.acme.simple.persistence
Bundle-Version: 3.2.4.200912231004
A Persistence Bundle is a normal bundle; it must follow all the rules of OSGi and can use all OSGi constructs like Bundle-ClassPath, fragment bundles, import packages, export packages, etc. However, there is one limitation: any entity classes must originate in the bundle's JAR, it cannot come from a fragment. This requirement is necessary to simplify enhancing entity classes.
A Client Bundle uses the entity classes from a Persistence Bundle to provide its required functionality. To store and retrieve these entity classes a Client Bundle requires an Entity Manager that is configured for the corresponding Persistence Unit.
An Entity Manager is intended to be used by a single session, it is not thread safe. Therefore, a client needs an Entity Manager Factory to create an Entity Manager. In an OSGi environment, there are multiple routes to obtain an Entity Manager Factory.
A JPA Provider must register an Entity Manager Factory service for
each assigned Persistence Unit that is complete.
Complete means that it is a configured Persistence Unit, including the
reference to the relational database. The Entity Manager Factory service
is therefore bound to a Data Source Factory service and Client Bundles
should not attempt to rebind the Data Source Factory with the
configuration properties of the createEntityManager(Map)
method. See Rebinding for the consequences. If the Data
Source Factory must be bound by the Client Bundle then the Client Bundle
should use the Custom Configured Entity Manager.
The Entity Manager Factory service must be registered with the service properties as defined in Service Registrations. These are:
-
osgi.unit.name
- (String
) The name of the Persistence Unit -
osgi.unit.version
- (String
) The version of the associated Persistence Bundle -
osgi.unit.provider
- (String
) The implementation class name of the JPA Provider
The life cycle of the Entity Manager Factory service is bound to the Persistence Bundle, the JPA Provider, and the selected Data Source Factory service.
A Client Bundle that wants to use an Entity Manager Factory service should therefore use an appropriate filter to select the Entity Manager Factory service that corresponds to its required Persistence Unit. For example, the following snippet uses Declarative Services, see Declarative Services Specification, to statically depend on such a service:
<reference name="accounting"
target="(&(osgi.unit.name=Accounting)(osgi.unit.version=3.2.*))"
interface="javax.persistence.EntityManagerFactory"/>
If a Client Bundle needs to provide configuration properties for the creation of an Entity Manager Factory it should use the Entity Manager Factory Builder service. This can for example be used to provide the database selection properties when the Persistence Unit is incomplete or if the database selection needs to be overridden. The Entity Manager Factory Builder service also provides information about the JPA Provider that will be used to create the Entity Manager Factory. This information can be used by the Client Bundle when determining what (if any) JPA Provider implementation specific configuration that the Client Bundle will provide.
The Entity Manager Factory Builder service's life cycle must not depend on the availability of any Data Source Factory, even if a JDBC driver class name is specified in the Persistence Descriptor. The Entity Manager Factory Builder service is registered with the same service properties as the corresponding Entity Factory service, see Service Registrations.
The following methods are defined on the
EntityManagerFactoryBuilder
interface:
-
createEntityManagerFactory(Map) - Returns a custom configured
EntityManagerFactory
instance for the Persistence Unit associated with the service. Accepts a map with the configuration properties to be applied during Entity Manager Factory creation. The method must return a proper Entity Manager Factory or throw an Exception. -
getPersistenceProviderName() - Returns the name of the
PersistenceProvider
implementation class used in Entity Manager Factory creation. This name will be the same as the value of the JPA_UNIT_PROVIDER service property. -
getPersistenceProviderBundle() - Returns the bundle JPA Provider implementation bundle which provides the
PersistenceProvider
. If the Persistence Provider was provided as an OSGi service then this method must return the bundle which registered the service. Otherwise this method must return the bundle which loaded thePersistenceProvider
implementation class.
The createEntityManagerFactory
method allows standard
and vendor-specific properties to be passed in and applied to the Entity
Manager Factory being created. However, some properties cannot be
honored by the aforementioned method. For example, the
javax.persistence.provider
JPA property, as a means to
specify a specific JPA Provider at runtime, cannot be supported because
the JPA Provider has already been decided; it is the JPA Provider that
registered the Entity Manager Factory Builder service. A JPA Provider
should throw an Exception if it recognizes the property but it cannot
use the property when specified through the builder. Unrecognized
properties must be ignored.
Once an Entity Manager Factory is created the specified Data Source becomes associated with the Entity Manager Factory. It is therefore not possible to re-associate an Entity Manager Factory with another Data Source by providing different properties. A JPA Provider must throw an Exception when an attempt is made to re-specify the database properties. See Rebinding for further information.
As an example, a sample snippet of a client that wants to operate
on a persistence unit named Accounting
and pass in the JDBC
user name and password properties is:
ServiceReference[] refs = context.getServiceReferences(
EntityManagerFactoryBuilder.class.getName(),
"(osgi.unit.name=Accounting)");
if ( refs != null ) {
EntityManagerFactoryBuilder emfBuilder =
(EntityManagerFactoryBuilder) context.getService(refs[0]);
if ( emfBuilder != null ) {
Map<String,Object> props = new HashMap<String,Object>();
props.put("javax.persistence.jdbc.user", userString);
props.put("javax.persistence.jdbc.password",passwordString);
EntityManagerFactory emf = emfBuilder.createEntityManagerFactory(props);
EntityManager em = emf.createEntityManager();
...
}
The example does not handle the dynamic dependencies on the associated Data Source Factory service.
The [3] JPA 2.1 specification adds a significant number of standard property names. These properties are used both for runtime control, and also for configuring JPA persistence units as they are created.
The EntityManagerFactoryBuilder service must support the defined property names as per the JPA specification. In most cases this will be accomplished by passing the values directly to the Persistence Provider, but in some cases it may require further action from the JPA Service implementation.
A Persistence Bundle is identified by its Meta-Persistence manifest header that references a number of Persistence Descriptor resources. Persistence bundles must be detected by a JPA Provider. The JPA Provider must parse any Persistence Descriptors in these bundles and detect the assigned Persistence Units. For each assigned Persistence Unit, the JPA Provider must register an Entity Manager Factory Builder service when the Persistence Bundle is ready, see Ready Phase.
For complete and assigned Persistence Units, the JPA Provider must find the required Data Source Factory service based on the driver name. When the Persistence Bundle is ready and the selected Data Source Factory is available, the JPA Provider must have an Entity Manager Factory service registered that is linked to that Data Source Factory.
When the Persistence Bundle is stopped (or the JPA Provider stops), the JPA Provider must close all connections and cleanup any resources associated with the Persistence Bundle.
This process is outlined in detail in the following sections.
A JPA Provider must ignore Persistence Bundles that are in another
class space for the javax.persistence.*
packages. Such a
JPA Provider cannot create JPA Services that would be visible and usable
by the Client Bundles.
A Persistence Bundle is a bundle that contains the Meta-Persistence header. If this header is not present, then this specification does not apply and a JPA Provider should ignore the corresponding bundle.
The persistence root of a Persistence Unit is the root of the Persistence Bundle's JAR
The Meta-Persistence header has a syntax of:
Meta-Persistence ::= ( jar-path ( ',' jar-path)* )?
jar-path ::= path ( '!/' spath )?
spath ::= path // must not start with solidus ('/' \u002F)
The header may include zero or more comma-separated
jar-paths
, each a path to a Persistence Descriptor resource
in the bundle. Paths may optionally be prefixed with the solidus
('/' \u002F
) character. The JPA Provider must always
include the META-INF/persistence.xml
first if it is not one
of the listed paths. Wildcards in directories are not supported. The
META-INF/persistence.xml
is therefore the default location
for an empty header.
For example:
Meta-Persistence: META-INF/jpa.xml, persistence/jpa.xml
The previous example will instruct the JPA Provider to process the
META-INF/persistence.xml
resource first, even though it is
not explicitly listed. The JPA Provider must then subsequently process
META-INF/jpa.xml
and the persistence/jpa.xml
resources.
The paths in the Meta-Persistence header must be used with the
Bundle.getEntry()
method, or a mechanism with similar
semantics, to obtain the corresponding resource. The
getEntry
method does not force the bundle to resolve when
still unresolved; resolving might interfere with the efficiency of any
required entity class enhancements. However, the use of the
getEntry
method implies that fragment bundles cannot be
used to contain Persistence Descriptors nor entity classes.
Paths in the Meta-Persistence header can reference JAR files that
are nested in the bundle by using the !/ jar:
URL syntax to
separate the JAR file from the path within the JAR, for example:
Meta-Persistence: embedded.jar!/META-INF/persistence.xml
This example refers to a resource in the embedded.jar
resource, located in the META-INF
directory of
embedded.jar
.
The !/
splits the jar-path
in a prefix
and a suffix:
-
Prefix - The prefix is a path to a JAR resource in the bundle.
-
Suffix - The suffix is a path to a resource in the JAR identified by the prefix.
For example:
embedded.jar!/META-INF/persistence.xml
prefix: embedded.jar
suffix: META-INF/persistence.xml
It is not required that all listed or implied resources are
present in the bundle's JAR. For example, it is valid that the default
META-INF/persistence.xml
resource is absent. However, if no
Persistence Units are found at all then the absence of any Persistence
Unit is regarded as an error that should be logged. In this case, the
Persistence Bundle is further ignored.
A JPA Provider can detect a Persistence Bundle as early as its installation time. This early detection allows the JPA Provider to validate the Persistence Bundle as well as prepare any mechanisms to enhance the classes for better performance. However, this process can also be delayed until the bundle is started.
The JPA Provider must validate the Persistence Bundle. A valid Persistence Bundle must:
-
Have no parsing errors of the Persistence Descriptors
-
Validate all Persistence Descriptors against their schemas
-
Have at least one assigned Persistence Unit
-
Have all entity classes mentioned in the assigned Persistence Units on the Persistence Bundle's JAR.
A Persistence Bundle that uses multiple providers for its Persistence Units could become incompatible with future versions of this specification.
If any validation fails, then this is an error and should be logged. Such a bundle is ignored completely even if it also contains valid assigned Persistence Units. Only a bundle update can recover from this state.
Persistence Units can restrict JPA Providers by specifying a provider dependency. JPA Providers that do not own this JPA Provider implementation class must ignore such a Persistence Unit completely. Otherwise, if the JPA Provider can service a Persistence Unit, it assigns itself to this Persistence Unit.
If after the processing of all Persistence Descriptors, the JPA Provider has no assigned Persistence Units, then the JPA Provider must further ignore the Persistence Bundle.
A Persistence Bundle is ready when its state
is ACTIVE
or, when a lazy
activation policy is
used, STARTING
. A JPA Provider must track the ready state
of Persistence Bundles that contain assigned Persistence Units.
While a Persistence Bundle is ready, the JPA Provider must have,
for each assigned Persistence Unit, an Entity Manager Factory Builder
service registered to allow Client Bundles to create new
EntityManagerFactory
objects. The JPA Provider must also
register an Entity Manager Factory for each assigned and complete
Persistence Unit that has its corresponding Data Source available in the
service registry.
The service registration process is asynchronous with the Persistence Bundle start because a JPA Provider could start after a Persistence Bundle became ready.
The JPA Services must be registered through the Bundle Context of the corresponding Persistence Bundle to ensure proper class space consistency checks by the OSGi Framework.
JPA Services are always related to an assigned Persistence Unit. To identify this Persistence Unit and the assigned JPA Provider, each JPA Service must have the following service properties:
-
osgi.unit.name
- (String
) The name of the Persistence Unit. This property corresponds to thename
attribute of thepersistence-unit
element in the Persistence Descriptor. It is used by Client Bundles as the primary filter criterion to obtain a JPA Service for a required Persistence Unit. There can be multiple JPA Services registered under the sameosgi.unit.name
, each representing a different version of the Persistence Unit. -
osgi.unit.version
- (String
) The version of the Persistence Bundle, as specified in Bundle-Version header, that provides the corresponding Persistence Unit. Client Bundles can filter their required JPA Services based on a particular Persistence Unit version. -
osgi.unit.provider
- (String
) The JPA Provider implementation class name that registered the service. Theosgi.unit.provider
property allows Client Bundles to know the JPA Provider that is servicing the Persistence Unit. Client Bundles should be careful when filtering on this property, however, since the JPA Provider that is assigned a Persistence Unit may not be known by the Client Bundle ahead of time. If there is a JPA Provider dependency, it is better to specify this dependency in the Persistence Unit because other JPA Providers are then not allowed to assign such a Persistence Unit and will therefore not register a service.
Once the Persistence Bundle is ready, a JPA Provider must register an Entity Manager Factory Builder service for each assigned Persistence Unit from that Persistence Bundle.
The Entity Manager Factory Builder service must be registered with
the service properties listed in Service Registrations. The Entity
Manager Factory Builder service is registered under the
org.osgi.service.jpa.EntityManagerFactoryBuilder
name. This
interface is using the JPA packages and is therefore bound to one of the
two supported versions, see Dependencies.
The Entity Manager Factory Builder service enables the creation of a parameterized version of an Entity Factory Manager by allowing the caller to specify configuration properties. This approach is necessary if, for example, the Persistence Unit is not complete.
A complete Persistence Unit is configured with a specific relational database driver, see JDBC Access in JPA. A JPA Provider must have an Entity Manager Factory service registered for each assigned and complete Persistence Unit when:
-
The originating Persistence Bundle is ready, and
-
A matching Data Source Factory service is available. Matching a Data Source Factory service to a Persistence Unit is discussed in Database Access.
A JPA Provider must track the life cycle of the matching Data Source Factory service; while this service is unavailable the Entity Manager Factory service must also be unavailable. Any active Entity Managers created by the Entity Manager Factory service become invalid to use at that time.
The Entity Manager Factory service must be registered with the same service properties as described for the Entity Manager Factory Builder service, see Service Registrations. It should be registered under the following name:
javax.persistence.EntityManagerFactory
The EntityManagerFactory
interface is from the JPA
packages and is therefore bound to one of the two supported versions,
see Dependencies.
An Entity Manager Factory is bound to a Data Source Factory
service because its assigned Persistence Unit was complete. However, a
Client Bundle could still provide JDBC configuration properties for the
createEntityManager(Map)
method. This not always possible,
see Rebinding.
In the case of an incomplete Persistence Unit no Entity Manager
Factory can be initially registered, however once configured using an
Entity Manager Factory Builder service the JPA Service must register the
created Entity Manager Factory as a service. The registered service must
include any supplied configuration properties that match the recommended
OSGi service property types as service properties. The
javax.persistence.jdbc.password
property must be omitted
from these service properties.
If the Entity Manager Factory Builder service is later used to change the configuration being used by the Entity Manager Factory Service then the registered Entity Manager Factory service must be unregistered and closed. The newly created Entity Manager Factory object must then be registered as a service.
If a Persistence Bundle is being stopped, then the JPA Provider
must ensure that any resources allocated on behalf of the Persistence
Bundle are cleaned up and all open connections are closed. This cleanup
must happen synchronously with the STOPPING
event. Any
Exceptions being thrown while cleaning up should be logged but must not
stop any further clean up.
If the JPA Provider is being stopped, the JPA Provider must unregister all JPA Services that it registered through the Persistence Bundles and clean up as if those bundles were stopped.
The Entity Manager Factory object has a close
method.
This method closes the EntityManagerFactory and all associated Entity
Manager instances. As an OSGi framework is a multi-tenant environment it
should not be possible for one user of an Entity Manager Factory service
to break the valid usage of another. Therefore calls to the
close
method of the EntityManagerFactory registered in the
service registry must not close the Entity Manager
Factory.
When an Entity Manager Factory Builder service is used to create an Entity Manager Factory the same rules apply to the resulting Entity Manager Factory service, however the object returned by the Entity Manager Factory Builder behaves differently. This object has a working close method which must unregister the Entity Manager Factory service and close the Entity Manager Factory. This allows callers of the Entity Manager Factory Builder to invalidate the Entity Manager Factories that they create if, for example, a configuration changes, or a Data Source becomes invalid.
JPA Providers supply the implementation of the JPA Services and the Persistence Provider service. It is the responsibility of a JPA Provider to store and retrieve the entity classes from a relational database. It is the responsibility of the JPA Provider to register a Persistence Provider and start tracking Persistence Bundles, see Extending a Persistence Bundle.
A JPA Provider that supports running in managed mode should
register a specific service for the Java EE Containers: the Persistence
Provider service. The interface is the standard JPA
PersistenceProvider
interface. See Dependencies for the issues around the multiple versions that
this specification supports.
The service must be registered with the following service property:
-
javax.persistence.provider
- The JPA Provider implementation class name, a documented name for all JPA Providers.
The Persistence Provider service enables a Java EE Container to find a particular JPA Provider. This service is intended for containers only, not for Client Bundles because there are implicit assumptions in the JPA Providers about the Java EE environment. A Java EE Container must obey the life cycle of the Persistence Provider service. If this service is unregistered then it must close all connections and clean up the corresponding resources.
A Persistence Unit is configured to work with a relational database. JPA Providers must communicate with a relational database through a compliant JDBC database driver. The database and driver parameters are specified with properties in the Persistence Unit or the configuration properties when a Entity Manager Factory Builder is used to build an Entity Manager Factory. All JPA Providers, regardless of version, in an OSGi environment must support the following properties for database access:
-
javax.persistence.jdbc.driver
- Fully-qualified name of the driver class. -
javax.persistence.jdbc.url
- Driver-specific URL to indicate database information -
javax.persistence.jdbc.user
- User name to use when obtaining connections -
javax.persistence.jdbc.password
- Password to use when obtaining connections
There are severe limitations in specifying these properties after the Entity Manager Factory is created for the first time, see Rebinding.
Providers must use the javax.persistence.jdbc.driver
property, as defined in JDBC Access in JPA, to obtain a Data
Source Factory service. The Data Source Factory is specified in Data Service Specification for JDBC™ Technology. The javax.persistence.jdbc.driver
property must be matched with the value of the Data Source Factory
service property named osgi.jdbc.driver.class
.
The Data Source Factory service is registered with the
osgi.jdbc.driver.class
service property that holds the
class name of the driver. This property must match the
javax.persistence.jdbc.driver
service property of the
Persistence Unit.
For example, if the Persistence Unit specifies the
com.acme.db.Driver
database driver in the
javax.persistence.jdbc.driver property
(or in the
Persistence Descriptor property
element), then the
following filter would select an appropriate Data Source
Factory:
(&(objectClass=org.osgi.service.jdbc.DataSourceFactory)
(osgi.jdbc.driver.class=com.acme.db.Driver))
Once the Data Source Factory is obtained, the JPA Provider must
obtain a DataSource
object. This Data Source object must
then be used for all relational database access.
In [1] JPA 1.0 the JPA JDBC properties were not standardized. JPA Providers typically defined a set of JDBC properties, similar to those defined in JPA 2.0, to configure JDBC driver access. JPA 1.0 JPA Providers must look up the Data Source Factory service first using the JPA 2.0 JDBC properties. If these properties are not defined then they should fall back to their proprietary driver properties.
In this specification, the Entity Manager Factory service is only registered when the Persistence Unit is complete and a matching Data Source Factory service is available. However, the API of the Entity Manager Factory Builder allows the creation of an Entity Manager Factory with configuration properties. Those configuration properties could contain the JDBC properties to bind to another Data Source Factory service than it had already selected.
This case must not be supported by a JPA Provider, an Illegal Argument Exception must be thrown. If such a case would be supported then the life cycle of the Entity Manager Factory service would still be bound to the first Data Source Factory. There would be no way for the JPA Provider to signal to the Client Bundle that the returned Entity Manager Factory is no longer valid because the rebound Data Source Factory was unregistered.
Therefore, when an Entity Manager Factory is being created using the Entity Manager Factory Builder, a JPA Provider must verify that the new properties are compatible with the properties of the already created Entity Manager Factory. If no, then an Exception must be thrown. If they are compatible, then an instance of the previous Entity Manager Factory should be returned.
JPA Providers may choose to implement the JPA specifications using various implementation approaches and techniques. This promotes innovation in the area, but also opens the door to limitations and constraints arising due to implementation choices. For example, there are JPA Providers that perform byte code weaving during the entity class loading. Dynamic byte code weaving requires that the entity classes are not loaded until the JPA Provider is first able to intercept the loading of the entity class and be given an opportunity to do its weaving. It also implies that the Persistence Bundle and any other bundles that import packages from that bundle must be refreshed if the JPA Provider needs to be changed.
This is necessary because the JPA Services are registered against the Bundle Contexts of the Persistence Bundles and not the Bundle Context of the JPA Providers. Client Bundles must then unget the service to unbind themselves from the uninstalled JPA Provider. However, since most JPA Providers perform some kind of weaving or class transformation on the entity classes, the Persistence Bundle will likely need to be refreshed. This will cause the Client Bundles to be refreshed also because they depend on the packages of the entity classes.
JPA Providers cannot have package dependencies on entity classes
in Persistence Bundles because they cannot know at install time what
Persistence Bundles they will be servicing. However, when a JPA Provider
is servicing a Persistence Bundle, it must be able to load classes and
resources from that Persistence Bundle according to the OSGi bundle
rules. To do this class loading it must obtain a class loader that has
the same visibility as the Persistence Bundle's bundle class loader.
This will also allow it to load and manage metadata for the entity
classes and resources for that Persistence Bundle's assigned Persistence
Units. These resources and entity classes must reside directly in the
Persistence Bundle, they must be accessed using the
getEntry
method. Entity classes and resources must not
reside in fragments.
Non-managed client usage of JPA has traditionally been achieved
through the Persistence
class. Invoking a static method on
the Persistence
class is a dependency on the returned JPA
Provider that cannot be managed by the OSGi framework.
However, such an unmanaged dependency is supported in this specification by the Static Persistence bundle. This bundle provides backwards compatibility for programs that use existing JPA access patterns. However, usage of this static model requires that the deployer ensures that the actors needed are in place at the appropriate times by controlling the life cycles of all participating bundles. The normal OSGi safe-guards and dependency handling do not work in the case of static access.
A Static Persistence Bundle must provide static access from the
Persistence
class to the JPA Services.
There are two methods on the Persistence
class:
-
createEntityManagerFactory(String)
-
createEntityManagerFactory(String,Map)
Both methods take the name of a Persistence Unit. The last method
also takes a map that contains extra configuration properties. To
support the usage of the static methods on the Persistence
class, the implementation of the
Persistence.createEntityManagerFactory
method family must
do a lookup of one of the JPA Services associated with the selected
Persistence Unit.
If no configuration properties are specified, the Static
Persistence Bundle must look for an Entity Manager Factory service with
the osgi.unit.name
property set to the given name. The
default service should be used because no selector for a version is
provided. If no such service is available, null
must be
returned. Provisioning of multiple versioned Persistence Units is not
supported. Deployers should ensure only a single version of a
Persistence Unit with the same name is present in an OSGi framework at
any moment in time.
Otherwise, if configuration properties are provided, the Static
Access implementation must look for an Entity Manager Factory Builder
service with the osgi.unit.name
property set to the given
Persistence Unit name. If no such service exists, null
must
be returned. Otherwise, the default service must be used to create an
Entity Manager Factory with the given configuration properties. The
result must be returned to the caller.
For service lookups, the Static Persistence Bundle must use its own Bundle Context, it must not attempt to use the Bundle Context of the caller. All exceptions should be passed to the caller.
The class space of the Entity Manager Factory and the class space
of the client cannot be enforced to be consistent by the framework
because it is the Persistence
class that is doing the
lookup of the service, and not the actual calling Client Bundle that
will be using the Entity Manager Factory. The framework cannot make the
connection and therefore cannot enforce that the class spaces
correspond. Deployers should therefore ensure that the involved class
spaces are correctly wired.
The JPA Service Implementation must supply a number of capabilities for use by client bundles and Deployers.
A JPA Service implementation must provide an extender which finds
and extends persistence bundles. The bundle providing this extender must
provide a capability in the
osgi.extender
namespace declaring an extender with
the name JPA_CAPABILITY_NAME. This capability must also declare a uses
constraint for the org.osgi.service.jpa
and
javax.persistence
packages. For example:
Provide-Capability: osgi.extender;
osgi.extender="osgi.jpa";
version:Version="1.1";
uses:="org.osgi.service.jpa,javax.persistence"
This capability must follow the rules defined for the osgi.extender Namespace.
All persistence bundles should require the
osgi.extender
capability from the JPA Service. This
requirement will wire the persistence bundle to the JPA Service
implementation and ensure that the JPA service is using the same API
packages as the persistence bundle.
Require-Capability: osgi.extender;
filter:="(&(osgi.extender=osgi.jpa)(version>=1.1)(!(version>=2.0)))"
This requirement can be easily generated using the RequireJPAExtender annotation.
The JPA extender must only process a persistence bundle's persistence units if the following is true:
-
The bundle's wiring has a required wire for at least one
osgi.extender
capability with the nameosgi.jpa
and the first of these required wires is wired to the JPA extender. -
The bundle's wiring has no required wire for an
osgi.extender
capability with the nameosgi.jpa
.
Otherwise, the JPA Service extender must not process the persistence bundle
Previous versions of this specification recommended that the JPA API packages were versioned using the OSGi recommended semantic versioning policy. Whilst this would have been an excellent way to ensure compatibility between JPA persistence bundles, client bundles, and JPA providers, in practice few bundles followed this versioning policy. As a result the various actors in the JPA service can easily be created with have clashing version ranges.
This problem is not isolated to JPA, and so a general solution was
created called [5] Portable Java Contract Definitions. These define a
capability namespace called osgi.contract
In order to permit JPA clients to reliably work when paired with newer versions of JPA there needs to be a defined contract upon which the clients and persistence units can rely, otherwise a JPA 1.0 compatible client cannot declare a dependency which also accepts the backward compatible JPA 2.0 API. For JPA the following three contracts exist:
osgi.contract;osgi.contract=JavaJPA;version:Version=1;
uses:="javax.persistence,javax.persistence.spi"
osgi.contract;osgi.contract=JavaJPA;version:Version=2;
uses:="javax.persistence,javax.persistence.criteria,
javax.persistence.metamodel,javax.persistence.spi"
osgi.contract;osgi.contract=JavaJPA;version:Version=2.1;
uses:="javax.persistence,javax.persistence.criteria,
javax.persistence.metamodel,javax.persistence.spi"
JPA API providers must declare the full set of API contract versions with which they are compatible. As JPA API versions are backward compatible this will typically result in the provider exposing all versions of a contract. Note that when a provider offers multiple versions of a contract then all of the contract versions must be offered by a single capability. For example:
Export-Package: javax.persistence,javax.persistence.criteria,
javax.persistence.metamodel,javax.persistence.spi
Provide-Capability: osgi.contract;osgi.contract=JavaJPA;
version:List>Version<="2.1,2,1"; uses:="javax.persistence,
javax.persistence.criteria,javax.persistence.metamodel,javax.persistence.spi"
The contract capability means that clients can safely import the API using the contract and no import version. For example:
Import-Package: javax.persistence,javax.persistence.criteria
Require-Capability: osgi.contract;
filter:="(&(osgi.contract=JavaJPA)(version=2.1))"
The JPA Service implementation is responsible for registering both
an EntityManagerFactoryBuilder
service and a
EntityManagerFactory
service on behalf of the persistence
bundle. The persistence bundle should therefore provide two capabilities
in the osgi.service
namespace, one representing the EntityManagerFactoryBuilder service, and another representing the
javax.persistence.EntityManagerFactory
service. These
capabilities must also declare uses constraints for the packages that
they expose. For example:
Provide-Capability: osgi.service;
objectClass:List<String>=
"org.osgi.service.jpa.EntityManagerFactoryBuilder";
uses:="org.osgi.service.jpa",
osgi.service;objectClass:List<String>=
"javax.persistence.EntityManagerFactory";
uses:="javax.persistence"
This capability must follow the rules defined for the osgi.service Namespace.
When Java permissions are enabled, the JPA service must perform the following security procedures.
The JPA service is built upon the existing OSGi service
infrastructure. This means that Service Permission applies regarding the
ability to publish services. A persistence bundle therefore must have
ServicePermission[<interface>, REGISTER]
for both the
EntityManagerFactory
and
EntityManagerFactoryBuilder
services.
If a persistence bundle specifies a complete persistence unit then
the persistence bundle must either have
ServicePermission[<org.osgi.service.jdbc.DataSourceFactory>,
GET]
, or be able to directly load the configured database
driver.
Client bundles that wish to configure a persistence unit using the
EntityManagerFactoryBuilder
service must have
ServicePermission[<org.osgi.service.jpa.EntityManagerFactoryBuilder>,
GET]
. Furthermore, if this service is used to configure an
incomplete persistence unit with a database driver name then it is the
getter of the EntityManagerFactoryBuilder
service whose
permissions must be checked when obtaining the
DataSourceFactory
service. If the caller of the
EntityManagerFactory Builder passes a ready constructed database
Driver
or DataSource
then no permission check
is required.
JPA Package Version 1.1.
Bundles wishing to use this package must list the package in the Import-Package header of the bundle's manifest. This package has two types of users: the consumers that use the API in this package and the providers that implement the API in this package.
Example import for consumers using the API in this package:
Import-Package: org.osgi.service.jpa; version="[1.1,2.0)"
Example import for providers implementing the API in this package:
Import-Package: org.osgi.service.jpa; version="[1.1,1.2)"
-
EntityManagerFactoryBuilder
- This service interface offers JPA clients the ability to create instances of EntityManagerFactory for a given named persistence unit.
This service interface offers JPA clients the ability to create instances of EntityManagerFactory for a given named persistence unit. A service instance will be created for each named persistence unit and can be filtered by comparing the value of the osgi.unit.name property containing the persistence unit name. This service is used specifically when the caller wants to pass in factory-scoped properties as arguments. If no properties are being used in the creation of the EntityManagerFactory then the basic EntityManagerFactory service should be used.
Consumers of this API must not implement this type
The name of the JPA extender capability.
1.1
The version of the extender capability for the JPA Service specification
1.1
The name of the persistence unit.
The class name of the provider that registered the service and implements the JPA javax.persistence.PersistenceProvider interface.
The version of the persistence unit bundle.
Properties to be used, in addition to those in the persistence descriptor, for configuring the EntityManagerFactory for the persistence unit.
Return an EntityManagerFactory instance configured according to the properties defined in the corresponding persistence descriptor, as well as the properties passed into the method.
An EntityManagerFactory for the persistence unit associated with this service. Must not be null.
This method returns the Bundle which provides the PersistenceProvider implementation that is used by this EntityManagerFactoryBuilder.
If the PersistenceProvider is provided as an OSGi service then this method must return the bundle which registered the service. Otherwise this method must return the bundle which loaded the PersistenceProvider implementation class.
The Bundle which provides the PersistenceProvider implementation used by this EntityManagerFactoryBuilder.
1.1
This method returns the name of the PersistenceProvider implementation that is used by this EntityManagerFactoryBuilder. The returned value will be the same as the value of the JPA_UNIT_PROVIDER service property.
the name of the PersistenceProvider implementation
1.1
JPA Service Annotations Package Version 1.1.
This package contains annotations that can be used to require the JPA Service implementation.
Bundles should not normally need to import this package as the annotations are only used at build-time.
-
RequireJPAExtender
- This annotation can be used to require the JPA extender.
[1]JPA 1.0https://jcp.org/en/jsr/detail?id=220
[2]JPA 2.0https://jcp.org/en/jsr/summary?id=317
[3]JPA 2.1https://jcp.org/en/jsr/summary?id=317
[5]Portable Java Contract Definitionshttps://docs.osgi.org/reference/portable-java-contracts.html