Community

Using Java Persistence API(JPA) in bluemix with EJBs

Dave Cohen / Advisory Software Engineer / IBM Cloud OE Runtime Test


 
This article will demonstrate how to deploy  a typical JPA application into the BlueMix cloud.
It makes use of a  JPA Sample Application, bound to a db2  database, driven by either EJBs or Servlets.  

  • JPA (Java Persistence API) is a set of APIs that is used for reading and writing data to databases via Java objects.  
  • An EJB (Enterprise Java Bean) is a server side Java object that contains business logic with special qualities  of service (collaborators) such as transactions and security.  

The article provides code snippets for EJBs and JPA, and a sample application.  It explains how to push this application to BlueMix, making use of the auto-config support to connect to a DB2 database. Auto-config is a feature of the BlueMix runtime that facilitates the connection of an application to a back end resource, such as a database, shielding the customer from the complexity of configuring resource XML stanzas or even creating the database itself.

Overview

1. Importing the Sample into Eclipse
2. EJBs

3. JPA
4. Persistence Units, persistence.xml and mapping to the database for JPA

5. Explanation of Auto-config
6. Pushing the standalone war file
7. Running the Application


1. Importing the Sample into Eclipse

Next if you want to examine the source and run the sample application yourself, you can get it from here: <a href="https://hub.jazz.net/project/dacohen01/EJB%20BlueMix%20Sample/overview" title="JPA Blue Mix Project" target="_blank"><span style="text-decoration: underline;">JPABlueMixProject</span></a> <br />Read instructions in the README.MD file to get the project set up with the CF plugin in Eclipse.<br /><br /><strong style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 14px; line-height: 1.5em;"><strong><strong><strong><strong><em></em></strong></strong></strong></strong></strong>

2. EJBs (Enterprise Java Beans)
The sample uses annotations to create callable handles to EJBs that are invoked from servlets.
Annotations (e.g. @EJB ListDBEJB listEJB; ) simplify the process of creating these callable handles to EJBs by just
specifying the EJB class name (e.g. ListDBEJB) followed by the instance name (e.g. listEJB):

Code in servlet that calls the EJB

@EJB ListDBEJB listEJB;<br /><br />OutBean output = null;    //output object to receive results from EJB call<br />InBean input = new InBean(); // create/instantiate a new input object<br /><br />input.setSql(Boolean.parseBoolean(request.getParameter("isMySQL")));  // set the DB type into<br />                                                                      // input object<br />output = listEJB.list(input);  // call the ejb and get the results in output instance


EJB definition in the EJB’s java file containing your business logic:

  @Stateless     // annotation for stateless session EJB bean
    @LocalBean     // local ejb, can only be called from within same JVM, as per ejbLite feature
    public class ListDBEJB { 
        //  your methods and business logic here
    }


3. JPA

The PopulateDB function (via EJB or servlet) creates a database table  using native SQL:

q = em.createNativeQuery("CREATE TABLE  CUSTOMERACCT ( C_NUM  SMALLINT
NOT NULL  ,  C_NAME  CHARACTER(30) NOT NULL  ,  C_MONEY DECIMAL(12,2)
NOT NULL,PRIMARY KEY ( C_NUM ) )");


and then populates the rows of the table using standard JPA APIs:

    // set values into the CustomerAcct java object("ca") and then persist them into the CustomerAcct database table<br />        for (short j = 0; j &lt; 8; j++) {
       ca = new CustomerAcct();
       ca.setCustomerAcct((short) (j + 1));
       ca.setCustomerMoney(BigDecimal.valueOf(money[j]));
       ca.setCustomerName(name[j]);
       em.persist(ca);
    }
    em.close();

The ListDB functions lists out the customers in the database table using a named query (query invoked from a servlet or EJB, but  the query itself is defined in the JPA entity):

   EntityManager em = emfToUse.createEntityManager();  // get an entity manager handle

    OpenJPAQuery q = OpenJPAPersistence.cast(em
            .createNamedQuery("listCustomers"));   // run the named query

    Collection coll = null;
    coll = q.getResultList();    // gather up the result set into a java collection

    if (coll != null) {          // if we have results
        Iterator it = coll.iterator();    // get the collection into a java iterator
        CustomerAcct[] cas = new CustomerAcct[8];   // set up an array for the results
        int i = 0;
        CustomerAcct ca = null;
        while (it.hasNext()) {     //iterate through the results of the query
            ca = (CustomerAcct) it.next();   // get the next result 
            System.out.println("customer acct from result set: "+ca);
            cas[i] = ca;
            i++;
        }
        ob.setCustomerAccts(cas); //set array of customers into output object
    }

