Chapter 2. Starting a project

In this chapter

  • Overview of Hibernate projects
  • “Hello World” with Hibernate and Java Persistence
  • Configuration and integration options

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.

2.1. Introducing Hibernate

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:

  • Hibernate ORM— Hibernate ORM consists of a core, a base service for persistence with SQL databases, and a native proprietary API. Hibernate ORM is the foundation for several of the other projects and is the oldest Hibernate project. You can use Hibernate ORM on its own, independent of any framework or any particular runtime environment with all JDKs. It works in every Java EE/J2EE application server, in Swing applications, in a simple servlet container, and so on. As long as you can configure a data source for Hibernate, it works.
  • Hibernate EntityManager— This is Hibernate’s implementation of the standard Java Persistence APIs, an optional module you can stack on top of Hibernate ORM. You can fall back to Hibernate when a plain Hibernate interface or even a JDBC Connection is needed. Hibernate’s native features are a superset of the JPA persistence features in every respect.
  • Hibernate Validator— Hibernate provides the reference implementation of the Bean Validation (JSR 303) specification. Independent of other Hibernate projects, it provides declarative validation for your domain model (or any other) classes.
  • Hibernate Envers— Envers is dedicated to audit logging and keeping multiple versions of data in your SQL database. This helps you add data history and audit trails to your application, similar to version control systems you might already be familiar with such as Subversion and Git.
  • Hibernate Search— Hibernate Search keeps an index of your domain model data up to date in an Apache Lucene database. It lets you query this database with a powerful and naturally integrated API. Many projects use Hibernate Search in addition to Hibernate ORM, adding full-text search capabilities. If you have a free text search form in your application’s user interface, and you want happy users, work with Hibernate Search. Hibernate Search isn’t covered in this book; you can find more information in Hibernate Search in Action by Emmanuel Bernard (Bernard, 2008).
  • Hibernate OGM— The most recent Hibernate project is the object/grid mapper. It provides JPA support for NoSQL solutions, reusing the Hibernate core engine but persisting mapped entities into a key/value-, document-, or graph-oriented data store. Hibernate OGM isn’t covered in this book.

Let’s get started with your first Hibernate and JPA project.

2.2. “Hello World” with JPA

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.

2.2.1. Configuring a persistence unit

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:

Path: /model/src/main/resources/META-INF/persistence.xml

  1. The persistence.xml file configures at least one persistence unit; each unit must have a unique name.
  2. Each persistence unit must have a database connection. Here you delegate to an existing java.sql.DataSource. Hibernate will find the data source by name with a JNDI lookup on startup.
  3. A persistent unit has persistent (mapped) classes. You list them here.
  4. Hibernate can scan your classpath for mapped classes and add them automatically to your persistence unit. This setting disables that feature.
  5. Standard or vendor-specific options can be set as properties on a persistence unit. Any standard properties have the javax.persistence name prefix; Hibernate’s settings use hibernate.
  6. The JPA engine should drop and re-create the SQL schema in the database automatically when it boots. This is ideal for automated testing, when you want to work with a clean database for every test run.

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.

Logging SQL

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.

2.2.2. Writing a persistent class

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:

Path: /model/src/main/java/org/jpwh/model/helloworld/Message.java

  1. Every persistent entity class must have at least the @Entity annotation. Hibernate maps this class to a table called MESSAGE.
  2. Every persistent entity class must have an identifier attribute annotated with @Id. Hibernate maps this attribute to a column named ID.
  3. Someone must generate identifier values; this annotation enables automatic generation of IDs.
  4. You usually implement regular attributes of a persistent class with private or protected fields and public getter/setter method pairs. Hibernate maps this attribute to a column called TEXT.

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.

2.2.3. Storing and loading messages

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:

Path: /examples/src/est/java/org/jpwh/helloworld/HelloWorldJPA.java

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:

Path: /examples/src/test/java/org/jpwh/helloworld/HelloWorldJPA.java

  1. Get access to the standard transaction API UserTransaction, and begin a transaction on this thread of execution.
  2. Begin a new session with the database by creating an EntityManager. This is your context for all persistence operations.
  3. Create a new instance of the mapped domain model class Message, and set its text property.
  4. Enlist the transient instance with your persistence context; you make it persistent. Hibernate now knows that you wish to store that data, but it doesn’t necessarily call the database immediately.
  5. Commit the transaction. Hibernate automatically checks the persistence context and executes the necessary SQL INSERT statement.
  6. If you create an EntityManager, you must close it.

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:

Path: /examples/src/test/java/org/jpwh/helloworld/HelloWorldJPA.java

  1. Every interaction with your database should occur within explicit transaction boundaries, even if you’re only reading data.
  2. Execute a query to retrieve all instances of Message from the database.
  3. You can change the value of a property. Hibernate detects this automatically because the loaded Message is still attached to the persistence context it was loaded in.
  4. On commit, Hibernate checks the persistence context for dirty state and executes the SQL UPDATE automatically to synchronize in-memory with the database state.

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.

2.3. Native Hibernate configuration

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:

Path: /examples/src/test/java/org/jpwh/helloworld/HelloWorldHibernate.java

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:

Path: /examples/src/test/java/org/jpwh/helloworld/HelloWorldHibernate.java

  1. This builder helps you create the immutable service registry with chained method calls.
  2. Configure the services registry by applying settings.

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:

Path: /examples/src/test/java/org/jpwh/helloworld/HelloWorldHibernate.java

  1. This builder helps you create the immutable service registry with chained method calls.
  2. Configure the services registry by applying settings.

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:

Path: /examples/src/test/java/org/jpwh/helloworld/HelloWorldHibernate.java

Metadata metadata = metadataBuilder.build();
assertEquals(metadata.getEntityBindings().size(), 1);
SessionFactory sessionFactory = metadata.buildSessionFactory();
Creating an EntityManagerFactory from a SessionFactory

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():

Path: /examples/src/ test/java/org/jpwh/helloworld/HelloWorldHibernate.java

  1. Get access to the standard transaction API UserTransaction, and begin a transaction on this thread of execution.
  2. Whenever you call getCurrentSession() in the same thread, you get the same org.hibernate.Session. It’s bound automatically to the ongoing transaction and is closed for you automatically when that transaction commits or rolls back.
  3. The native Hibernate API is very similar to the standard Java Persistence API, and most methods have the same names.
  4. Hibernate synchronizes the session with the database and automatically closes the “current” session on commit of the bound transaction.

Accessing the current Session results in compact code:

Path: /examples/src/test/java/org/jpwh/helloworld/HelloWorldHibernate.java

  1. A Hibernate criteria query is a type-safe programmatic way to express queries, automatically translated into SQL.

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.

2.4. Summary

  • You’ve completed your first JPA project.
  • You wrote a persistent class and its mapping with annotations.
  • You’ve seen how to configure and bootstrap a persistence unit, and how to create the EntityManagerFactory entry point. Then you called the Entity-Manager to interact with the database, storing and loading instances of your persistent domain model class.
  • We discussed some of the more advanced native Hibernate bootstrap and configuration options, as well as the equivalent basic Hibernate APIs, Session-Factory and Session.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.172.38