TOTD #133: JPA2 (JPQL & Criteria), JavaDB, and embedded GlassFish - perfect recipe for testing

TOTD #132 explained how to use embedded GlassFish for deploying/testing a simple Servlet. As mentioned in that blog, this Tip Of The Day (TOTD) will extend that entry and show how to create a simple test that:

  • Creates a JDBC Connection Pool using GlassFish APIs
  • Creates a JDBC Resource using that Connection Pool, again programmatically
  • Persists and Queries an entity in that database using JPQL and the newly introduced Criteria APIs in JPA 2

This application uses a "Games" entity with two simple fields - "id" for the primary key and "name" for the name of a game. The test persists 3 entries in the database and then retrieves them by issuing a JPQL statement and Criteria API. There are four tests:

  • The first test consists of creating a JDBC Connection Pool & a JDBC Resource using that pool in the embedded GlassFish instance
  • The second test stores 3 entities in the database using the JDBC resource
  • The third test queries the database using JPQL
  • The fourth test queries the database using Criteria API

The entire source code is available here and "mvn test" is all you need to see the action. The updated directory structure from TOTD #132 is:

src
src/main
src/main/java
src/main/java/org
src/main/java/org/glassfish
src/main/java/org/glassfish/embedded
src/main/java/org/glassfish/embedded/samples
src/main/java/org/glassfish/embedded/samples/App.java
src/main/java/org/glassfish/embedded/samples/Games.java
src/main/resources
src/main/resources/META-INF
src/main/resources/META-INF/persistence.xml
src/test
src/test/java
src/test/java/org
src/test/java/org/glassfish
src/test/java/org/glassfish/embedded
src/test/java/org/glassfish/embedded/samples
src/test/java/org/glassfish/embedded/samples/AppTest.java

The newly added files are highlighted in the bold. "Games.java" is the new entity class and "persistence.xml" provides the definition of Persistence Unit.

Here is our simple entity class:

package org.glassfish.embedded.samples;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

/\*\*
 \* @author arungupta
 \*/
@Entity
@Table
public class Games implements Serializable {
    @Id
    int id;

    @Column
    String name;

    public Games() { }

