JDO : PersistenceManagerFactory

Any JDO-enabled application will require at least one PersistenceManagerFactory (PMF). Typically applications create one per datastore being utilised. A PersistenceManagerFactory provides access to PersistenceManagers which allow objects to be persisted, and retrieved. The PersistenceManagerFactory can be configured to provide particular behaviour.

Important : A PersistenceManagerFactory is designed to be thread-safe. A PersistenceManager is not

There are many ways of creating a PersistenceManagerFactory .

Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass", "org.datanucleus.api.jdo.JDOPersistenceManagerFactory");
properties.setProperty("javax.jdo.option.ConnectionURL","jdbc:mysql://localhost/myDB");
properties.setProperty("javax.jdo.option.ConnectionDriverName","com.mysql.jdbc.Driver");
properties.setProperty("javax.jdo.option.ConnectionUserName","login");
properties.setProperty("javax.jdo.option.ConnectionPassword","password");
PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory(properties);

A slight variation on this, is to have a file to specify these properties like this

javax.jdo.PersistenceManagerFactoryClass=org.datanucleus.api.jdo.JDOPersistenceManagerFactory
javax.jdo.option.ConnectionURL=jdbc:mysql://localhost/myDB
javax.jdo.option.ConnectionDriverName=com.mysql.jdbc.Driver
javax.jdo.option.ConnectionUserName=login
javax.jdo.option.ConnectionPassword=password

and then to create the PersistenceManagerFactory using this file

File propsFile = new File(filename);
PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory(propsFile);

or if the above file is in the CLASSPATH (at "datanucleus.properties" in the root of the CLASSPATH), then

PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory("datanucleus.properties");

If using a named PMF file, you can create the PMF by providing the name of the PMF like this

PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory("myNamedPMF");

If using a META-INF/persistence.xml file, you can simply specify the persistence-unit name as

PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory("myPersistenceUnit");

Another alternative, when specifying your datastore via JNDI, would be to call JDOHelper.getPersistenceManagerFactory(jndiLocation, context);, and then set the other persistence properties on the received PMF.

Whichever way we wish to obtain the PersistenceManagerFactory we have defined a series of properties to give the behaviour of the PersistenceManagerFactory. The first property specifies to use the DataNucleus implementation, and the following 4 properties define the datastore that it should connect to. There are many properties available. Some of these are standard JDO properties, and some are DataNucleus extensions.


Specifying the datastore properties

With JDO you have 3 ways of specifying the datastore via persistence properties

  • Specify the connection URL/driverName/userName/password and it will internally create a DataSource for this URL (with optional connection pooling). This is achieved by specifying javax.jdo.option.ConnectionDriverName, javax.jdo.option.ConnectionURL, javax.jdo.option.ConnectionUserName, and javax.jdo.option.ConnectionPassword
  • Specify the JNDI name of the connectionFactory This is achieved by specifying javax.jdo.option.ConnectionFactoryName, and javax.jdo.option.ConnectionFactory2Name (for secondary operations)
  • Specify the DataSource of the connectionFactory This is achieved by specifying javax.jdo.option.ConnectionFactory, and javax.jdo.option.ConnectionFactory2 (for secondary operations)

JDO Persistence Properties

Name Values Description
javax.jdo.PersistenceManagerFactoryClass The name of the PMF implementation. org.datanucleus.api.jdo.JDOPersistenceManagerFactory Only required if you have more than one JDO implementation in the CLASSPATH
javax.jdo.option.ConnectionFactory Alias for datanucleus.ConnectionFactory. Only for RDBMS
javax.jdo.option.ConnectionFactory2 Alias for datanucleus.ConnectionFactory2. Only for RDBMS
javax.jdo.option.ConnectionFactoryName Alias for datanucleus.ConnectionFactoryName. Only for RDBMS
javax.jdo.option.ConnectionFactory2Name Alias for datanucleus.ConnectionFactory2Name. Only for RDBMS
javax.jdo.option.ConnectionDriverName Alias for datanucleus.ConnectionDriverName
javax.jdo.option.ConnectionURL Alias for datanucleus.ConnectionURL
javax.jdo.option.ConnectionUserName Alias for datanucleus.ConnectionUserName
javax.jdo.option.ConnectionPassword Alias for datanucleus.ConnectionPassword
javax.jdo.option.IgnoreCache true | false Alias for datanucleus.IgnoreCache
javax.jdo.option.Multithreaded true | false Alias for datanucleus.Multithreaded
javax.jdo.option.NontransactionalRead true | false Alias for datanucleus.NontransactionalRead
javax.jdo.option.NontransactionalWrite true | false Alias for datanucleus.NontransactionalWrite
javax.jdo.option.Optimistic true | false Alias for datanucleus.Optimistic
javax.jdo.option.RetainValues true | false Alias for datanucleus.RetainValues
javax.jdo.option.RestoreValues true | false Alias for datanucleus.RestoreValues
javax.jdo.option.DetachAllOnCommit true | false Alias for datanucleus.DetachAllOnCommit
javax.jdo.option.CopyOnAttach true | false Alias for datanucleus.CopyOnAttach
javax.jdo.option.TransactionType Alias for datanucleus.TransactionType
javax.jdo.option.PersistenceUnitName Alias for datanucleus.PersistenceUnitName
javax.jdo.option.ServerTimeZoneID Alias for datanucleus.ServerTimeZoneID
javax.jdo.option.Name Name of the named PMF to use. Refers to a PMF defined in "META-INF/jdoconfig.xml".
javax.jdo.option.ReadOnly true | false Alias for datanucleus.readOnlyDatastore
javax.jdo.option.TransactionIsolationLevel Alias for datanucleus.transactionIsolation
javax.jdo.option.DatastoreReadTimeoutMillis Alias for datanucleus.datastoreReadTimeout
javax.jdo.option.DatastoreWriteTimeoutMillis Alias for datanucleus.datastoreWriteTimeout
javax.jdo.option.Mapping Alias for datanucleus.Mapping Only for datastores with a "schema"
javax.jdo.mapping.Catalog Alias for datanucleus.Catalog Only for datastores with a "schema"
javax.jdo.mapping.Schema Alias for datanucleus.Schema Only for datastores with a "schema"

