NetBeans 7.0 M2 was released recently. There are several Java EE related improvements in this release:
- Find usages of managed beans (JSF/CDI) and their properties
- PrimeFaces is now available as an integrated JSF component library
- Wizard for creating Bean Validation constraint
- CDI Qualifier creation editor hint
- Cleaned up Inspect Observer/Producer for CDI events
- Generation of Bean Validation annotations for Entities
and some others. One of the features that is not much spoken about is the automatic generation of JPA 2 Metamodel classes from Entity classes. This Tip Of The Day (TOTD) will explain how to generate these classes and use them for writing type-safe JPA2 Criteria queries.
The JPA2 Metamodel classes capture the metamodel of the persistent state and relationships of the managed classes of a persistence unit. This abstract persistence schema is then used to author the type-safe queries using Critieria API. The canonical metamodel classes can be generated statically using an annotation processor following the rules defined by the specification. The good thing is that no extra configuration is required to generate these metamodel classes. NetBeans IDE automatically generates the canonical metamodel classes using the EclipseLink Canonical Model Generator. There are two ways these metamodel classes are generated in NetBeans:
- Pre-configured when Entity Classes are generated from a Database using the wizards. TOTD #122 provide more details on that. The actual metamodel classes are generated when the project is build using "Clean and Build", "Deploy" or some other related target.
- Explicitly configured by right-clicking on the project, "Properties", "Libraries", "Processor", "Add Library…", and select "EclipseLink(JPA 2.0)" and "EclipseLink-ModelGen(JPA 2.0)" libraries and click on "Add Library" as shown below.
This TOTD will use the "Manufacturer" table from the pre-configured "jdbc/sample" JDBC resource in NetBeans and GlassFish. It will create a simple 2-page application where the first page (index.xhtml) accepts a Manufacturer name and the second page (show.xhtml) displays some details about that manufacturer.
- Create a NetBeans Web project with the title "CriteriaMetamodel", make sure to enable CDI and Java Server Faces during the creation.
- Create "Manufacturer" JPA entity by using the pre-configured "jdbc:derby://localhost:1527/sample" database connection and using the MANUFACTURER table. Notice that the generated manufacturer entity contains the bean validation constraints derived from the database schema, yet another new feature in 7.0 M2. More on this topic in a later blog.
- Generate the metamodel classes by right-clicking on the project and selecting "Clean and Build". The generated metamodel class looks like:
package org.glassfish.samples.entities; import javax.annotation.Generated; import javax.persistence.metamodel.SingularAttribute; import javax.persistence.metamodel.StaticMetamodel; @Generated("EclipseLink-2.1.0.v20100614-r7608 @ Mon Oct 25 16:35:03 PDT 2010") @StaticMetamodel(Manufacturer.class) public class Manufacturer_ { public static volatile SingularAttribute addressline2; public static volatile SingularAttribute zip; public static volatile SingularAttribute phone; public static volatile SingularAttribute addressline1; public static volatile SingularAttribute fax; public static volatile SingularAttribute manufacturerId; public static volatile SingularAttribute email; public static volatile SingularAttribute name; public static volatile SingularAttribute state; public static volatile SingularAttribute city; public static volatile SingularAttribute rep; }
This is shown as "Generated Sources" in NetBeans IDE as shown:
- Generate a new Java class "DatabaseBean" and mark it with "@javax.enterprise.inject.Model" annotation. This class will be the "backing bean" for the JSF pages and will have
- A field to accept the manufacturer’s name from "index.xhtml"
- A field to show information about the searched manufacturer in "show.xhtml"
- A business method "searchManufacturer" that searches the database for the given manufacturer’s name. This method will use the generated metamodel class and type-safe Criteria API to query the database.
The complete source code for the class looks like:
@PersistenceUnit EntityManagerFactory emf; String name; Manufacturer manufacturer; public String getName() { return name; } public void setName(String name) { this.name = name; } public Manufacturer getManufacturer() { return manufacturer; } public void searchManufacturer() { EntityManager em = emf.createEntityManager(); CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery criteria = builder.createQuery(Manufacturer.class); // FROM clause Root root = criteria.from(Manufacturer.class); // SELECT clause criteria.select(root); // WHERE clause Predicate condition = builder.like(root.get(Manufacturer_.name), "%" + name + "%"); criteria.where(condition); // FIRE query TypedQuery query = em.createQuery(criteria); // PRINT result List manufacturers = query.getResultList(); if (manufacturers != null && manufacturers.size() > 0) { manufacturer = (Manufacturer)manufacturers.get(0); } }
The business method returns the first manufacturer whose name contains the text entered in the textbox. No validation is performed in order to keep the business logic simple.
Notice how "searchManufacturer" method is not using any String-based identifiers for constructing the query graph. This gives the complete type-safety for query construction and allows the errors to be detected much earlier.
- Edit the generated "index.xhtml" such that the content within <h:body> looks like:
<h:form> <h:panelGrid columns="3"> <h:outputText value="Name:" /> <h:inputText value="#{databaseBean.name}" id="name"/> </h:panelGrid> <h:commandButton actionListener="#{databaseBean.searchManufacturer}" action="show" value="submit"/> </h:form>
This page shows a text box and a submit button. The "searchManufacturer" method of the DatabaseBean is invoked when the "submit" button is clicked and passes the entered text in the "name"property of the DatabaseBean.
- Create a new XHTML page and name it "show.xhtml". Replace the generated boilerplate code with the code given below:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>Show Manufacturer's Detail</title> </head> <body> Name: #{databaseBean.manufacturer.name}<br/> Phone: #{databaseBean.manufacturer.phone}<br/> Address Line1: #{databaseBean.manufacturer.addressline1}<br/> Address Line2: #{databaseBean.manufacturer.addressline2}<br/> </body> </html>
Now you can deploy the application to GlassFish by usual means and access "http://localhost:8080/CriteriaMetamodel/faces/index.xhtml" which gets displayed as:
Enter some value as "S" in the text box and click on "Submit" to display the result as:
The complete source code for this sample can be downloaded here.
Now Criteria query is little verbose but it does give you the type-safety and was explicitly asked within the JPA Expert Group. It allows you to manipulate different parts of a query such as SELECT, FROM, and WHERE clauses and that too using the Java type system. This reminds me of Mr Potato Head
This behavior can be achieved in JPQL but is available exclusively using String manipulation and the errors are not detected until runtime. |
How are you using Criteria API ?
What use cases would you like to see solved in JPA2.next ?
Technorati: totd jpa2 criteria metamodel netbeans javaee6 glassfish