Wednesday, January 26, 2011

JPA2 Criteria API

In the process of writing the cabin finder application, I wanted to have a search panel where you could enter amenities such as fireplace, hottub, firepit, price range, etc. and update content panel with a google maps view of the matching cabins.  First I started with the old JPAQL way of writing a entityManager.createQuery("select c from cabins c where .......").   Then I came across the new way of doing it.  The Critieria API.

What's so great about Critieria API?  Well 2 things for starters.  First, queries are strongly typed in that the don't return a List of untyped objects.  Now they return a List<Cabin> or whatever root object type you set.  Second, they query syntax used to be a String, now it is a set of builder commands that can be validated by the compiler with the use of MetaData.

Step 1 is to setup MetaData.  If you're using eclipse, this is pretty simple.  Goto project properties and set the project facet for JPA version 2.  The additional configuration options allow you to select the JPA provider and download a user library of the JPA provider jars.  I'm using eclipselink since it's native to Glassfish and helps keep my war file small (~5mb so far of which 4.2 are the db drivers for derby and mysql).  Once the user library and JPA provider are setup, eclipse will scan your project and create the JPA  metadata.  I put mine in the .apt_generated folder.  Once created you can review the metadata.  Mostly you'll see SingularAttribute or ListAttributs for simple and list types respectively.  Also note the class names are followed with and inderscore, so Cabin entity gets Cabin_ metadata created.

Step 2 is to start using the Criteria API.  The example to search for amenities such as fireplace, hottub and/or firepit is pretty straightforward.  First, get a CriteriaBuilder from the entityManager.  Then set the class type you are expecting the query to return.  The "where" part of the query is defined by Predicates so we'll initialize one and add each submitted criteria one at a time.  Then set the query's where to the predicate and get the results list.



 private List buildAndRunQuery() {
  List retVal = null;
  CriteriaBuilder builder = db.getCriteriaBuilder();
  CriteriaQuery query = builder.createQuery(Cabin.class);
  Root root = query.from(Cabin.class);

  Predicate temp = builder.conjunction();;
  if(cabin.getAddress().getState()!=null) {
   Join address = root.join( Cabin_.address );
   temp = builder.and(temp,builder.equal(address.get(Address_.state),cabin.getAddress().getState()));
  }
  if (cabin.isFirePit()) {
   temp = builder.and(temp,builder.isTrue(root.get(Cabin_.firePit)));
  }
  if (cabin.isFirePlace()) {
   temp = builder.and(temp,builder.isTrue(root.get(Cabin_.firePlace)));
  }
  if (cabin.isHotTub()) {
   temp = builder.and(temp,builder.isTrue(root.get(Cabin_.hotTub)));
  }
  query.where(temp);
  
  retVal = db.createQuery(query).getResultList();  
  return retVal;
 }

Here's a few references that I found to be helpful.
http://docs.jboss.org/hibernate/stable/entitymanager/reference/en/html/querycriteria.html
http://wiki.eclipse.org/EclipseLink/Examples/JPA/JSF_Tutorial#Using_JPA_Queries
http://stackoverflow.com/questions/2510106/dynamic-jpa-2-0-query-using-criteria-api
and
http://stackoverflow.com/questions/2880209/jpa-findbyexample

The source code for the cabin finder is on GitHub at https://github.com/sbasinge/primetest.

So give the Criteria API a shot and have validated, type safe and fast queries!

Tuesday, January 25, 2011

On word

Gizmo was born in the back of a semi-truck, son a a runaway father and slightly irresponsible mother.  He didn't let the difficult upbringing affect him at all.  He learned quickly to befriend others.  My wife Judy first met Gizmo when he was very young and had been separated from his mother.  All the women in the office loved him immediately, but Judy was the one who Gizmo took to the most and it was not long until he became part of our family.

Let''s just say that his older brother Felix was not as easy to get along with.  For the first few weeks the two had to be separated for their safety and our sanity, but it was not long until the young Gizmo won the respect of his older (by 1 1/2 years) brother.  For the next 15 years the 2 were inseparable.  They played together, ate together and slept together.

As is often the case, the older siblings are the first to go.  As Felix suffered through a cancerous growth on his larynx, Gizmo was inconsolable, lost...  After a bit of time, Gizmo marched forward on his own.  As he had been since the beginning, undeterred by his surroundings, he sought out others, relished in his new role in the family and even became a social beacon for the rest of the family.

