Wednesday May 05, 2010

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

Thursday Apr 09, 2009

LOTD #20: How to create a JPA application using GlassFish Tools Bundle for Eclipse ?


Here is a great screencast (from the community) that shows how to create a JPA application using EclipseLink and deploy on GlassFish v2.1 - all using GlassFish Tools Bundle for Eclipse.

Click on the image below for the video:



Thanks!

I'll work on a MySQL version of it :)

All previous links in this series are archived at LOTD.

Technorati: lotd glassfish eclipse jpa javadb screencast

Sunday Aug 19, 2007

TOTD #3: Using JavaDB with JRuby on Rails

In a previous screencast, I showed how to develop a Rails application fetching data from the MySQL database and deploy it in GlassFish. GlassFish comes pre-bundled with JavaDB. Based upon a user request, this TOTD shows to use JavaDB database instead of MySQL.

Here are the steps required to replace MySQL database configuration with JavaDB. You can either mix these steps with the existing screencast or follow these steps altogether after the original app is created using MySQL.

  1. Copy "GLASSFISH_HOME/javadb/lib/derbyclient.jar" to "C:\\Program Files\\NetBeans 6.0M10\\ruby1\\jruby-1.0\\lib" (or the directory location where JRuby for NetBeans 6 is installed). Make sure NetBeans IDE is restarted after the jar file is copied.
  2. In your application, change development database configuration in "database.yml" from:

    development:
      adapter: mysql
      database: glassfishrocks_development
      username: root
      password:
      host: localhost


    to

    development:
      adapter: jdbc
      driver: org.apache.derby.jdbc.ClientDriver
      url: jdbc:derby://localhost:1527/sample
      username: app
      password: app


    The NetBeans IDE comes pre-configured with the database URL, username and password used in this configuration.
  3. Start JavaDB using "asadmin start-database" command.
  4. Invoke db:migrate rake target to create the database required by the application. The development database configuration is used to create the database tables. If you followed the screencast, then a new table "greetings" is created in the database "sample".

  5. Change production database configuration in "database.yml" from:

    production:
      adapter: mysql
      database: glassfishrocks_production
      username: root
      password:
      host: localhost


    to

    production:
      adapter: jdbc
      driver: org.apache.derby.jdbc.ClientDriver
      url: jdbc:derby://localhost:1527/sample
      username: app
      password: app

    This new database configuration is exactly the same as for the development configuration. The production configuration is used by the WAR file.
  6. Insert value into the newly create database as "insert into GREETINGS values (1, 'Hello from JavaDB');".

  7. Create the WAR file using war:standalone:create rake target.
  8. Deploy the WAR file in the "GLASSFISH_HOME/domains/domain/autodeploy" directory.

You can also check out an early version of a tutorial that is being written about installing and configuring Ruby support in the NetBeans IDE. If you would like to get early access to Ruby documentation, learn more here.

Please leave suggestions on the TOTD that you'd like to see. A complete archive is available here.

Technorati: totd jrubyonglassfish jruby rubyonrails javadb glassfish ror ruby

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