Above query as defined in the CustomerAcct JPA Entity:

@Entity
@Table(name="CUSTOMERACCT")
@NamedQuery(
        name="listCustomers",
        query="select c from CustomerAcct c"
        )

The CustomerCredit function locates a customer record and updates that customer’s balance in the CUSTOMERACCT table:

CustomerAcct customerAcct = em.find(CustomerAcct.class, customerNumber);  // find the customer record for this "customerNumber"
    double money = customerAcct.getCustomerMoney().doubleValue()+ moneyToCredit; //determine the credit $$
    customerAcct.setCustomerMoney(new BigDecimal(money));  // set new balance value<br />        em.close();<br /><br /><em></em>

4. Persistence Units, persistence.xml and mapping to the database for JPA

Persistence units specify the persistent unit names, dataSources and JPA entity name(s) necessary to access the database table.
The persistence.xml, which describes the persistence units, is under the src/META-INF folder.

The business logic of an application can inject a Persistence Unit (via annotation: @PersistenceUnit) and use it to to gain access to a database table which automatically maps that table to a JPA entity, easing accessing to the database table:

Example annotation to inject a persistent unit:
(as used in the business logic/java code of all of the EJBs and Servlets in this sample)

@PersistenceUnit(unitName = "CustomerQueryDB2") EntityManagerFactory emf;

Its also important to understand the associations that are created in the contents of the persistence.xml:

Notice the persistent unit name, dataSource name and JPA entity name

Persistence.xml:<br /><br />&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"&gt;  
    &lt;persistence-unit name="CustomerQueryDB2"&gt;
        &lt;jta-data-source&gt;jdbc/MyDataSourceDB2&lt;/jta-data-source&gt;
        &lt;class&gt;myjpa.CustomerAcct&lt;/class&gt;
        &lt;!-- exclude-unlisted-classes&gt;true&lt;/exclude-unlisted-classes --&gt;
        &lt;properties&gt;
            &lt;property name="openjpa.LockTimeout" value="30000" /&gt;
            &lt;property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(foreignKeys=true,schemaAction='drop,add')"   /&gt;
            &lt;property name="openjpa.jdbc.TransactionIsolation" value="read-committed" /&gt;
            &lt;property name="openjpa.Log" value="none" /&gt;
            &lt;property name="openjpa.jdbc.UpdateManager" value="operation-order" /&gt;
        &lt;/properties&gt;
    &lt;/persistence-unit&gt;
&lt;/persistence&gt;

The dataSource in the Persistence Unit maps to the following dataSource stanza that is generated in server.xml:<br />
&lt;dataSource id='db2-MyDataSourceDB2' jdbcDriverRef='db2-driver'
jndiName='jdbc/MyDataSourceDB2' statementCacheSize='30' transactional='true'&gt;
  &lt;properties.db2.jcc databaseName='${cloud.services.MyDataSourceDB2.connection.dbname}'
  id='db2-MyDataSourceDB2-props' password='${cloud.services.MyDataSourceDB2.connection.password}'
  portNumber='${cloud.services.MyDataSourceDB2.connection.port}'
  serverName='${cloud.services.MyDataSourceDB2.connection.host}' user='${cloud.services.MyDataSourceDB2.connection.username}' /&gt;
&lt;/dataSource&gt;<br /><br />
&lt;library id='db2-library'&gt;
&lt;fileset dir='${server.config.dir}/lib' id='db2-fileset'
  includes='db2jcc4.jar db2jcc_license_cu.jar' /&gt;
 &lt;/library&gt;

&lt;jdbcDriver id='db2-driver' libraryRef='db2-library' /&gt;

5. Explanation of Auto-config

This application makes use of the auto-config capability of IBM Cloud OE Runtime.

The autoconfig capability generates the necessary dataSource stanza in the server.xml in order
to make the DB2 service bound to the application map to the backend database.

