In this chapter, you’ll start with Hibernate and Java Persistence using a step-by-step example. You’ll see both persistence APIs and how to benefit from using either native Hibernate or standardized JPA. We first offer you a tour through Hibernate with a straightforward “Hello World” application. Before you start coding, you must decide which Hibernate modules to use in your project.
Hibernate is an ambitious project that aims to provide a complete solution to the problem of managing persistent data in Java. Today, Hibernate is not only an ORM service, but also a collection of data management tools extending well beyond ORM.
The Hibernate project suite includes the following:
Let’s get started with your first Hibernate and JPA project.
In this section, you’ll write your first Hibernate application, which stores a “Hello World” message in the database and then retrieves it. Let’s start by installing and configuring Hibernate.
We use Apache Maven as the project build tool, as we do for all the examples in this book. Declare the dependency on Hibernate:
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>5.0.0.Final</version> </dependency>
The hibernate-entitymanager module includes transitive dependencies on other modules you’ll need, such as hibernate-core and the Java Persistence interface stubs.
Your starting point in JPA is the persistence unit. A persistence unit is a pairing of your domain model class mappings with a database connection, plus some other configuration settings. Every application has at least one persistence unit; some applications have several if they’re talking to several (logical or physical) databases. Hence, your first step is setting up a persistence unit in your application’s configuration.
The standard configuration file for persistence units is located on the classpath in META-INF/persistence.xml. Create the following configuration file for the “Hello World” application:
H When printing SQL in logs, let Hibernate format the SQL nicely and generate comments into the SQL string so you know why Hibernate executed the SQL statement.
Most applications need a pool of database connections, with a certain size and optimized thresholds for the environment. You also want to provide the DBMS host and credentials for your database connections.
All SQL statements executed by Hibernate can be logged—an invaluable tool during optimization. To log SQL, in persistence.xml, set the properties hibernate.format_sql and hibernate.use_sql_comments to true. This will cause Hibernate to format SQL statements with causation comments. Then, in your logging configuration (which depends on your chosen logging implementation), set the categories org.hibernate.SQL and org.hibernate.type.descriptor.sql.BasicBinder to the finest debug level. You’ll then see all SQL statements executed by Hibernate in your log output, including the bound parameter values of prepared statements.
For the “Hello World” application, you delegate database connection handling to a Java Transaction API (JTA) provider, the open source Bitronix project. Bitronix offers connection pooling with a managed java.sql.DataSource and the standard javax.transaction.UserTransaction API in any Java SE environment. Bitronix binds these objects into JNDI, and Hibernate interfaces automatically with Bitronix through JNDI lookups. Setting up Bitronix in detail is outside of the scope of this book; you can find the configuration for our examples in org.jpwh.env.TransactionManagerSetup.
In the “Hello World” application, you want to store messages in the database and load them from the database. Hibernate applications define persistent classes that are mapped to database tables. You define these classes based on your analysis of the business domain; hence, they’re a model of the domain. This example consists of one class and its mapping.
Let’s see what a simple persistent class looks like, how the mapping is created, and some of the things you can do with instances of the persistent class in Hibernate.
The objective of this example is to store messages in a database and retrieve them for display. The application has a simple persistent class, Message:
The identifier attribute of a persistent class allows the application to access the database identity—the primary key value—of a persistent instance. If two instances of Message have the same identifier value, they represent the same row in the database.
This example uses Long for the type of the identifier attribute, but this isn’t a requirement. Hibernate allows virtually anything for the identifier type, as you’ll see later.
You may have noticed that the text attribute of the Message class has JavaBeans-style property accessor methods. The class also has a (default) constructor with no parameters. The persistent classes we show in the examples will usually look something like this. Note that you don’t need to implement any particular interface or extend any special superclass.
Instances of the Message class can be managed (made persistent) by Hibernate, but they don’t have to be. Because the Message object doesn’t implement any persistence-specific classes or interfaces, you can use it just like any other Java class:
Message msg = new Message(); msg.setText("Hello!"); System.out.println(msg.getText());
It may look like we’re trying to be cute here; in fact, we’re demonstrating an important feature that distinguishes Hibernate from some other persistence solutions. You can use the persistent class in any execution context—no special container is needed.
You don’t have to use annotations to map a persistent class. Later we’ll show you other mapping options, such as the JPA orm.xml mapping file, and native hbm.xml mapping files, and when they’re a better solution than source annotations.
The Message class is now ready. You can store instances in your database and write queries to load them again into application memory.
What you really came here to see is Hibernate, so let’s save a new Message to the database. First you need an EntityManagerFactory to talk to your database. This API represents your persistence unit; most applications have one EntityManagerFactory for one configured persistence unit:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("HelloWorldPU");
Once it starts, your application should create the EntityManagerFactory; the factory is thread-safe, and all code in your application that accesses the database should share it.
You can now work with the database in a demarcated unit—a transaction—and store a Message:
To help you understand how Hibernate works, we show the automatically generated and executed SQL statements in source code comments when they occur. Hibernate inserts a row in the MESSAGE table, with an automatically generated value for the ID primary key column, and the TEXT value.
You can later load this data with a database query:
The query language you’ve seen in this example isn’t SQL, it’s the Java Persistence Query Language (JPQL). Although there is syntactically no difference in this trivial example, the Message in the query string doesn’t refer to the database table name, but to the persistent class name. If you map the class to a different table, the query will still work.
Also, notice how Hibernate detects the modification to the text property of the message and automatically updates the database. This is the automatic dirty-checking feature of JPA in action. It saves you the effort of explicitly asking your persistence manager to update the database when you modify the state of an instance inside a transaction.
You’ve now completed your first Hibernate and JPA application. Maybe you’ve already noticed that we prefer to write examples as executable tests, with assertions that verify the correct outcome of each operation. We’ve taken all the examples in this book from test code, so you (and we) can be sure they work properly. Unfortunately, this also means you need more than one line of code to create the EntityManagerFactory when starting the test environment. We’ve tried to keep the setup of the tests as simple as possible. You can find the code in org.jpwh.env.JPASetup and org.jpwh.env.JPATest; use it as a starting point for writing your own test harness.
Before we work on more-realistic application examples, let’s have a quick look at the native Hibernate bootstrap and configuration API.
Although basic (and extensive) configuration is standardized in JPA, you can’t access all the configuration features of Hibernate with properties in persistence.xml. Note that most applications, even quite sophisticated ones, don’t need such special configuration options and hence don’t have to access the bootstrap API we show in this section. If you aren’t sure, you can skip this section and come back to it later, when you need to extend Hibernate type adapters, add custom SQL functions, and so on.
The native equivalent of the standard JPA EntityManagerFactory is the org.hibernate.SessionFactory. You have usually one per application, and it’s the same pairing of class mappings with database connection configuration.
Hibernate’s native bootstrap API is split into several stages, each giving you access to certain configuration aspects. In its most compact form, building a Session-Factory looks like this:
SessionFactory sessionFactory = new MetadataSources( new StandardServiceRegistryBuilder() .configure("hibernate.cfg.xml").build() ).buildMetadata().buildSessionFactory();
This loads all settings from a Hibernate configuration file. If you have an existing Hibernate project, you most likely have this file on your classpath. Similar to persistence.xml, this configuration file contains database connection details, as well as a list of persistent classes and other configuration properties.
Let’s deconstruct this bootstrap snippet and look at the API in more detail. First, create a ServiceRegistry:
If you want to externalize your service registry configuration, you can load settings from a properties file on the classpath with StandardServiceRegistryBuilder#load-Properties(file).
With the ServiceRegistry built and immutable, you can move on to the next stage: telling Hibernate which persistent classes are part of your mapping metadata. Configure the metadata sources as follows:
The MetadataSources API has many methods for adding mapping sources; check the Javadoc for more information. The next stage of the boot procedure is building all the metadata needed by Hibernate, with the MetadataBuilder you obtained from the metadata sources.
You can then query the metadata to interact with Hibernate’s completed configuration programmatically, or continue and build the final SessionFactory:
Metadata metadata = metadataBuilder.build(); assertEquals(metadata.getEntityBindings().size(), 1); SessionFactory sessionFactory = metadata.buildSessionFactory();
At the time of writing, Hibernate has no convenient API to build an EntityManager-Factory programmatically. You can use an internal API for this purpose: the org.hibernate.jpa.internal.EntityManagerFactoryImpl has a constructor that accepts a SessionFactory.
Let’s see if this configuration works by storing and loading a message with Hibernate’s native equivalent of EntityManager, org.hibernate.Session. You can create a Session with the SessionFactory, and you must close it just as you have to close your own EntityManager.
Or, using another Hibernate feature, you can let Hibernate take care of creating and closing the Session with SessionFactory#getCurrentSession():
Accessing the current Session results in compact code:
Most of the examples in this book don’t use the SessionFactory or Session API. From time to time, when a particular feature is only available in Hibernate, we show you how to unwrap() the native interface given a standard API.
3.144.172.38