JPA : Usage of DataNucleus within a JavaEE environment

JPA is designed to allow easy deployment into a JavaEE container. The JavaEE container takes care of integration of the JPA implementation (DataNucleus), so there is no JCA connector required.

Key points to remember when deploying your JPA application to use DataNucleus under JavaEE

  • Define a JTA datasource for your persistence operations
  • Define a non-JTA datasource for your schema and sequence operations. These are cross-EntityManager and so need their own datasource that is not affected by transactions.

Individual guides for specific JavaEE servers are listed below. If you have a guide for some other server, please notify us via the DataNucleus forum and it will be added to this list.

JBoss AS7

This guide was provided by Nicolas Seyvet.

JBoss AS7 is the latest JavaEE server from JBoss. Despite searching in multiple locations, I could not find a comprehensive guide on how to switch from the default JBoss Hibernate JPA provider to Datanucleus 3. If you try this guide, please PM the author (or add a comment) and let me know how it worked out. Your feedback will be used to improve this guide. This guide is cross-referenced as part of the JBoss JPA Reference Guide.

Download JBoss AS 7 and DataNucleus 3.2+

  • JBoss : At the time I am writing this "How To", the latest JBoss AS available from the main JBoss community site is 7.1.1.Final aka Brontes. In this guide, the latest 7.x SNAPSHOT was used but the steps will work with any JBoss 7.x version.
  • DataNucleus : Version 3.2.0 was used, from SourceForge.

Install JBoss AS 7

Install JBoss AS 7 by unzipping the downloaded JBoss zip file in the wanted folder to be used as the JBoss home root folder (example: /local/jboss). From this point, the path where JBoss is unzipped will be referred to as *$JBOSS_HOME*.

Note: JBoss AS 7 configuration is controlled by either standalone.xml ($JBOSS_HOME/standalone/configuration) or domain.xml ($JBOSS_HOME/domain/configuration) depending on the operation mode (standalone or domain) of the application server. The domain mode is typically used for cases where the AS is deployed in a cluster environment. In this tutorial, a single AS instance is used, as such, the standalone mode is selected and all configuration changes will be applied to the "standalone.xlm" file.

Start JBoss

To start the server, use:

On Linux:

$ cd $JBOSS_HOME/bin/
$ ./standalone.sh

On Windows:

$ cd $JBOSS_HOME/bin/
$ standalone.bat

After a few seconds, a message should indicate the server is started.

17:23:00,251 INFO  [org.jboss.as] (Controller Boot Thread) JBAS015874: JBoss AS 7.2.0.Alpha1-SNAPSHOT "Steropes" started
 in 3717ms - Started 198 of 257 services (56 services are passive or on-demand)

To verify, access the administration GUI located at http://localhost:9990/, and expect to see a "Welcome to AS 7" banner. On the first start up, a console will show that an admin user must first be created in order to be able to access the management UI. Follow the steps and create a user.

On Linux:

$JBOSS_HOME/bin$ add-user.sh


On Windows:

$JBOSS_HOME/bin$ add-user.bat

Add a JDBC DataSource (Optional)

This step is only necessary if an RDBMS solution is used as a data store, or if external drivers are required. This tutorial will use MySQL as the RDBMS storage, and the required drivers and data source will be added. For more information, about data sources under JBoss AS 7, refer to the JBoss docs

Add MySQL drivers

For MySQL, it is recommended to use Connector/J, which can be found here. Note that this tutorial uses version 5.1.20. Note: JBoss uses OSGI to define a set of modules, further info about class loading in JBoss. In short, the configuration files binds the services and the modules, defining what is available in the class loader for a specific service or application.

While dropping the drivers in the $JBOSS_HOME/standalone/deployments directory works, this approach is not recommended. The proper approach is to add the drivers by defining a new module containing the required libraries. The full instructions are available under here.

Short walk through for MySQL:

  • Get the drivers
  • create a "mysql" directory under $JBOSS_HOME/modules/com/
  • create a "main" directory under $JBOSS_HOME/modules/com/mysql
  • Copy the "mysql-connector-java-5.1.20-bin.jar" drivers under $JBOSS_HOME/modules/com/mysql/main
  • Add a "module.xml" file under $JBOSS_HOME/modules/com/mysql/main
    <?xml version="1.0" encoding="UTF-8"?>
    <module xmlns="urn:jboss:module:1.0" name="com.mysql">
        <resources>
            <resource-root path="mysql-connector-java-5.1.20-bin.jar"/>
        </resources>
        <dependencies>
            <module name="javax.api"/>
        </dependencies>
    </module>
    

The name is important as it defines the module name and is used in the "standalone.xml" configuration file. Now, let's say the URL to the MySQL database to be used is "jdbc:mysql://localhost:3306/simple", there are three ways to add that to the server, either through the management console at localhost or, by modifying the "standalone.xml" configuration file, or by using the Command Line Interface (CLI).