e.g. DataSource stanza automatically generated by autoconfig support in server.xml<br /><br />
&lt;library id='db2-library'&gt;
&lt;fileset dir='${server.config.dir}/lib' id='db2-fileset'
 includes='db2jcc4.jar db2jcc_license_cu.jar' /&gt;
 &lt;/library&gt;

&lt;jdbcDriver id='db2-driver' libraryRef='db2-library' /&gt;

&lt;dataSource id='db2-MyDataSourceDB2' jdbcDriverRef='db2-driver'
jndiName='MyDataSourceDB2' statementCacheSize='30' transactional='true'&gt;
 &lt;properties.db2.jcc databaseName='${cloud.services.MyDataSourceDB2.connection.dbname}'
 id='db2-MyDataSourceDB2-props' password='${cloud.services.MyDataSourceDB2.connection.password}'
 portNumber='${cloud.services.MyDataSourceDB2.connection.port}'
 serverName='${cloud.services.MyDataSourceDB2.connection.host}' user='${cloud.services.MyDataSourceDB2.connection.username}' /&gt;
&lt;/dataSource&gt;


6. Pushing the standalone war file

If you prefer to using the command line you can get the war file: JPABlueMixProject.war  and push the sample application using CF Commands, creating and binding the required services (dataSource in this case):

cf push jpa -p JPABlueMixProject.war<br />cf create-service SQLDB SQLDB_OpenBeta MyDataSourceDB2<br />cf bind-service jpa MyDataSourceDB2<br />(note this message comes out after bind service: "TIP: Use 'cf push' to ensure your env variable <br />changes take effect" )
cf push jpa -p JPABlueMixProject.war
<br />note: the plan name (e.g. "SQLDB_OpenBeta" for DB2) used in the <br />cf create-service command above, can <br />be found by issuing the CF MARKETPLACE command<br /><br /><strong style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 14px; line-height: 1.5em;"><strong>Now verify that the app is up and running ok<br /><br /></strong></strong>cf apps<br />name requested state instances memory disk urls<br />jpa      started       0/1      512M  1G    jpa.<b>ng.bluemix.net</b>

7. Running the Application

You can invoke it from a browser with the following URL: jpa.ng.bluemix.net/JPABlueMixProject
The home page looks like this:

JPA Sample

Note:
PopulateDB must be run before ListDB or CustomerCredit so that there is a database table to work with.

You can use the “Drop Table” button to drop the table and start all over again if you like

Have Fun!


Find

Share this post:

Share on LinkedIn

Add Comment
28 Comments

Leave a Reply

Your email address will not be published.Required fields are marked *


Leo

what JPA implementation can I use?