DataNucleus provides many properties to extend the control that JDO gives you. These can be used alongside the above standard JDO properties, but will only work with DataNucleus. Please consult the Persistence Properties Guide for full details.

PersistenceManagerFactory for Persistence-Unit

When designing an application you can usually nicely separate your persistable objects into independent groupings that can be treated separately, perhaps within a different DAO object, if using DAOs. JDO uses the (JPA) idea of a persistence-unit. A persistence-unit provides a convenient way of specifying a set of metadata files, and classes, and jars that contain all classes to be persisted in a grouping. The persistence-unit is named, and the name is used for identifying it. Consequently this name can then be used when defining what classes are to be enhanced, for example.

To define a persistence-unit you first need to add a file persistence.xml to the META-INF/ directory of the CLASSPATH (this may mean WEB-INF/classes/META-INF when using a web-application in such as Tomcat). This file will be used to define your persistence-units. Lets show an example

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
        http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd" version="2.1">

    <!-- Online Store -->
    <persistence-unit name="OnlineStore">
        <class>org.datanucleus.samples.metadata.store.Product</class>
        <class>org.datanucleus.samples.metadata.store.Book</class>
        <class>org.datanucleus.samples.metadata.store.CompactDisc</class>
        <class>org.datanucleus.samples.metadata.store.Customer</class>
        <class>org.datanucleus.samples.metadata.store.Supplier</class>
        <exclude-unlisted-classes/>
        <properties>
            <property name="datanucleus.ConnectionDriverName" value="org.h2.Driver"/>
            <property name="datanucleus.ConnectionURL" value="jdbc:h2:mem:datanucleus"/>
            <property name="datanucleus.ConnectionUserName" value="sa"/>
            <property name="datanucleus.ConnectionPassword" value=""/>
        </properties>
    </persistence-unit>

    <!-- Accounting -->
    <persistence-unit name="Accounting">
        <mapping-file>/com/datanucleus/samples/metadata/accounts/package.jdo</mapping-file>
        <properties>
            <property name="datanucleus.ConnectionDriverName" value="org.h2.Driver"/>
            <property name="datanucleus.ConnectionURL" value="jdbc:h2:mem:datanucleus"/>
            <property name="datanucleus.ConnectionUserName" value="sa"/>
            <property name="datanucleus.ConnectionPassword" value=""/>
        </properties>
    </persistence-unit>

</persistence>

In this example we have defined 2 persistence-units. The first has the name "OnlineStore" and contains 5 classes (annotated). The second has the name "Accounting" and contains a metadata file called "package.jdo" in a particular package (which will define the classes being part of that unit). This means that once we have defined this we can reference these persistence-units in our persistence operations. You can find the XSD for persistence.xml here.

There are several sub-elements of this persistence.xml file

  • provider - Not used by JDO
  • jta-data-source - JNDI name for JTA connections (make sure you set transaction-type as JTA on the persistence-unit for this). You can alternatively specify JDO standard javax.jdo.option.ConnectionFactoryName to the same end.
  • non-jta-data-source - JNDI name for non-JTA connections. You can alternatively specify JDO standard javax.jdo.option.ConnectionFactory2Name to the same end.
  • jar-file - name of a JAR file to scan for annotated classes to include in this persistence-unit.
  • mapping-file - name of an XML "mapping" file containing persistence information to be included in this persistence-unit. This is the JDO XML Metadata file (package.jdo) (not the ORM XML Metadata file)
  • class - name of an annotated class to include in this persistence-unit
  • properties - properties defining the persistence factory to be used.
  • exclude-unlisted-classes - when this is specified then it will only load metadata for the classes/mapping files listed.