Sadly, this last Saturday Gizmo passed after a long bout with kidney disease.  He was a good friend, brother and family member.  At age 93 (19 in cat years) he lived a full and rewarding life. He will be missed, but someday my wife and I may adopt another cat, heck maybe even a dog.

Saturday, January 15, 2011

Google App Engine and JSF/Weld

I was planning to create another long blog about this topic but it turned out to be more work to get a sample application running.  Here's the basic premise.  In prior blogs about Glassfish and JSF we went thru the process of setting up an eclipse based development environment, mavenized project that illustrated JEE6 features like JSF, Facelets and JPA.  We also added Primefaces JSF tags for a little spice.

Now I'd like to turn that exercise into something more real so I imaged an application....  Call it cabin finder and what I want to build is a 4 panel layout -- with header, footer, left panel and content.  On the header I want to use primefaces Dock for site navigation.  The footer is adverstisements I guess.  The left panel is a search criteria builder where you could enter the location, amenities and price range of the vacation spot your looking for.  In the content panel we'll show google maps of the resulting cabins and a popup dialog for each with more details.

We'll also need a cabin entry page that should be secured -- so login required.  Also, just to get things up and going I want an admin page to load sample data.  I actually had the entities, search controller and pages built pretty quickly based on our previous work.

Then......

I decided it would be cool to host the whole thing on Google App Engine.  I've spent the past 2 days working on that part and finally have it going.  Take a look for yourself at http://mycabinshh.appspot.com.  Keep in mind I've spent more time futzing around with GAE than working on the application so there's no entry page, security, only the state searching is supported, the maps info popus aren't there, etc., etc.

Now .....

I'd like to move the source code to github.  Maybe I'll never get to the app.

Saturday, January 8, 2011

Setup Eclipse JEE6 Primefaces and Glassfish - Part II

In the first installment we setup a basic web application with JEE6, JSF2, Primefaces running on Glassfish using Eclipse for the IDE.  We'll continue here to add testing with Arquillian and Persistence with EclipseLink/JPA2.  Once again we're simply tweaking Nicklas Karlsson's examples except we're making it a little less JBoss specific.


We'll build upon the primetest project, which you can download from here.  Simply extract the zip into your workspace and use import existing maven projects to get you caught up.


The first thing we'll do is add some dependencies for Arquillian and a Profile for Glassfish Embedded.  Arquillian allows you to run tests with JUnit or TestNG.  It facilitates in-container testing, which means all the CDI injections take place, you don't have to hand-wire together test objects.  Let's add a few properties to the pom for our dependencies:
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <arquillian.version>1.0.0.Alpha4</arquillian.version>
    <glassfish.embedded.version>3.1-b04</glassfish.embedded.version>
    <junit.version>4.8.1</junit.version>
    <slf4j.version>1.5.9.RC1</slf4j.version>
</properties>