Reply

    Dave Cohen

    When you push or deploy to BlueMix it will make use of Liberty’s JPA feature which is currently OpenJPA for JPA 2.0

    Reply

      Rakesh Jaiswal

      I am getting the error exactly same as described in
      http://www-01.ibm.com/support/docview.wss?uid=swg1PM70246

      [ERROR ] CWWJP0013E: The server cannot locate the jdbc/MyDataSourceDB2 data source for the CustomerQueryDB2 persistence unit because it has encountered the following exception: javax.naming.NameNotFoundException: Intermediate context does not exist: jdbc/MyDataSourceDB2.
      [ERROR ] CNTR0020E: EJB threw an unexpected (non-declared) exception during invocation of method “list” on bean “BeanId(myapp#myapp#ListDBEJB, null)”. Exception data: org.apache.openjpa.persistence.ArgumentException: The persistence provider is attempting to use properties in the persistence.xml file to resolve the data source. A Java Database Connectivity (JDBC) driver or data source class name must be specified in the openjpa.ConnectionDriverName or javax.persistence.jdbc.driver property. The following properties are available in the configuration: “WsJpaJDBCConfigurationImpl@a004b24f: PDQ disabled: AccessIntent Task=disable”.

      Reply

        Rakesh Jaiswal

        Please note that I do have SQLDB service called “MyDataSourceDB2” associated with my application.

        Reply

          Dave Cohen

          Hi Rakesh,

          Saw your statement, “Please note that I do have SQLDB service called “MyDataSourceDB2″ associated with my application”

          Did you have a question?


          Rakesh Jaiswal

          Hi Dave,

          I had posted another comment about an exception I get when I try to run application but it seems that was never published. Here is the detail which I had posted last time.

          I get following exception when I try to run application to ‘populate database’ or ‘list database content’ with either EJB or Servlet option event though I do have SQLDB service called “MyDataSourceDB2″ associated with my application.

          [ERROR ] CWWJP0013E: The server cannot locate the jdbc/MyDataSourceDB2 data source for the CustomerQueryDB2 persistence unit because it has encountered the following exception: javax.naming.NameNotFoundException: Intermediate context does not exist: jdbc/MyDataSourceDB2.
          [ERROR ] CNTR0020E: EJB threw an unexpected (non-declared) exception during invocation of method “list” on bean “BeanId(myapp#myapp#ListDBEJB, null)”. Exception data: org.apache.openjpa.persistence.ArgumentException: The persistence provider is attempting to use properties in the persistence.xml file to resolve the data source. A Java Database Connectivity (JDBC) driver or data source class name must be specified in the openjpa.ConnectionDriverName or javax.persistence.jdbc.driver property. The following properties are available in the configuration: “WsJpaJDBCConfigurationImpl@a004b24f: PDQ disabled: AccessIntent Task=disable”.
          at org.apache.openjpa.jdbc.schema.DataSourceFactory.newDataSource(DataSourceFactory.java:72)
          at org.apache.openjpa.jdbc.conf.JDBCConfigurationImpl.createConnectionFactory(JDBCConfigurationImpl.java:849)

          I have found a Tech Note from IBM stating that it is defect in WAS and fix is available in WAS 8.5.0.1. Has anyone tried to run this application recently? Or have I missed something obvious which is causing exception in my application?

          http://www-01.ibm.com/support/docview.wss?uid=swg1PM70246

          Regards
          Rakesh


          Dave Cohen

          Hi Rakesh,

          I reviewed the ReadMe.MD file and there was a case sensitivity typo,
          “MyDataSourcedb2” should have been “MyDataSourceDB2”. I’ve updated the documentation, please retry with the corrected name.

          Sorry for any inconvenience,

          Dave


          Rakesh Jaiswal

          Hi Dave,
          Many thanks for your response.

          I had noticed the case sensitive name of the datasource when I was first building my app and I had used ‘MyDataSourceDB2’ instead of ‘MyDataSourcedb2’ and still I was getting the error I mentioned earlier.

          I suspect when you ( or other people) had build and run this application then it might be using different version of Java Liberty. I guess if you try to run same application now then you might get same error as I am getting.

          Regards
          Rakesh


          Dave Cohen

          Hi Rakesh,

          Please cf login to BlueMix and issue the following commands, and reply with the results here:

          cf files JPABlueMixProject app/.liberty/usr/servers/defaultServer/server.xml

          cf files JPABlueMixProject app/.liberty/usr/servers/defaultServer/runtime-vars.xml

          We don’t think its a WAS/Liberty issue. Need to check to see if your bindings and autoconfig are working properly.


          Rakesh Jaiswal

          Thanks again for your help, Dave. Here are the output of the commands you had asked to run.

          C:Usersram>cf files JPABlueMixProject app/.liberty/usr/servers/defaultServer/server.xml
          Getting files for app JPABlueMixProject in org ***@yahoo.com / space dev as ***@yahoo.com…
          OK

          jsf-2.0
          jsp-2.2
          servlet-3.0
          ejbLite-3.1
          cdi-1.0
          jpa-2.0
          jdbc-4.0
          jndi-1.0
          managedBeans-1.0
          jaxrs-1.1
          appstate-1.0
          icap:managementConnector-1.0

          C:Usersram>

          C:Usersram>cf files JPABlueMixProject app/.liberty/usr/servers/defaultServer/runtime-vars.xml
          Getting files for app JPABlueMixProject in org ***@yahoo.com / space dev as ***@yahoo.com…
          OK

          C:Usersram>


          Dave Cohen

          Heard back from Rakesh, after deleting and rebinding his services, the app is now working :-)


          RaksUK

          Last reply did not correctly posted my full message ( possibly because of XML message) . I am trying again.

          C:Usersram>cf files JPABlueMixProject app/.liberty/usr/servers/defaultServer/server.xml
          Getting files for app JPABlueMixProject in org ***@yahoo.com / space dev as ***@yahoo.com…
          OK

          jsf-2.0
          jsp-2.2
          servlet-3.0
          ejbLite-3.1
          cdi-1.0
          jpa-2.0
          jdbc-4.0
          jndi-1.0
          managedBeans-1.0
          jaxrs-1.1
          appstate-1.0
          icap:managementConnector-1.0

          C:Usersram>
          C:Usersram>cf files JPABlueMixProject app/.liberty/usr/servers/defaultServer/runtime-vars.xml
          Getting files for app JPABlueMixProject in org ***@yahoo.com / space dev as ***@yahoo.com…
          OK

          C:Usersram>


Leo Kenji

thanks a bunch, very useful tutorial. Just curious if Bluemix Liberty supports Criteria API w/ MetaModel.

Reply

Dave Cohen

try adding this jar:

C:wlpdevapithird-partycom.ibm.websphere.appserver.thirdparty.jpa_1.0.3.jar

Reply

Mary Ellen Kerr

Very helpful tutorial!

Reply

Santiago H

I have a quick question, I have only eclipse and the bluemix server, where can I get the wlpdevapi libs?

thanks

Reply

Manish.Tripathy

Hi Dave. Your explanation of the project is really good. I can’t deny getting benefited from it. But I have a doubt here-

How can I know what is the database schema being used for creation of the table. I tried to search the table name CUSTOMERACCT in the IBM Managed Database Service console. But I couldn’t get it. Can you please let me know, how can I search for the table in console, and is there any way to specify my own schema name.

Thanks.

Reply

    davco01

    you can add this to the persistence.xml of your application to specify your own custom schema name:

    schema name specification in persistence.xml

    Reply

      Manish.Tripathy

      Hi Dave. I have already tried the above solution but I always get the following error-

      “MANISH.EMPLOYEE” is an undefined name.. SQLCODE=-204, SQLSTATE=42704, DRIVER=4.14.103 {prepstmnt 1401802661 INSERT INTO manish.Employee (emp_id, emp_name) VALUES (?, ?)}

      Please note that MANISH is the schema name and EMPLOYEE is the table name.
      I googled this error. I understand that this error says that it couldn’t find the table. But then why is this condition arising.

      Here is the code-
      userTran.begin();
      em.joinTransaction();
      System.out.println(“****creating table****”);
      q = em.createNativeQuery(“CREATE TABLE EMPLOYEE (EMP_ID INTEGER NOT NULL , EMP_NAME CHARACTER(30) NOT NULL , PRIMARY KEY ( EMP_ID ) )”);
      q.executeUpdate();
      //this.populateData();
      String[] empNames = ….(values are given)
      Integer[] empIds = …..(values are given)

      for (short i = 0; i < empNames.length; i++) {
      Employee e = new Employee();
      e.setEmpId(empIds[i]);
      e.setEmpName(empNames[i]);
      em.persist(e);
      }
      //End of populate
      userTran.commit();

      Can you please let me know what is going wrong here. Thanks.

      Reply

        davco01

        Do you have a create statement in your code? Or preferably you should have something like this in your persistence.xml to create the table (you can see this in the persistence.xml for the sample that goes with this article):

        Persistence.xml w drop/add

        Reply

Professor

I would like to build the app locally in my Eclipse with Derby and then push to BlueMix and use SQLDB. However persistence.xml only allows single per file. What is the best practice to deal with this? Shall I create two persistence.xml files on a side and rebuild the project before I do cf push with a different persistence.xml file?

If I used JDBC then all configuration can be done via VCAPS programmatically in the code – is there any way to do this programmatically when using JPA?

Reply
More Community Stories

Bluemix Compute Options: Cloud Foundry + OpenWhisk

Cloud Foundry and OpenWhisk are low-time-to-provision Compute models that a developer can use to power their application's workload. Find out which one is right for you, or whether it makes more sense to use both.

Prepping your OpenWhisk code for GA

It’s been an exciting year as advances in serverless computing, and OpenWhisk in particular, have transformed the way that we can build our applications and APIs. Learn how to prep your code for the GA.

Six reasons to take IoT for Electronics Starter for a spin

Six great reasons that IoT for Electronics is the best place to trial, pilot, and jump-start your electronics Internet of Things product launch.