Let's modify the "standalone.xml" file. Verify the AS is stopped. Open "standalone.xml" for editing. Search for "subsystem xmlns="urn:jboss:domain:datasources:1.1", the section defines data sources and driver references. Let's add our data source and drivers. Add the following in the datasources section:

<datasource jndi-name="java:/jdbc/simple" pool-name="MySQL-DS" enabled="true">
    <connection-url>jdbc:mysql://localhost:3306/simple</connection-url>
      <driver>com.mysql</driver>
      <transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
      <pool>
        <min-pool-size>10</min-pool-size>
        <max-pool-size>100</max-pool-size>
        <prefill>true</prefill>
      </pool>
      <security>
        <user-name>[A valid DB user name]</user-name>
        <password>[A valid DB password]</password>
      </security>
      <statement>
        <prepared-statement-cache-size>32</prepared-statement-cache-size>
        <share-prepared-statements>true</share-prepared-statements>
      </statement>
  </datasource>
  <datasource jta="false" jndi-name="java:/jdbc/simple-nonjta" pool-name="MySQL-DS-NonJTA" enabled="true">
    <connection-url>jdbc:mysql://localhost:3306/simple</connection-url>
      <driver>com.mysql</driver>
      <transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
      <security>
        <user-name>[A valid DB user name]</user-name>
        <password>[A valid DB password]</password>
      </security>
      <statement>
        <share-prepared-statements>false</share-prepared-statements>
      </statement>
  </datasource>

The above defines two data sources (MySQL-DS and MySQL-DS-NonJTA) referring to the same database. The difference between the two is that MySQL-DS has JTA enabled while MySQL-DS-NonJTA does not. This is useful to separate operations during the database automated schema generation phase. Any change to a schema should be made outside the scope of JTA. Many JDBC drivers (for example) will fall apart (assorted type of SQLException) if you try to commit a connection with DDL and SQL mixed, or SQL first then DDL after. Consequently it is recommended to have a separate data source for such operations, hence using the non-jta-data-source.

In the drivers section, add:

<driver name="com.mysql" module="com.mysql">
    <xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
  </driver>

The above defines which drivers to use for the data sources MySQL-DS and MySQL-DS-NonJTA. More info is available as part of the JBoss documentation, refer to the section describing how to setup a new data source.

Add DataNucleus to JBoss

This step adds the DataNucleus libraries as a JBoss module.

  • Create a directory to store the DataNucleus libraries, as $JBOSS_HOME/modules/org/datanucleus/main
  • Add the following jars from the lib directory of the datanucleus-accessplatform-full-deps ZIP file lib directory : datanucleus-api-jpa-XXX.jar, datanucleus-core-XXX.jar, datanucleus-rdbms-XXX.jar, datanucleus-jpa-query-XXX.jar, and jdo-api-3.1-rc1.jar
  • Add a "module.xml" file in the $JBOSS_HOME/modules/org/datanucleus/main directory like this
    <module xmlns="urn:jboss:module:1.1" name="org.datanucleus">
        <dependencies>
            <module name="javax.api"/>
            <module name="javax.persistence.api"/>
            <module name="javax.transaction.api"/>
            <module name="javax.validation.api"/>
        </dependencies>
        <resources>
            <resource-root path="datanucleus-api-jpa-3.2.7.jar"/>
            <resource-root path="datanucleus-core-3.2.11.jar"/>
            <resource-root path="datanucleus-rdbms-3.2.10.jar"/>
            <resource-root path="datanucleus-jpa-query-3.0.3.jar"/>
            <resource-root path="jdo-api-3.1-rc1.jar"/>
        </resources>
    </module>
    

At this point, all the JPA dependencies are resolved.

A simple example using DataNucleus JPA and JBoss AS7

Now you simply need to define persistence.xml and use JPA as you normally would. In order to use DataNucleus as a persistence provider, the "persistence.xml" file must contain the "jboss.as.jpa.providerModule" property. Using the datasources defined above, an example of a "persistence.xml" file could be:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
    <persistence-unit name="[Persistence Unit Name]" transaction-type="JTA">
        <provider>org.datanucleus.api.jpa.PersistenceProviderImpl</provider>
        <!-- MySQL DS -->
        <jta-data-source>java:/jdbc/simple</jta-data-source>
        <non-jta-data-source>java:/jdbc/simple-nonjta</non-jta-data-source>

        <class>[Entities must be listed here]</class>

        <properties>
            <!-- Magic JBoss property for specifying the persistence provider -->
            <property name="jboss.as.jpa.providerModule" value="org.datanucleus"/>

            <!-- following is probably not useful... but it ensures we bind to the JTA transaction manager...-->
            <property name="datanucleus.jtaLocator" value="custom_jndi"/>
            <property name="datanucleus.jtaJndiLocation" value="java:/TransactionManager"/>

            <property name="datanucleus.autoCreateSchema" value="true"/>
            <property name="datanucleus.metadata.validate" value="false"/>
            <property name="datanucleus.validateTables" value="false"/>
            <property name="datanucleus.validateConstraints" value="false"/>
        </properties>
    </persistence-unit>
</persistence>