and the dependencies themselves:
<dependency>
<groupId>org.jboss.arquillian</groupId>
<artifactId>arquillian-junit</artifactId>
<version>${arquillian.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
We'll also add a dependency management section like so:

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

and lastly, the profile for Glassfish embedded:

<profile>
<id>glassfish-embedded-3</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<dependencies>
<dependency>
<groupId>org.jboss.arquillian.container</groupId>
<artifactId>arquillian-glassfish-embedded-3</artifactId>
<version>${arquillian.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.extras</groupId>
<artifactId>glassfish-embedded-all</artifactId>
<version>${glassfish.embedded.version}</version>
</dependency>
</dependencies>
</profile>
Notice the activeByDefault flag.  We'll toggle the flag between true for testing and false for runtime.  A bit problematic, but the best way I've found to have a profile for testing only.

ok, now we're all setup for testing, we just need to write a test......
First we'll create a simply class named TemperatureConverter in the com.examples.test package like so:

package com.examples.temp;
public class TemperatureConverter {
public double convertToCelsius(double f) {
return ((f - 32) * 5 / 9);
}
public double convertToFarenheit(double c) {
return ((c * 9 / 5) + 32);
}
}


and then a test case in src/test/com.examples.temp like this:


package com.examples.temp;
import javax.inject.Inject;
import junit.framework.Assert;
import org.jboss.arquillian.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ArchivePaths;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import static org.junit.Assert.assertEquals;
@RunWith(Arquillian.class)
public class TemperatureConverterTest {
@Inject
private TemperatureConverter converter;
@Deployment
public static JavaArchive createTestArchive() {
return ShrinkWrap.create(JavaArchive.class, "test.jar")
.addClasses(TemperatureConverter.class)
.addManifestResource(
EmptyAsset.INSTANCE,
ArchivePaths.create("beans.xml"));
}
@Test
public void testConvertToCelsius() {
Assert.assertEquals(converter.convertToCelsius(32d), 0d);
Assert.assertEquals(converter.convertToCelsius(212d), 100d);
}
@Test
public void testConvertToFarenheit() {
Assert.assertEquals(converter.convertToFarenheit(0d), 32d);
Assert.assertEquals(converter.convertToFarenheit(100d), 212d);
}
}

A breif description of what's happening here.  The @Deployment annotation designates the container setup for the test.  We will create a temporary jar (test.jar) to put in the glassfish embedded container and add an empty beans.xml for CDI.  With that done, the container will properly inject the TemeratureConverter class that we'll use in the 2 test cases.


I tried several ways of getting the maven integration to work for this, including setting the default profile in the project's maven properties, setting the default profile in the pom and creating a special run config for the tests.  All had issues, so what I've settled on is changing the activeByDefault flag in the pom, and then perform mvn test (or run as/maven test in eclipse).  Try that and see the test results... 
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0


Not much in the way of logging is happening, so add a log4j.xml file in your src/test/resources:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="true">
   <!-- ============================== -->
   <!-- Append messages to the console -->
   <!-- ============================== -->
   <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
      <param name="Target" value="System.out"/>
      <param name="Threshold" value="ALL"/>
      <layout class="org.apache.log4j.PatternLayout">
         <!-- The default pattern: Date Priority [Category] Message\n -->
         <param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{1}] %m%n"/>
      </layout>
   </appender>
   <!-- ================ -->
   <!-- Limit categories -->
   <!-- ================ -->
   <category name="com.examples">
     <priority value="ALL" />
   </category>
   <root>
     <appender-ref ref="CONSOLE"/>
   </root>
</log4j:configuration>


Ok, now we have some running tests for our simple web app.  If you downloaded the zip file, you'll notice a few extra beans and additions to the greetings.xhtml to add a few more primefaces widgets (autocomplete and date picker).  

Now let's add some persistence for our greetings.  We'll use an Apache Derby database since it's simple to setup and comes with Glassfish.  So we first need to define a dataSource in Glassfish.  For that we use the sun-resources.xml file.  Create/register the jdbc/primetest jndi resource by adding sun-resources.xml in the src/main/resources folder like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0 Resource Definitions //EN"
   "http://www.sun.com/software/appserver/dtds/sun-resources_1_4.dtd">
<resources>
   <jdbc-resource pool-name="EmbeddedDerbyPool"
      jndi-name="jdbc/primetest"/>
   <jdbc-connection-pool name="EmbeddedDerbyPool"
      res-type="javax.sql.DataSource"
      datasource-classname="org.apache.derby.jdbc.EmbeddedDataSource"
      is-isolation-level-guaranteed="false">
      <property name="databaseName" value="/tmp/databases/derby"/>
      <property name="createDatabase" value="create"/>
   </jdbc-connection-pool>
</resources>

Then we'll add the JPA persistence file in the src/main/resources/META-INF/persistence.xml like this:


<?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="test">
      <!--
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
      -->
      <jta-data-source>jdbc/primetest</jta-data-source>
      <properties>
         <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
         <property name="hibernate.show_sql" value="true"/>
         <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
         <property name="eclipselink.logging.level" value="FINE"/>
      </properties>
   </persistence-unit>
</persistence>

Now we have our JPA setup and ready to go, let's create a Greeting entity that we can persist.  Create Greeting.java in the com.examples.greeting package:

package com.examples.greeting;
import static javax.persistence.TemporalType.TIMESTAMP;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Temporal;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@Entity
public class Greeting
{
   @Id
   @GeneratedValue
   int id;
   String text;
   
   @Temporal(TIMESTAMP)
   Date created = new Date();
   public Greeting(String greet) {
   text = greet;
   }
   public Greeting() {
   }
   public int getId()
   {
      return id;
   }
   public void setId(int id)
   {
      this.id = id;
   }
   public String getText()
   {
      return text;
   }
   public void setText(String text)
   {
      this.text = text;
   }
}

We are going to raise an event when a Greeting is added, so we'll create an Interface named Added and put it in the com.examples.greeting package.

package com.examples.greeting;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
@Qualifier
@Retention(RUNTIME)
@Target( { METHOD, FIELD, PARAMETER, TYPE })
public @interface Added
{
}

Now we need to create something to get and save Greetings.  We'll call it GreetingArchiver and put it in the com.examples.greeting package too.


package com.examples.greeting;
import java.util.List;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.UserTransaction;
@ApplicationScoped
public class GreetingArchiver
{
   @PersistenceContext
   EntityManager db;
   @Inject
   UserTransaction userTransaction;
   public void saveGreeting(@Observes @Added Greeting greeting)
   {
      try
      {
         userTransaction.begin();
         db.joinTransaction();
         db.persist(greeting);
         db.flush();
         userTransaction.commit();
      }
      catch (Exception e)
      {
         e.printStackTrace();
         // The recommended way of dealing with exceptions, right?
      }
   }
   public List<Greeting> loadGreetings()
   {
      return db.createQuery("select g from Greeting g").getResultList();
   }
}
Lastly, we need to update GreetingBean to use all this new persistence setup.


package com.examples.greeting;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.inject.Named;
@ApplicationScoped
@Named
public class GreetingBean implements Serializable
{
   Greeting greeting = new Greeting();
   List<Greeting> greetings = new ArrayList<Greeting>();
   @Inject
   @Added
   Event<Greeting> greetingAddedEvent;
   @Inject
   GreetingArchiver greetingArchiver;
   
   @PostConstruct
   public void init()
   {
      greetings = greetingArchiver.loadGreetings();
   }
   public void addGreeting()
   {
      greetings.add(greeting);
      greetingAddedEvent.fire(greeting);
      greeting = new Greeting();
   }
   public Greeting getGreeting()
   {
      return greeting;
   }
   public void setGreeting(Greeting greeting)
   {
      this.greeting = greeting;
   }
   public List<Greeting> getGreetings()
   {
      return greetings;
   }
   public void setGreetings(List<Greeting> greetings)
   {
      this.greetings = greetings;
   }
}
Notice that the greeting attribute is used as a template to hold information from the page.  Also note the postConstruct is used to load greetings upon initialization.  Lastly notice the greetingAddedEvent and that it is fired in the addGreeting method.  This is what triggers the saveGreeting method in GreetingArchiver because it is listening for Added Greetings.

A few tweaks are required to our page to enter and list the greetings and we'll be ready to run the application.  First, set the activeByDefault flag to false in the pom.  Then update the greetings.xhtml to look like this:


<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.prime.com.tr/ui">
<h:head>
<title>Greetings</title>
</h:head>
<h:body>
<h:form>
<p:ajaxStatus>
<f:facet name="start">
<h:outputText value="Loading..." />
</f:facet>
<f:facet name="complete">
<h:outputText value="Done!" />
</f:facet>
</p:ajaxStatus>
<p:accordionPanel id="mainPanel">
<p:tab title="Greetings" id="firstTab">
<p:dataTable id="myTable" value="#{greetingBean.greetings}" var="greeting">
<f:facet name="header"><h:outputText value="Greeting"/></f:facet>
<p:column>
<h:outputText value="#{greeting.text}"/>
</p:column>
</p:dataTable>
<h:inputText value="#{greetingBean.greeting.text}"/>
<p:commandButton value="Add" action="#{greetingBean.addGreeting}" update="mainPanel"/>
</p:tab>
<p:tab title="Date Picker">
<h:outputText value="Ipsum" />
<p:calendar value="#{dateBean.date}" />
</p:tab>
<p:tab title="Auto Complete">
<p:autoComplete value="#{autoCompleteBean.text}"
completeMethod="#{autoCompleteBean.complete}" />
</p:tab>
</p:accordionPanel>
<h:outputText value="#{greetingBean.greeting.text}" />
</h:form>
</h:body>
</html>

Notice that we named the main accordian panel "mainPanel" and that the Add button refreshes the "MainPanel".  This is all you have to do to have a partial page refresh using primefaces!  Pretty sweet.


Start your server and refresh the goto http://localhost:8080/primetest/greetings.jsf and you should get a page like this.



In order to setup our test environment for JPA we need to do a few things.  First add a src/test/resources-glassfish-embeded to hold our configuration files for the embedded test environment.  We'll need a persistence.xml and sun-resources.xml.  They'll look similar to the ones we created for the webapp so just copy them.  Rename the persistence.xml to test-persistence.xml.  Then add an arquillian.xml to the src/test/resources folder.


<?xml version="1.0" encoding="UTF-8"?>
<arquillian xmlns="http://jboss.com/arquillian"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:glassfish-embedded="urn:arq:org.jboss.arquillian.container.glassfish.embedded_3">
   <!-- Uncomment to have test archives exported to the file system for inspection -->
   <!--
   <engine>
      <deploymentExportPath>target/</deploymentExportPath>
   </engine>
   -->
   <glassfish-embedded:container>
      <glassfish-embedded:sunResourcesXml>src/test/resources-glassfish-embedded/sun-resources.xml</glassfish-embedded:sunResourcesXml>
   </glassfish-embedded:container>  
</arquillian>

Add the src/test/resources-glassfish-embedded to the build path.  With that we're all ready to test.  We just need to write the test in src/test/com.examples.greeting:

package com.examples.temp;
import static org.junit.Assert.assertEquals;
import java.io.PrintStream;
import java.util.List;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import javax.transaction.UserTransaction;
import org.jboss.arquillian.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.examples.greeting.Greeting;
@RunWith(Arquillian.class)
public class GreetingPersistenceTest {
private static Logger log = LoggerFactory.getLogger(GreetingPersistenceTest.class);
   @PersistenceContext
   EntityManager em;
   
   @Inject
   UserTransaction utx;
   @Deployment
   public static Archive<?> createDeployment()
   {
      return ShrinkWrap.create(WebArchive.class, "test.war")
            .addPackage(Greeting.class.getPackage())
            .addManifestResource("test-persistence.xml", "persistence.xml")
            .addWebResource(EmptyAsset.INSTANCE, "beans.xml");
   }
   private static final String[] GREETING_TEXT =
   {
      "Super Mario Brothers",
      "Mario Kart",
      "F-Zero"
   };
   public void insertSampleRecords() throws Exception
   {
      utx.begin();
      em.joinTransaction();
    
      log.info("Clearing the database...");
      em.createQuery("delete from Greeting").executeUpdate();
    
      log.info("Inserting records...");
      for (String title : GREETING_TEXT)
      {
         Greeting greet = new Greeting(title);
         em.persist(greet);
      }
    
      utx.commit();
   }
   @Test
   public void should_be_able_to_select_games_using_jpql() throws Exception
   {
      insertSampleRecords();
      
      utx.begin();
      em.joinTransaction();
    
      log.info("Selecting (using JPQL)...");
      List<Greeting> greetings =
         em.createQuery("select g from Greeting g order by g.id", Greeting.class)
         .getResultList();
      log.info("Found {} greetings (using JPQL)",greetings.size());
      assertEquals(GREETING_TEXT.length, greetings.size());
    
      for (int i = 0; i < GREETING_TEXT.length; i++) {
         assertEquals(GREETING_TEXT[i], greetings.get(i).getText());
         log.info("{}",greetings.get(i).getText());
      }
      
      utx.commit();
   }
   
   @Test
   public void should_be_able_to_select_games_using_criteria_api() throws Exception
   {
      insertSampleRecords();
      
      utx.begin();
      em.joinTransaction();
    
      CriteriaBuilder builder = em.getCriteriaBuilder();
      CriteriaQuery<Greeting> criteria = builder.createQuery(Greeting.class);
      // FROM clause
      Root<Greeting> greeting = criteria.from(Greeting.class);
      // SELECT clause
      criteria.select(greeting);
      // ORDER BY clause
      criteria.orderBy(builder.asc(greeting.get("id")));
      // No WHERE clause, select all
    
      log.info("Selecting (using Criteria)...");
      List<Greeting> greetings = em.createQuery(criteria).getResultList();
      log.info("Found " + greetings.size() + " greetings (using Criteria)");
      assertEquals(GREETING_TEXT.length, greetings.size());
    
      for (int i = 0; i < GREETING_TEXT.length; i++) {
         assertEquals(GREETING_TEXT[i], greetings.get(i).getText());
         log.info("{}",greetings.get(i).getText());
      }
    
      utx.commit();
   }
}


Set the activeByDefault flag to true in the pom and run/as maven test.

Tests run: 4, Failures: 0, Errors: 0, Skipped: 0


Awesome!  Till next time, keep learning!


Here's a link to the finished project.