    public Games(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Adding "@Entity" and "@Table" annotation on this POJO makes it an JPA Entity Bean. In addition, the primary key is defined using the "@Id" annotation on the field and an additional column is marked using "@Column"annotation.

In "AppTest.java", add a new method to create and test for JDBC connection pool and a JDBC resource using that pool as shown below:

public void testJDBCPoolAndResource() throws Throwable {
    Server.Builder builder = new Server.Builder("server");
    server = builder.build();
    server.createPort(8080);

    // add the required containers
    server.addContainer(ContainerBuilder.Type.web);
    server.addContainer(ContainerBuilder.Type.jpa);

    // Create a JDBC connection pool
    ParameterMap params = new ParameterMap();
    params.add("datasourceclassname", "org.apache.derby.jdbc.EmbeddedDataSource");
    params.add("property",
       "databaseName=games:connectionAttributes=\\\\;create\\\\=true");
    params.add("jdbc_connection_pool_id", JDBC_POOL);
    executeCommand("create-jdbc-connection-pool", server, params);
    System.out.println("JDBC Connection Pool successfully created.");

    // Assert the JDBC connection pool
    List<MessagePart> messageParts = 
        executeCommand("list-jdbc-connection-pools", server);
    assertInArray(messageParts, JDBC_POOL);

    // Create a JDBC resource
    System.out.println("Creating JDBC resource ...");
    ParameterMap params2 = new ParameterMap();
    params2.add("connectionpoolid", "MyPool");
    params2.add("jndi_name", JDBC_RESOURCE);
    executeCommand("create-jdbc-resource", server, params2);

    // Assert the JDBC resource
    messageParts = executeCommand("list-jdbc-resources", server);
    assertInArray(messageParts, JDBC_RESOURCE);
}

A disk representation of the database is created in the current directory by using "databaseName=games". Using in-memory JavaDB would've avoided the directory creation but facing issues with that (more on this at the end).

Here are convenience methods to execute an "asadmin"-equivalent command and used in the method above ...

private List executeCommand(String command, Server server) throws Throwable {
    return executeCommand(command, server, null);
}

private List executeCommand(String command, Server server, ParameterMap params) 
    throws Throwable {
    CommandRunner runner = server.getHabitat().getComponent(CommandRunner.class);
    ActionReport report = server.getHabitat().getComponent(ActionReport.class);
    if (params == null)
        runner.getCommandInvocation(command, report).execute();
    else
        runner.getCommandInvocation(command, report).parameters(params).execute();
    if (report.hasFailures()) {
        report.writeReport(System.out);
        throw report.getFailureCause();
    }

    return report.getTopMessagePart().getChildren();
}   

The error messages are shown on the console. Add another method for asserting on the created JDBC connection pool and resource as:

private void assertInArray(List messageParts, String resource) throws Throwable {
    boolean found = false;

    for (MessagePart part : messageParts) {
        if (part.getMessage().equals(resource)) {
            found = true;
            break;
        }
        System.out.println(part.getMessage());
    }

    if (!found)
        throw new Throwable("\\"" + resource + "\\" resource not found");
}

Here is a method to insert new values in the database using standard JPA calls:

public void testInsert() throws Throwable {
    EntityManagerFactory emf = 
        Persistence.createEntityManagerFactory("webtier2-PU");
    EntityManager em = emf.createEntityManager();

    // list of games to be added
    String[] games = {
               "Super Mario Galaxy",
               "Super Mario Brothers",
               "Mario Kart" };

    // create a transaction
    EntityTransaction utx = em.getTransaction();
    System.out.println("Persisting " + games.length + " games ...");
    utx.begin();
    for (int i=0; i<games.length; i++) {
        em.persist(new Games(i, games[i]));
        System.out.println("\\t " + games[i]);
    }
    utx.commit(); // and commit
    System.out.println("Committed.");
}

The method to query the database using JPQL ...

public void testJPQL() throws Throwable {
    EntityManagerFactory emf = 
        Persistence.createEntityManagerFactory("webtier2-PU");
    EntityManager em = emf.createEntityManager();

    // now query them
    List list = em.createQuery("select g from Games g").getResultList();
    assertEquals("Games retrieved", 3, list.size());
    System.out.println("Found " + list.size() + " games (using JPQL) ...");
    for (Object o : list) { // and dump
        System.out.println("\\t" + ((Games)o).getName());
    }
}

And finally a method to query the database using JPA 2 Critieria API ...

public void testCriteria() throws Throwable {
    EntityManagerFactory emf = 
        Persistence.createEntityManagerFactory("webtier2-PU");
    EntityManager em = emf.createEntityManager();

    CriteriaBuilder cb = emf.getCriteriaBuilder();
    CriteriaQuery<Games> criteria = cb.createQuery(Games.class);

    // FROM clause
    Root<Games> games = criteria.from(Games.class);

    // SELECT clause
    criteria.select(games);

    // No WHERE clause - pick all

    // FIRE
    List<Games> list = em.createQuery(criteria).getResultList();
    assertEquals("Games retrieved", 3, list.size());
    System.out.println("Found " + list.size() + " games (using Criteria) ...");
    for (Object o : list) { // and dump
       System.out.println("\\t" + ((Games)o).getName());
    }
 }

And here is the "persistence.xml"

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
       http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
   <persistence-unit name="webtier2-PU" transaction-type="JTA">
       <jta-data-source>jdbc/MyResource</jta-data-source>
       <properties>
           <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
       </properties>
       <class>org.glassfish.embedded.samples.Games</class>
   </persistence-unit>
</persistence>

The Persistence Unit is referring to "jdbc/MyResource" JDBC Resource created earlier. The table creation strategy is "drop-and-create-tables" and this can be "create-tables" if in-memory JavaDB is used instead.

The version of "glassfish-embedded-all" dependency in "pom.xml" is changed from "3.0" to "3.1-SNAPSHOT" so that latest updates are picked. The updated dependency looks like:

<dependency>
   <groupId>org.glassfish.extras</groupId>
   <artifactId>glassfish-embedded-all</artifactId>
   <version>3.1-SNAPSHOT</version>
</dependency>

Change the source version of "maven-compiler-plugin" from "1.5" to "1.6", the updated entry looks like:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.0.2</version>
    <configuration>
       <source>1.6</source>
       <target>1.6</target>
    </configuration>
</plugin>


And now when you fire "mvn clean test", a test output is shown as ...

INFO: Instantiated an instance of org.hibernate.validator.engine.resolver.JPATraversableResolver.
[EL Info]: 2010-05-05 13:49:28.339--ServerSession(149074043)--file:/Users/arungupta/samples/v3/
embedded/webtier2/target/classes/_webtier2-PU login successful
Persisting 3 games ...
         Super Mario Galaxy
         Super Mario Brothers
         Mario Kart
Committed.
Found 3 games (using JPQL) ...
        Mario Kart
        Super Mario Galaxy
        Super Mario Brothers
Found 3 games (using Criteria) ...
        Mario Kart
        Super Mario Galaxy
        Super Mario Brothers

You may see the following exception during the test run:

May 5, 2010 1:49:24 PM com.sun.logging.LogDomains$1 log
SEVERE: loader.asurlclassloader_malformed_url
java.util.zip.ZipException: error in opening zip file
        at java.util.zip.ZipFile.open(Native Method)
        at java.util.zip.ZipFile.(ZipFile.java:114)
        at java.util.jar.JarFile.(JarFile.java:133)
        at java.util.jar.JarFile.(JarFile.java:97)

This occurs because no URL is specified in the connection properties. This error message can be ignored as sufficient information is available to resolve the database otherwise.

Using the in-memory JavaDB is causing some sort of race condition in the test path and throwing an exception at random runs. After trying the configuration on couple of Macs, Windows 7 and Ubuntu and still not able to detect the cause of randomness, no formal issue is logged yet but its still being followed with the required folks.

In the meanwhile, enjoy the power of embedded GlassFish with JPA and Java DB.

Technorati: totd glassfish v3 javaee embedded servlet jpa persistence javadb

Comments:

Dear Arun,

I was waiting for a blog post like this since my first met with JEE 6, it's a shame I didn't have found the necessary bits to put it all together before.

Now I'll be rewriting some tests.

Thanks for everything,

Best Regards,
Alessandro

Posted by Alessandro Oliveira on May 05, 2010 at 09:49 PM PDT #

Alessandro,

Glad you liked the post, please post a trackback after you've written your tests and hopefully blogged about them :-)

Posted by Arun Gupta on May 06, 2010 at 01:39 AM PDT #

Hi Arun,

the blog is really cool. I tried the same some month ago without success.

Would it possible to download the source code.

TIA
Martin

Posted by Martin on May 06, 2010 at 09:39 PM PDT #

Martin, the source code is available at:

http://blogs.sun.com/arungupta/resource/totd133-webtier2.zip

Also linked from the blog entry.

Posted by Arun Gupta on May 07, 2010 at 01:47 AM PDT #

Arun, is the race condition by any chance an EclipseLink problem? I've seen such things before, and never thought it was because of an in-memory database issue. See http://old.nabble.com/Race-conditions-in-EclipseLink-runtime--td28463961.html for more details; Tom Ware is looking into it as we speak.

Posted by Laird Nelson on May 07, 2010 at 06:14 AM PDT #

Laird,

The symptoms are different.

Keeping EclipseLink as the only provider, a colleague could reproduce the problems with JDK 1.6 U19 and not with U17 & U20. So it seems like the root cause here might be different. I still need to investigate this little bit more but will keep you posted.

Posted by Arun Gupta on May 07, 2010 at 06:25 AM PDT #

Hi Arun,

thanks for this blog post, this is the first working unit tests I've could find on the internet for JPA, I will definitely need this!

But I've a question, I was trying to do tests like this withz junit 4.4 and I failed. When I've found yours, I had to switch from junit 3.x to 4.4 and I'm seeing similiar exceptions when I was trying. Could you explain why I'm experiencing this? Maybe it isn't possible to use junit 4.x? If it is, could you help me to upgrade it to 4.x?

Very thanks for your help!

Posted by Bálint Kriván on June 12, 2010 at 06:43 PM PDT #

Balint,

This seems more like a question for JUnit forums :-)

Posted by Arun Gupta on June 15, 2010 at 09:28 AM PDT #

Hi Arun, this bug: https://glassfish.dev.java.net/issues/show_bug.cgi?id=13865 recommends that one should be using glassfish-embedded-static-shell rather than glassfish-embedded-all. Can you please comment? Will I run into problems when using glassfish-embedded-all?

Posted by Petr Jiricka on November 01, 2010 at 11:00 PM PDT #

Petr, the bug report is now updated, please follow up there.

Posted by Arun Gupta on November 03, 2010 at 03:52 AM PDT #

Hello Arun
thanks for this post.
I was trying the same, but with an EJB who has persistence context injected into it. I'm confused now as to how to lookup the context for the EJB with this approach. Could you please clarify.

Posted by ranjan on December 04, 2010 at 07:07 PM PST #

This is great post, but I need more robust JUnit based JPA 2.0 testing.

Posted by Ojitha Kumanayaka on December 23, 2010 at 02:36 PM PST #

Arun, the source code is not available in any of the links. can you please upload
thanks

Posted by guest on December 11, 2012 at 07:57 AM PST #

The source code is already linked from the blog:

http://blogs.sun.com/arungupta/resource/totd133-webtier2.zip

Is it not working ?

Posted by Arun Gupta on December 11, 2012 at 10:52 AM PST #

The source code path is now:
https://blogs.oracle.com/arungupta/resource/totd133-webtier2.zip

Posted by guest on December 11, 2012 at 11:15 AM PST #

Post a Comment:
Comments are closed for this entry.
About

profile image
Arun Gupta is a technology enthusiast, a passionate runner, author, and a community guy who works for Oracle Corp.


Java EE 7 Samples

Stay Connected

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today