Use with JDO

JDO accepts the "persistence-unit" name to be specified when creating the PersistenceManagerFactory, like this

PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory("MyPersistenceUnit");

Metadata loading using persistence unit

When you specify a PMF using a persistence.xml it will load the metadata for all classes that are specified directly in the persistence unit, as well as all classes defined in JDO XML metadata files that are specified directly in the persistence unit. If you don't have the exclude-unlisted-classes set to true then it will also do a CLASSPATH scan to try to find any other annotated classes that are part of that persistence unit. To set the CLASSPATH scanner to a custom version use the persistence property datanucleus.metadata.scanner and set it to the classname of the scanner class.


Dynamically generated Persistence-Unit

DataNucleus allows an extension to JDO to dynamically create persistence-units at runtime. Use the following code sample as a guide. Obviously any classes defined in the persistence-unit need to have been enhanced.

import org.datanucleus.metadata.PersistenceUnitMetaData;
import org.datanucleus.api.jdo.JDOPersistenceManagerFactory;
 
PersistenceUnitMetaData pumd = new PersistenceUnitMetaData("dynamic-unit", "RESOURCE_LOCAL", null);
pumd.addClassName("org.datanucleus.test.A");
pumd.setExcludeUnlistedClasses();
pumd.addProperty("javax.jdo.ConnectionURL", "jdbc:hsqldb:mem:nucleus");
pumd.addProperty("javax.jdo.ConnectionDriverName", "org.hsqldb.jdbcDriver");
pumd.addProperty("javax.jdo.ConnectionUserName", "sa");
pumd.addProperty("javax.jdo.ConnectionPassword", "");
pumd.addProperty("datanucleus.schema.autoCreateAll", "true");

PersistenceManagerFactory pmf = new JDOPersistenceManagerFactory(pumd, null);

It should be noted that if you call pumd.toString(); then this returns the text that would have been found in a persistence.xml file.

Named PersistenceManagerFactory

Typically applications create one PMF per datastore being utilised. An alternate to persistence-unit is to use a named PMF, defined in a file META-INF/jdoconfig.xml at the root of the CLASSPATH (this may mean WEB-INF/classes/META-INF when using a web-application). Let's see an example of a jdoconfig.xml

<?xml version="1.0" encoding="utf-8"?>
<jdoconfig xmlns="http://xmlns.jcp.org/xml/ns/jdo/jdoconfig"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/jdo/jdoconfig
        http://xmlns.jcp.org/xml/ns/jdo/jdoconfig_3_2.xsd" version="3.2">

    <!-- Datastore Txn PMF -->
    <persistence-manager-factory name="Datastore">
        <property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/>
        <property name="javax.jdo.option.ConnectionURL" value="jdbc:mysql://localhost/datanucleus?useServerPrepStmts=false"/>
        <property name="javax.jdo.option.ConnectionDriverName" value="com.mysql.jdbc.Driver"/>
        <property name="javax.jdo.option.ConnectionUserName" value="datanucleus"/>
        <property name="javax.jdo.option.ConnectionPassword" value=""/>
        <property name="javax.jdo.option.Optimistic" value="false"/>
        <property name="datanucleus.schema.autoCreateAll" value="true"/>
    </persistence-manager-factory>

    <!-- Optimistic Txn PMF -->
    <persistence-manager-factory name="Optimistic">
        <property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/>
        <property name="javax.jdo.option.ConnectionURL" value="jdbc:mysql://localhost/datanucleus?useServerPrepStmts=false"/>
        <property name="javax.jdo.option.ConnectionDriverName" value="com.mysql.jdbc.Driver"/>
        <property name="javax.jdo.option.ConnectionUserName" value="datanucleus"/>
        <property name="javax.jdo.option.ConnectionPassword" value=""/>
        <property name="javax.jdo.option.Optimistic" value="true"/>
        <property name="datanucleus.schema.autoCreateAll" value="true"/>
    </persistence-manager-factory>

</jdoconfig>

So in this example we have 2 named PMFs. The first is known by the name "Datastore" and utilises datastore transactions. The second is known by the name "Optimistic" and utilises optimistic transactions. You simply define all properties for the particular PMF within its specification block. And finally we instantiate our PMF like this

PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory("Optimistic");

That's it. The PMF we are returned from JDOHelper will have all of the properties defined in META-INF/jdoconfig.xml under the name of "Optimistic".