This chapter covers
2.2 Starting a Java Persistence project
2.2.2 Using Hibernate EntityManager
Hibernate EntityManager is a wrapper around Hibernate Core that provides the JPA programming interfaces, supports the JPA entity instance lifecycle, and allows you to write queries with the standardized Java Persistence query language.
Because JPA functionality is a subset of Hibernate’s native capabilities, you may wonder why you should use the EntityManager package on top of Hibernate.
We’ll present a list of advantages later in this section, but you’ll see one particular simplification as soon as you configure your project for Hibernate EntityManager:
You no longer have to list all annotated classes (or XML mapping files) in your configuration file.
Let’s modify the “Hello World” project and prepare it for full JPA compatibility.
Basic JPA configuration
A SessionFactory represents a particular logical data-store configuration in a Hibernate application. The EntityManagerFactory has the same role in a JPA application, and you configure an EntityManagerFactory (EMF) either with con-figuration files or in application code just as you would configure a SessionFac-tory. The configuration of an EMF, together with a set of mapping metadata (usually annotated classes), is called the persistence unit.
The notion of a persistence unit also includes the packaging of the applica-tion, but we want to keep this as simple as possible for “Hello World”; we’ll assume that you want to start with a standardized JPA configuration and no special packag-ing. Not only the content, but also the name and location of the JPA configuration file for a persistence unit are standardized.
Create a directory named WORKDIR/etc/META-INF and place the basic config-uration file named persistence.xml, shown in listing 2.11, in that directory:
<persistence 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_1_0.xsd"
version="1.0">
<persistence-unit name="helloworld">
<properties>
<property name="hibernate.ejb.cfgfile"
value="/hibernate.cfg.xml"/>
</properties>
</persistence-unit>
</persistence>
Every persistence unit needs a name, and in this case it’s helloworld.
NOTE The XML header in the preceding persistence unit configuration file declares what schema should be used, and it’s always the same. We’ll omit it in future examples and assume that you’ll add it.
A persistence unit is further configured with an arbitrary number of properties, which are all vendor-specific. The property in the previous example, hiber-nate.ejb.cfgfile, acts as a catchall. It refers to a hibernate.cfg.xml file (in the root of the classpath) that contains all settings for this persistence unit—you’re reusing the existing Hibernate configuration. Later, you’ll move all configuration details into the persistence.xml file, but for now you’re more interested in run-ning “Hello World” with JPA.
The JPA standard says that the persistence.xml file needs to be present in the META-INF directory of a deployed persistence unit. Because you aren’t really pack-aging and deploying the persistence unit, this means that you have to copy persis-tence.xml into a META-INF directory of the build output directory. Modify your build.xml, and add the following to the copymetafiles target:
<property name="src.etc.dir" value="etc"/>
<target name="copymetafiles">
<!-- Copy metadata to build -->
<copy todir="${build.dir}">
<fileset dir="${src.java.dir}">
<patternset refid="meta.files"/>
</fileset>
</copy>
Listing 2.11 Persistence unit configuration file
<!-- Copy configuration files from etc/ -->
<copy todir="${build.dir}">
<fileset dir="${src.etc.dir}">
<patternset refid="meta.files"/>
</fileset>
</copy>
</target>
Everything found in WORKDIR/etc that matches the meta.files pattern is copied to the build output directory, which is part of the classpath at runtime.
Let’s rewrite the main application code with JPA.
“Hello World” with JPA
These are your primary programming interfaces in Java Persistence:
■ javax.persistence.Persistence—A startup class that provides a static method for the creation of an EntityManagerFactory.
■ javax.persistence.EntityManagerFactory—The equivalent to a Hiber-nate SessionFactory. This runtime object represents a particular persis-tence unit. It’s thread-safe, is usually handled as a singleton, and provides methods for the creation of EntityManager instances.
■ javax.persistence.EntityManager—The equivalent to a Hibernate Ses-sion. This single-threaded, nonshared object represents a particular unit of work for data access. It provides methods to manage the lifecycle of entity instances and to create Query instances.
■ javax.persistence.Query—This is the equivalent to a Hibernate Query. An object is a particular JPA query language or native SQL query representa-tion, and it allows safe binding of parameters and provides various methods for the execution of the query.
■ javax.persistence.EntityTransaction—This is the equivalent to a Hibernate Transaction, used in Java SE environments for the demarcation of RESOURCE_LOCAL transactions. In Java EE, you rely on the standardized javax.transaction.UserTransaction interface of JTA for programmatic transaction demarcation.
To use the JPA interfaces, you need to copy the required libraries to your WORKDIR/lib directory; check the documentation bundled with Hibernate EntityManager for an up-to-date list. You can then rewrite the code in WORKDIR/ src/hello/HelloWorld.java and switch from Hibernate to JPA interfaces (see listing 2.12).
package hello;
import java.util.*;
import javax.persistence.*;
public class HelloWorld {
public static void main(String[] args) { // Start EntityManagerFactory
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("helloworld");
// First unit of work
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Message message = new Message("Hello World");
em.persist(message);
tx.commit();
em.close();
// Second unit of work
EntityManager newEm = emf.createEntityManager();
EntityTransaction newTx = newEm.getTransaction();
newTx.begin();
List messages = newEm
.createQuery("select m from Message m ➥ order by m.text asc")
.getResultList();
System.out.println( messages.size() + " message(s) found" );
for (Object m : messages) {
Message loadedMsg = (Message) m;
System.out.println(loadedMsg.getText());
}
newTx.commit();
newEm.close();
// Shutting down the application emf.close();
} }
Listing 2.12 The “Hello World” main application code with JPA
The first thing you probably notice in this code is that there is no Hibernate import anymore, only javax.peristence.*. The EntityManagerFactory is cre-ated with a static call to Persistence and the name of the persistence unit. The rest of the code should be self-explanatory—you use JPA just like Hibernate, though there are some minor differences in the API, and methods have slightly different names. Furthermore, you didn’t use the HibernateUtil class for static initialization of the infrastructure; you can write a JPAUtil class and move the cre-ation of an EntityManagerFactory there if you want, or you can remove the now unused WORKDIR/src/persistence package.
JPA also supports programmatic configuration, with a map of options:
Map myProperties = new HashMap();
myProperties.put("hibernate.hbm2ddl.auto", "create-drop");
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("helloworld", myProperties);
Custom programmatic properties override any property you’ve set in the persis-tence.xml configuration file.
Try to run the ported HelloWorld code with a fresh database. You should see the exact same log output on your screen as you did with native Hibernate—the JPA persistence provider engine is Hibernate.
Automatic detection of metadata
We promised earlier that you won’t have to list all your annotated classes or XML mapping files in the configuration, but it’s still there, in hibernate.cfg.xml. Let’s enable the autodetection feature of JPA.
Run the “Hello World” application again after switching to DEBUG logging for the org.hibernate package. Some additional lines should appear in your log:
...
Ejb3Configuration:141
- Trying to find persistence unit: helloworld Ejb3Configuration:150
- Analyse of persistence.xml:
file:/helloworld/build/META-INF/persistence.xml PersistenceXmlLoader:115
- Persistent Unit name from persistence.xml: helloworld Ejb3Configuration:359
- Detect class: true; detect hbm: true JarVisitor:178
- Searching mapped entities in jar/par: file:/helloworld/build JarVisitor:217
- Filtering: hello.HelloWorld JarVisitor:217
- Filtering: hello.Message
JarVisitor:255
- Java element filter matched for hello.Message Ejb3Configuration:101
- Creating Factory: helloworld ...
On startup, the Persistence.createEntityManagerFactory() method tries to locate the persistence unit named helloworld. It searches the classpath for all META-INF/persistence.xml files and then configures the EMF if a match is found.
The second part of the log shows something you probably didn’t expect. The JPA persistence provider tried to find all annotated classes and all Hibernate XML mapping files in the build output directory. The list of annotated classes (or the list of XML mapping files) in hibernate.cfg.xml isn’t needed, because hello.Mes-sage, the annotated entity class, has already been found.
Instead of removing only this single unnecessary option from hiber-nate.cfg.xml, let’s remove the whole file and move all configuration details into persistence.xml (see listing 2.13).
<persistence-unit name="helloworld">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<!-- Not needed, Hibernate supports auto-detection in JSE <class>hello.Message</class>
-->
<properties>
<property name="hibernate.archive.autodetection"
value="class, hbm"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.connection.driver_class"
value="org.hsqldb.jdbcDriver"/>
<property name="hibernate.connection.url"
value="jdbc:hsqldb:hsql://localhost"/>
<property name="hibernate.connection.username"
value="sa"/>
<property name="hibernate.c3p0.min_size"
value="5"/>
<property name="hibernate.c3p0.max_size"
value="20"/>
<property name="hibernate.c3p0.timeout"
value="300"/>
<property name="hibernate.c3p0.max_statements"
value="50"/>
Listing 2.13 Full persistence unit configuration file
<property name="hibernate.c3p0.idle_test_period"
value="3000"/>
<property name="hibernate.dialect"
value="org.hibernate.dialect.HSQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="create"/>
</properties>
</persistence-unit>
There are three interesting new elements in this configuration file. First, you set an explicit <provider> that should be used for this persistence unit. This is usu-ally required only if you work with several JPA implementations at the same time, but we hope that Hibernate will, of course, be the only one. Next, the specifica-tion requires that you list all annotated classes with <class> elements if you deploy in a non-Java EE environment—Hibernate supports autodetection of map-ping metadata everywhere, making this optional. Finally, the Hibernate configuration setting archive.autodetection tells Hibernate what metadata to scan for automatically: annotated classes (class) and/or Hibernate XML map-ping files (hbm). By default, Hibernate EntityManager scans for both. The rest of the configuration file contains all options we explained and used earlier in this chapter in the regular hibernate.cfg.xml file.
Automatic detection of annotated classes and XML mapping files is a great fea-ture of JPA. It’s usually only available in a Java EE application server; at least, this is what the EJB 3.0 specification guarantees. But Hibernate, as a JPA provider, also implements it in plain Java SE, though you may not be able to use the exact same configuration with any other JPA provider.
You’ve now created an application that is fully JPA specification-compliant.
Your project directory should look like this (note that we also moved log4j.proper-ties to the etc/ directory):
WORKDIR +etc
log4j.properties +META-INF
persistence.xml +lib
<all required libraries>
+src +hello
HelloWorld.java Message.java
All JPA configuration settings are bundled in persistence.xml, all mapping meta-data is included in the Java source code of the Message class, and Hibernate
automatically scans and finds the metadata on startup. Compared to pure Hiber-nate, you now have these benefits:
■ Automatic scanning of deployed metadata, an important feature in large projects. Maintaining a list of annotated classes or mapping files becomes difficult if hundreds of entities are developed by a large team.
■ Standardized and simplified configuration, with a standard location for the configuration file, and a deployment concept—the persistence unit—that has many more advantages in larger projects that wrap several units (JARs) in an application archive (EAR).
■ Standardized data access code, entity instance lifecycle, and queries that are fully portable. There is no proprietary import in your application.
These are only some of the advantages of JPA. You’ll see its real power if you com-bine it with the full EJB 3.0 programming model and other managed components.