Chapter 1. Starting with Hibernate

An object model uses the principles of abstraction, encapsulation, modularity, hierarchy, typing, concurrency, polymorphism, and persistence. The object model enables you to create well-structured, complex systems. In an object model system, objects are the components of the system. Objects are instances of classes, and classes are related to other classes via inheritance relationships. An object has an identity, a state, and behavior. An object model helps you create reusable application frameworks and systems that can evolve over time. In addition, object-oriented systems are usually smaller than non-object-oriented implementations.

A relational model defines the structure of data, data manipulation, and data integrity. Data is organized in the form of tables, and different tables are associated by means of referential integrity (a foreign key). Integrity constraints such as a primary key, unique check constraints, and not null are used to maintain an entity's integrity in the relational model.

A relational data model isn't focused on supporting entity-type inheritance: entity-based polymorphic association from an object model can't be translated into similar entities in a relational model. In an object model, you use the state of the model to define equality between objects. But in a relational model, you use an entity's primary key to define equality of entities. Object references are used to associate different objects in an object model, whereas a foreign key is used to establish associations in a relational model. Object references in the object model facilitate easier navigation through the object graph.

Because these two models are distinctly different, you need a way to persist object entities (Java objects) into a relational database. Figures 1-1 and 1-2 provide a simple representation of the object model and the relational model.

Entity-relationship (ER) diagram of Book and Publisher

Figure 1-1. Entity-relationship (ER) diagram of Book and Publisher

Class diagram of Book and Publisher

Figure 1-2. Class diagram of Book and Publisher

Object/relational mapping (ORM) frameworks help you take advantage of the features present in the object model (like Java) and the relational model (like database management systems [DBMS]). With the help of ORM frameworks, you can persist objects in Java to relational tables using metadata that describes the mapping between the objects and the database. The metadata shields the complexity of dealing directly with SQL and helps you develop solutions in terms of business objects.

An ORM solution can be implemented at various levels:

  • Pure relational: An application is designed around the relational model.

  • Light object mapping: Entities are represented as classes and are mapped manually to relational tables.

  • Medium object mapping: An application is designed using an object model, and SQL is generated during build time using code-generation utilities.

  • Full object mapping: This mapping supports sophisticated object modeling including composition, inheritance, polymorphism, and persistence by reachability.

The following are the benefits of using an ORM framework:

  • Productivity: Because you use metadata to persist and query data, development time decreases and productivity increases.

  • Prototyping: Using an ORM framework is extremely useful for quick prototyping.

  • Maintainability: Because much of the work is done through configuration, your code has fewer lines and thus requires less maintenance.

  • Vendor independence: An ORM abstracts an application from the underlying SQL database and SQL dialect. This gives you the portability to support multiple databases.

ORM frameworks also have some disadvantages:

  • Learning curve: You may experience a steep learning curve as you learn how to map and, possibly, learn a new query language.

  • Overhead: For simple applications that use a single database and data without many business requirements for complex querying, an ORM framework can be extra overhead.

  • Slower performance: For large batch updates, performance is slower.

Hibernate is one of the most widely used ORM frameworks in the industry. It provides all the benefits of an ORM solution and implements the Java Persistence API (JPA) defined in the Enterprise JavaBeans (EJB) 3.0 specification.

Its main components are as follows:

  • Hibernate Core: The Core generates SQL and relieves you from manually handling Java Database Connectivity (JDBC) result sets and object conversions. Metadata is defined in simple XML files. The Core offers various options for writing queries: plain SQL; Hibernate Query Language (HQL), which is specific to Hibernate; programmatic criteria, or Query by Example (QBE). It can optimize object loading with various fetching and caching options.

  • Hibernate Annotations: With the introduction of Annotations in JDK 5.0, Hibernate provides the option of defining metadata using annotations. This reduces configuration using XML files and makes it simple to define required metadata directly in the Java source code.

  • Hibernate EntityManager: The JPA specification defines programming interfaces, lifecycle rules for persistent objects, and query features. The Hibernate implementation for this part of the JPA is available as Hibernate EntityManager.

This book provides solutions using Hibernate Core and Annotations for each problem. The Hibernate version used is 3.3.2.

Setting Up Hibernate

Problem

What tools and libraries are required to set up Hibernate and get started?

Solution

You need JDK 1.5+, an IDE such as Eclipse, a database (this book uses Apache Derby), and SQL Squirrel to provide a GUI to use the database. You can also use Maven to configure your project. Maven is a software project-management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project's build, reporting, and documentation from a central piece of information. In Maven, the POM.XML is the central piece where all the information is stored.

The following libraries are required for the Hibernate 3.3.2 setup:

  • Hibernate3.jar

  • Hibernate-commons-annotations.jar

  • Hibernate-annotations.jar

  • Hibernate-entitymanager.jar

  • Antlr-2.7.6.jar

  • Commons-collections-3.1.jar

  • Dom4j-1.6.1.jar

  • Javassist-3.9.0.GA.jar

  • Jta-1.1.jar

  • Slf4j-api-1.5.8.jar

  • Ejb3-persistence.jar

  • Slf4j-simple1.5.8.jar

The following are required for the Derby setup :

  • Derby.jar

  • Derbyclient.jar

  • Derbynet.jar

  • Derbytools.

How It Works

The next few sections describe how to set up each of the required tools and then provide the solution to the problem. All the solutions are provided on a Windows platform. They can also be implemented on UNIX, provided you install and download the libraries and executables specific to the UNIX platform wherever applicable.

Installing the JDK

The JDK is an essential toolkit provided for Java application development. You can go to http://java.sun.com/j2se/1.5.0/download.jsp to download JDK 5.0. Install it into a folder such as C:jdk1.5.0.

Installing the Eclipse Web Tools Platform (WTP)

Eclipse is an IDE for developing Java applications. The latest version is Galileo. You can install it from the following URL: www.eclipse.org/downloads/download.php?file=/technology/epp/downloads/release/galileo/SR1/eclipse-jee-galileo-SR1-win32.zip.

Installing Derby

Derby is an open source SQL relational database engine written in Java. You can go to http://db.apache.org/derby/derby_downloads.html and download the latest version. Derby also provides plug-ins for Eclipse. The plug-in gives you the required jar files for development and also provides a command prompt (ij) in Eclipse to execute Data Definition Language (DDL) and Data Manipulation Language (DML) statements.

Creating a Derby Database Instance

To create a new Derby database called BookShopDB at the ij prompt, use the following command:

connect 'jdbc:derby://localhost:1527/BookShopDB;create=true;
user=book;password=book';

After the database is created, execute the SQL scripts in the next section to create the tables.

Creating the Tables (Relational Model)

These solutions use the example of a bookshop. Books are published by a publisher, and the contents of a book are defined by the chapters. The entities Book, Publisher, and Chapter are stored in the database; you can perform various operations such as reading, updating, and deleting.

Because an ORM is a mapping between an object model and a relational model, you first create the relational model by executing the DDL statements to create the tables/entities in the database. You later see the object model in Java and finally the mapping between the relational and the object models.

Create the tables for the online bookshop using the following SQL statements:

CREATE TABLE  PUBLISHER (
       CODE VARCHAR(4) NOT NULL ,
       PUBLISHER_NAME VARCHAR(100) NOT NULL,        ADDRESS VARCHAR(200),  PRIMARY KEY (CODE)
);
CREATE TABLE  BOOK
   (ISBN VARCHAR(50) NOT NULL,
    BOOK_NAME VARCHAR(100) NOT NULL,
    PUBLISHER_CODE VARCHAR(4),     PUBLISH_DATE DATE,
    PRICE  integer,
    PRIMARY KEY (ISBN),     FOREIGN KEY (PUBLISHER_CODE)
    REFERENCES  PUBLISHER (CODE)
   );
CREATE TABLE  CHAPTER
   (BOOK_ISBN VARCHAR(50) NOT NULL,
     IDX integer NOT NULL,
     TITLE VARCHAR(100) NOT NULL,
     NUM_OF_PAGES integer,
     PRIMARY KEY (BOOK_ISBN, IDX),
     FOREIGN KEY (BOOK_ISBN)
     REFERENCES  BOOK (ISBN)
   );

Figure 1-3 shows the entity model for the sample table structure.

Relational model diagram for the bookshop

Figure 1-3. Relational model diagram for the bookshop

Next, let's input some data for these tables using |the following SQL statements:

insert into PUBLISHER(code, publisher_name, address)
values ('001', 'Apress', 'New York ,New York'),
insert into PUBLISHER(code, publisher_name, address)
values ('002', 'Manning', 'San Francisco', 'CA')
insert into book(isbn, book_name, publisher_code, publish_date, price)
values ('PBN123', 'Spring Recipes', '001', DATE('2008-02-02'), 30)
insert into book(isbn, book_name, publisher_code, publish_date, price)
values ('PBN456', 'Hibernate Recipes', '002', DATE('2008-11-02'), 40)

Programming with Basic JDBC

Problem

The traditional way to access a relational database is to use Java Database Connectivity (JDBC). Some common problems with using JDBC directly are as follows:

  • You must manually handle database connections. There is always the risk that connections aren't closed, which can lead to other problems.

  • You have to write a lot of bulky code, because all the fields required for inserts, updates, and queries must be explicitly mentioned.

  • You have to manually handle associations. For complex data, this can be a major issue.

  • The code isn't portable to other databases.

Solution

This section shows how you perform basic Create, Read, Update, and Delete (CRUD) operations using JDBC and describes the problems with using basic JDBC. You see how the object model is translated into the relational data model.

How It Works

To Start, you will need to create an eclipse project. You will need to install Derby jars and configure

Creating an Eclipse Project

To begin developing your Java application, you create a bookshop project in Eclipse. To set up the Derby database, you can install the core and UI plug-ins or download the Derby jar files and add them to your Eclipse project classpath.

To install the Derby plug-ins for Eclipse, do the following:

  1. Download the plug-ins from http://db.apache.org/derby/releases/release-10.5.3.0.cgi.

  2. Extract the zip files to your Eclipse home. If Eclipse is located at C:eclipse, extract the zips to the same location.

  3. Restart Eclipse. You should see the Derby jar files—derby.jar, derbyclient.jar, derbynet.jar, and derbytools.jar—added to your project's classpath.

  4. Select your project, and right-click. Select Apache Derby, and then select Add Network Server.

  5. Click the "Start Derby Network Server" button.

  6. After the server starts, select the ij option. The SQL prompt appears in the console window.

  7. At the prompt, execute the SQL statements from the previous recipe's "Creating the Tables (Relational Model)" section.

JDBC Initialization and Cleanup

You must load the JDBC driver and create a connection to that database before you can execute any SQL statements. The JDBC driver for Derby is in the derby.jar file that was added to your project's build path during Derby's installation. Be careful: you must remember to close that connection (whether an exception is raised or not). Connections are a costly resource—if they aren't closed after use, the application will run out of them and stop working:

Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
Connection connection = DriverManager.getConnection(
"jdbc:derby://localhost:1527/BookShopDB", "book", "book");
try {
// Using the connection to query or update database
} finally {
connection.close();
}

Using JDBC to Query a Database

For demonstration purpose, let's query for a book whose ISBN is 1932394419. Here's the JDBC code for this task:

PreparedStatement stmt = connection.prepareStatement
("SELECT * FROM BOOK WHERE ISBN = ?");
stmt.setString(1, "1932394419");
ResultSet rs = stmt.executeQuery();
while (rs.next())
{
       System.out.println("ISBN : " + rs.getString("ISBN"));
       System.out.println("Book Name : " + rs.getString("BOOK_NAME"));
       System.out.println("Publisher Code : " +                     rs.getString("PUBLISHER_CODE"));
       System.out.println("Publish Date : " +           rs.getDate("PUBLISH_DATE"));
       System.out.println("Price : " + rs.getInt("PRICE"));
       System.out.println();
}
rs.close();
stmt.close();

Using JDBC to Update a Database

Let's update the title of the book whose ISBN is 1932394419. Here's the JDBC code:

PreparedStatement stmt = connection.prepareStatement(
"UPDATE BOOK SET BOOK_NAME = ? WHERE ISBN = ?");
stmt.setString(1, "Hibernate Quickly 2nd Edition");
stmt.setString(2, "1932394419");
int count = stmt.executeUpdate();
System.out.println("Updated count : " + count);
stmt.close();

Creating the Domain Model

You use normal JavaBeans to build your object/domain model. These JavaBeans are called Plain Old Java Objects (POJOs). This term is used to distinguish them from Enterprise JavaBeans (EJBs). EJBs are Java objects that implement one of the javax.ejb interfaces and that need to be deployed in an EJB container. Note that each of these POJOs must have a no-argument constructor:

public class Publisher {
       private String code;
       private String name;
       private String address;
       // Getters and Setters
}
public class Book {
       private String isbn;
       private String name;
       private Publisher publisher;
       private Date publishDate;
       private int price;
       private List chapters;
       // Getters and Setters
}
public class Chapter {
       private int index;
       private String title;
       private int numOfPages;
       // Getters and Setters
}

You use a foreign key to reference PUBLISHER from the BOOK table, but it's a many-to-one association represented with a list of chapters in the Book class. You use a foreign key to reference BOOK from the CHAPTER table, but there's nothing referencing the Book class from the Chapter class. In contrast, a Book object has a list of Chapter objects (one-to-many association). This is a case of an object-relational mismatch, which focuses on the association between two classes or tables in their corresponding model. To handle the incompatibility of these models, you need to do some conversion/translation when you retrieve and save your object model. This is called object/relational mapping (O/R Mapping or ORM).

Let's say your Bookshop sells audio and video discs. In the object-oriented model, this can be represented by using a Disc superclass and two subclasses called AudioDisc and VideoDisc (see Figure 1-4). On the relational database side, you don't have a way to map this inheritance relationship. This is a major object-relational system mismatch.

You may also want to use a polymorphic query that refers to the Disc class and have the query return its subclasses. SQL doesn't support this kind of requirement—this is another object-relational system mismatch.

Inheritance in the object model

Figure 1-4. Inheritance in the object model

You can see that there are differences between an object model and a relational model. The object model is based on the analysis of the business domain, and therefore this forms the domain model. The relational model or tables are defined based on how the data is organized in rows and columns. ORM frameworks like Hibernate provide strategies to overcome the association mismatch, inheritance mismatch, and the polymorphic mismatch.

Retrieving Object Graphs

Suppose you have a web page in your application that shows a book's details (including ISBN, book name, publisher name, publisher address, publish date, and all the chapters in the book). You can use the following JDBC code fragment to get the result set:

PreparedStatement stmt = connection.prepareStatement(
"SELECT * FROM BOOK, PUBLISHER WHERE BOOK.PUBLISHER_CODE = PUBLISHER.CODE
AND BOOK.ISBN = ?");
stmt.setString(1, isbn);
ResultSet rs = stmt.executeQuery();
Book book = new Book();
if (rs.next())
{
       book.setIsbn(rs.getString("ISBN"));
       book.setName(rs.getString("BOOK_NAME"));
       book.setPublishDate(rs.getDate("PUBLISH_DATE"));
       book.setPrice(rs.getInt("PRICE"));

       Publisher publisher = new Publisher();
       publisher.setCode(rs.getString("PUBLISHER_CODE"));
       publisher.setName(rs.getString("PUBLISHER_NAME"));
       publisher.setAddress(rs.getString("ADDRESS"));
       book.setPublisher(publisher);
}
rs.close();
stmt.close();
List chapters = new ArrayList();
stmt = connection.prepareStatement("SELECT * FROM CHAPTER WHERE BOOK_ISBN = ?");
stmt.setString(1, isbn);
rs = stmt.executeQuery();
while (rs.next()) {
       Chapter chapter = new Chapter();
       chapter.setIndex(rs.getInt("IDX"));
       chapter.setTitle(rs.getString("TITLE"));
       chapter.setNumOfPages(rs.getInt("NUM_OF_PAGES"));
       chapters.add(chapter);
}
book.setChapters(chapters);
rs.close();
stmt.close();
return book;

The result set must be iterated through to create book and publisher objects. To retrieve the chapters, you have to execute another SQL statement based on the book's ISBN property. A group of objects with such an association is called an object graph.

Persisting Object Graphs

Suppose you want to provide a web page where users can input a book's information, including the publisher and chapters. When the user is finished, the entire object graph is saved to the database:

PreparedStatement stmt = connection.prepareStatement(
"INSERT INTO PUBLISHER (CODE, PUBLISHER_NAME, ADDRESS) VALUES (?, ?, ?)");
stmt.setString(1, book.getPublisher().getCode());
stmt.setString(2, book.getPublisher().getName());
stmt.setString(3, book.getPublisher().getAddress());
stmt.executeUpdate();
stmt.close();

stmt = connection.prepareStatement(
"INSERT INTO BOOK (ISBN, BOOK_NAME, PUBLISHER_CODE, PUBLISH_DATE, PRICE)
VALUES (?, ?, ?, ?, ?)");
stmt.setString(1, book.getIsbn());
stmt.setString(2, book.getName());
stmt.setString(3, book.getPublisher().getCode());

stmt.setDate(4, new java.sql.Date(book.getPublishDate().getTime()));
stmt.setInt(5, book.getPrice());
stmt.executeUpdate();
stmt.close();

stmt = connection.prepareStatement(
"INSERT INTO CHAPTER (BOOK_ISBN, IDX, TITLE, NUM_OF_PAGES) VALUES (?, ?, ?, ?)");
for (Iterator iter = book.getChapters().iterator(); iter.hasNext();)
{
       Chapter chapter = (Chapter) iter.next();
stmt.setString(1, book.getIsbn());
       stmt.setInt(2, chapter.getIndex());
       stmt.setString(3, chapter.getTitle());
       stmt.setInt(4, chapter.getNumOfPages());
       stmt.executeUpdate();
}
stmt.close();

Problems with Using JDBC

Using JDBC means you can execute any kind of SQL statements. For a simple task, you have to code many SELECT, INSERT, UPDATE, and DELETE statements repeatedly. This results in the following issues:

  • Too much copy code: When you perform object retrieval, you need to copy the fields in a ResultSet to the properties of an object. When you perform object persistence, you need to copy the properties of an object to the parameters in PreparedStatement.

  • Manually handled associations: When you perform object retrieval, you have to perform a table join or read from several tables to get an object graph. When you perform object persistence, you must update several tables accordingly.

  • Database dependent: The SQL statements you wrote for one database may not work with another brand of database. Although the chance is very small, you may have to migrate to another database.

Configuring Hibernate

Problem

How do you configure a Java project that uses an object/relational framework like Hibernate as a persistence framework? How do you configure Hibernate programmatically?

Solution

Hibernate is a powerful ORM framework for developing Java applications. You need to import the required jars into your project's classpath and create mapping files that map the state of a Java entity to the columns of its corresponding table. From your Java application, you execute CRUD operations on the object entities. Hibernate takes care of translating the object state from the object model to the relational model.

How It Works

To configure a Java project to use Hibernate framework, you need to start with downloading the required jars and configuring them in the build path.

Getting the Required Jars

You can go to www.hibernate.org/ and download Hibernate Core 3.3.2. After you download the compressed Hibernate distribution, extract it to a directory such as C:hibernate. In Eclipse, go to Windows

Getting the Required Jars
${Hibernate_Install_Dir}/hibernate3.jar
${Hibernate_Install_Dir}/lib/antlr.jar
${Hibernate_Install_Dir}/lib/asm.jar
${Hibernate_Install_Dir}/lib/asm-attrs.jars
${Hibernate_Install_Dir}/lib/cglib.jar
${Hibernate_Install_Dir}/lib/commons-collections.jar
${Hibernate_Install_Dir}/lib/commons-logging.jar
${Hibernate_Install_Dir}/lib/dom4j.jar
${Hibernate_Install_Dir}/lib/ehcache.jar
${Hibernate_Install_Dir}/lib/jta.jar
${Hibernate_Install_Dir}/lib/log4j.jar

Defining a custom library this way makes it easy to reuse in another project. If you have another project that uses Hibernate, you can import this user library into that project. Follow these steps to add this user library to a project's build path:

  1. Right-click your project in Eclipse.

  2. Select BuildPath

    Getting the Required Jars
  3. Click the Add Library button.

  4. Select User Library, and click Next.

  5. Select Hibernate, and click Finish.

The custom library is now configured to your project's build path.

Creating Mapping Definitions

First, you ask Hibernate to retrieve and persist the book objects for you. For simplicity, let's ignore the publisher and chapters at this moment. You create an XML file Book.hbm.xml in the same package as the Book class. This file is called the mapping definition for the Book class. The Book objects are called persistent objects or entities because they can be persisted in a database and represent the real-world entities:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.metaarchit.bookshop">
       <class name="Book" table="BOOK">
       <id name="isbn" type="string" column="ISBN" />
       <property name="name" type="string" column="BOOK_NAME" />
       <property name="publishDate" type="date" column="PUBLISH_DATE"/>
<property name="price" type="int" column="PRICE" />
       </class>
</hibernate-mapping>

Each persistent object must have an identifier. It's used by Hibernate to identify that object uniquely. Here, you use the ISBN as the identifier for a Book object.

Configuration

Before Hibernate can retrieve and persist objects for you, you need to tell it your application's settings. For example, which kind of objects are persistent objects? What kind of database are you using? How do you connect to the database? You can configure Hibernate three ways:

  • Programmatic configuration: Use the API to load the hbm file, load the database driver, and specify the database connection details.

  • XML configuration: Specify the database connection details in an XML file that's loaded along with the hbm file. The default file name is hibernate.cfg.xml. You can use another name by specifying the name explicitly.

  • Properties file configuration: This is similar to the XML configuration but uses a .properties file. The default name is hibernate.properties.

This solution introduces only the first two approaches (programmatic and XML configuration). The properties file configuration is much like XML configuration.

Programmatic Configuration

The following code loads the configuration programmatically. The Configuration class provides the API to load the hbm files, to specify the driver to be used for the database connection, and to provide other connection details:

Configuration configuration = new Configuration()
.addResource("com/metaarchit/bookshop/Book.hbm.xml")
.setProperty("hibernate.dialect", "org.hibernate.dialect.DerbyDialect")
.setProperty("hibernate.connection.driver_class", "org.apache.derby.jdbc.EmbeddedDriver")
.setProperty("hibernate.connection.url", "jdbc:derby://localhost:1527/BookShopDB")
.setProperty("hibernate.connection.username", "book")
.setProperty("hibernate.connection.password", "book");
SessionFactory factory = configuration.buildSessionFactory();

Instead of using addResource() to add the mapping files, you can also use addClass() to add a persistent class and let Hibernate load the mapping definition for this class:

Configuration configuration = new Configuration()
.addClass(com.metaarchit.bookshop.Book.class)
.setProperty("hibernate.dialect", "org.hibernate.dialect.DerbyDialect")
.setProperty("hibernate.connection.driver_class", "org.apache.derby.jdbc.EmbeddedDriver")
.setProperty("hibernate.connection.url", "jdbc:derby://localhost:1527/BookShopDB")
.setProperty("hibernate.connection.username", "book")
.setProperty("hibernate.connection.password", "book");
SessionFactory factory = configuration.buildSessionFactory();

If your application has hundreds of mapping definitions, you can pack it in a jar file and add it to the Hibernate configuration. This jar file must be found in your application's classpath:

Configuration configuration = new Configuration()
.addJar(new File("mapping.jar"))
.setProperty("hibernate.dialect", "org.hibernate.dialect.DerbyDialect")
.setProperty("hibernate.connection.driver_class", "org.apache.derby.jdbc.EmbeddedDriver")
.setProperty("hibernate.connection.url", "jdbc:derby://localhost:1527/BookShopDB")
.setProperty("hibernate.connection.username", "book")
.setProperty("hibernate.connection.password", "book");
SessionFactory factory = configuration.buildSessionFactory();

SessionFactory

The following statement creates a Hibernate SessionFactory:

SessionFactory factory = configuration.buildSessionFactory();

A session factory is a global object for maintaining org.hibernate.Session objects. It's instantiated once, and it's thread-safe. You can look up the SessionFactory from a Java Naming and Directory Interface (JNDI) context in an ApplicationServer or any other location.

XML Configuration

Another way to configure Hibernate is to use an XML file. You create the file hibernate.cfg.xml in the source directory, so Eclipse copies it to the root of your classpath:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.driver_class">
   org.apache.derby.jdbc.EmbeddedDriver
</property>
<property name="connection.url">jdbc:derby://localhost:1527/BookShopDB</property>
<property name="connection.username">book</property>
<property name="connection.password">book</property>
<property name="dialect">org.hibernate.dialect.DerbyDialect</property>
<mapping resource="com/metaarchit/bookshop/Book.hbm.xml" />
</session-factory>
</hibernate-configuration>

Now, the code fragment to build up a session factory can be simplified. The configuration loads your hibernate.cfg.xml file from the root of the classpath:

Configuration configuration = new Configuration().configure();

This method loads the default hibernate.cfg.xml from the root class path. The new Configuration() loads the hibernate.properties file, and the configure() method loads hibernate.cfg.xml if hibernate.properties isn't found. If you need to load another configuration file located elsewhere (not in the root classpath), you can use the following code:

new Configuration().configure("/config/recipes.cfg.xml")

This code looks for recipes.cfg.xml in the config subdirectory of your classpath.

Opening and Closing Sessions

A Hibernate Session object represent a unit of work and is bound to the current thread. It represents a transaction in a database. A session begins when getCurrentSession() is first called on the current thread. The Session object is then bound to the current thread. When the transaction ends with a commit or rollback, Hibernate unbinds the session from the thread and closes it.

Just as when you use JDBC, you need to do some initial cleanup for Hibernate. First, you ask the session factory to open a new session for you. After you finishing your work, you must remember to close the session:

Session session = factory.openSession();
try {
       // Using the session to retrieve objects
}catch(Exception e)
{

       e.printStackTrace();
} finally {
       session.close();

}

Retrieving Objects

Given the ID (an ISBN, in this case) of a book, you can retrieve the unique Book object from the database. There are two ways to do that:

Book book = (Book) session.load(Book.class, isbn);

and

Book book = (Book) session.get(Book.class, isbn);

What's the difference between load() and get()? First, when the given ID can't be found, load() throws an exception org.hibernate.ObjectNotFoundException, whereas get() returns a null object. Second, load() just returns a proxy by default; the database isn't hit until the proxy is first invoked. get() hits the database immediately. The load method is useful when you only need a proxy and don't need to make a database call. You just need a proxy, when in a given session you need to associate an entity before persisting.

Just as you can use SQL to query a database, you can use Hibernate to query objects, using Hibernate Query Language (HQL). For example, the following codes queries for all the Book objects:

Query query = session.createQuery("from Book");
List books = query.list();

If you're sure only one object will match, you can use the uniqueResult() method to retrieve the unique result object:

Query query = session.createQuery("from Book where isbn = ?");
query.setString(0, isbn);
Book book = (Book) query.uniqueResult();

Configuring a JPA Project

Problem

How do you manage the metadata required for ORM? Can you use any mechanism other than specifying the metadata in XML files? How do you configure a JPA project?

Solution

The EJB3.0 specification defines the Java Persistence API, which provides ORM using a Java domain model to manage a relational database. Different providers implement this API:

  • TopLink: This is a Java ORM solution currently owned by Oracle. Here's the URL for more details about TopLink: www.oracle.com/technology/products/ias/toplink/index.html.

  • JDO: The JDO API is a standard interface-based Java model abstraction of persistence developed by the Java Community Process. The current JDO 2.0 is Java Specification Request 243. Beginning with JDO 2.0, the development of the API is taking place within Apache JDO open source.

  • Hibernate: This is a very popular ORM framework. Hibernate provides Hibernate Annotations, which implement JPA standard and also provide more advanced mapping features. We will be demonstrating configuring a JPA project that uses Hibernate Annotations.

How It Works

To use Hibernate Annotations, download the HibernateAnnotation package from the Hibernate site: www.hibernate.org/6.html. The following jars need to be in your Eclipse project build path in addition to the Hibernate core jar files:

  • hibernate-annotations.jar

  • lib/hibernate-comons-annotations.jar

  • lib/ejb3-persistence.jar

Configure the session factory in hibernate.cfg.xml. (Note that if you change the name of this file to anything other than hibernate.cfg.xml, you must upload the file programmatically.) The dialect property is used to define the name of the database. This enables Hibernate to generate SQL optimized for a particular relational database. You use Derby as a database in this case, so you use org.hibernate.dialect.DerbyDialect. Also, if you change the database—say, from Derby to Oracle—you must change the value from org.hibernate.dialect.DerbyDialect to org.hibernate.dialect.Oracle9Dialect. This is how portability is achieved using Hibernate. Some of the common dialects that Hibernate supports are as follows

  • DB2Dialect (supports DB2)

  • FrontBaseDialect

  • HSQLDialect

  • InformixDialect

  • IngresDialect

  • InterbaseDialect

  • MySQLDialect

  • Oracle8Dialect

  • Oracle9Dialect

  • Oracle10Dialect

  • PointbaseDialect

  • PostgreSQLDialect

  • ProgressDialect

  • ProgressDialect

  • SybaseDialect

Here's a sample configuration for the database BookShopDB:

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <!-- Database connection settings -->
        <property name="connection.driver_class">
           org.apache.derby.jdbc.EmbeddedDriver
</property>
        <property name="connection.url">
          jdbc:derby://localhost:1527/BookShopDB
        </property>
        <property name="connection.username">book</property>
        <property name="connection.password">book</property>
        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">1</property>

              <!-- SQL dialect -->
        <property name="dialect">
         org.hibernate.dialect.DerbyDialect
        </property>

        <!-- Enable Hibernate's automatic session context management -->
        <property name="current_session_context_class">thread</property>
         <!-- Disable the second-level cache  -->
        <property name="cache.provider_class">
         org.hibernate.cache.NoCacheProvider
        </property>
        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>
        <!-- Drop and re-create the database schema on startup -->
        <!-- property name="hbm2ddl.auto">update</property> -->
        <mapping class="com.hibernaterecipes.annotations.domain.Book"/>
    </session-factory>
</hibernate-configuration>

When you use annotations, you don't need the additional mapping file (*.hbm.xml). The metadata for the ORM is specified in the individual classes. You only need to add the class mapping in hibernate.cfg.xml. In the previous example, the line

<mapping class="com.hibernaterecipes.annotations.domain.Book"/>

takes care of the class mapping. Next, let's look at Book.java with annotations for the table name, column names, and other attributes:

package com.hibernaterecipes.annotations.domain;

import java.util.Date;
import javax.persistence.Column;
import javax.persistence.*;
import javax.persistence.Entity;
import javax.persistence.Table;

/**
 * @author Guruzu
 *
 */
@Entity
@Table (name="BOOK")
public class Book {

       @Column (name="isbn")
       @Id
       String isbn;

       @Column (name="book_Name")
       String bookName;

       @Column (name="publisher_code")
       String publisherCode;

       @Column (name="publish_date")
       Date publishDate;

       @Column (name="price")
       Long price;

       /**
        * @return the isbn
        */
       public String getIsbn() {
             return isbn;
       }
       /**
        * @param isbn the isbn to set
       */
       public void setIsbn(String isbn) {
              this.isbn = isbn;       }
       /**
       * @return the bookName
       */
       public String getBookName() {
              return bookName;
       }
       /**
       * @param bookName the bookName to set
       */
       public void setBookName(String bookName) {
       this.bookName = bookName;
       }
       /**
       * @return the publisherCode
       */
       public String getPublisherCode() {
              return publisherCode;
       }
       /**
       * @param publisherCode the publisherCode to set
       */
       public void setPublisherCode(String publisherCode) {
              this.publisherCode = publisherCode;
}
       /**
       * @return the publishDate
       */       public Date getPublishDate() {
       return publishDate;
       }
       /**
       * @param publishDate the publishDate to set
       */
       public void setPublishDate(Date publishDate) {
              this.publishDate = publishDate;
       }
       /**
       * @return the price
       */
       public Long getPrice() {
              return price;
       }
       /**
       * @param price the price to set
       */
       public void setPrice(Long price) {
              this.price = price;
       }

}

@Entity is defined by the EJB3.0 specification to annotate an entity bean. An entity represents a lightweight persistent domain object.

An entity class must have a public or protected no-arg constructor. It may have other constructors as well. It should be a top level class and must not be final. If the entity is to be passed by value (that is, through a remote interface) it must implement Serializable.

The state of the entity is represented by the entity's instance variables. The instance variables must be accessed only from within the entity class. The client of the entity shouldn't be able to access the state of the entity directly. The instance variables must have private, protected, or package visibility.

Every entity must have a primary key. The primary key must be declared only once in the entity hierarchy.

You can generate the set and get methods using the Eclipse IDE. Select the instance variables for which you need to generate the methods, right-click the selection, and select Source

How It Works

In the previous class, the name of the table BOOK is specified with the name attribute of the Table annotation. The variable isbn is the primary key, which is specified by the @Id tag. The rest of the columns are specified by the @column annotation. If the @column annotation isn't specified, the names of the instance variables are considered column names. Every nonstatic and nontransient properties of an entity bean are considered persistent unless you specify @Transient. @Transient properties are ignored by the EntityManager when you map persistent properties.

Opening a Session

Opening a session is similar to doing so in Hibernate in general, except you use AnnotationConfiguration to build the session factory:

public class SessionManager {

       private static final SessionFactory sessionFactory = buildSessionFactory();

       private static SessionFactory buildSessionFactory() {
               try {
                   // Create the SessionFactory from hibernate.cfg.xml
                   return new                                                                          AnnotationConfiguration()
        .configure().buildSessionFactory();
               }
               catch (Throwable ex) {
                   // Make sure you log the exception, as it might be swallowed                     ex.printStackTrace();

                   throw new ExceptionInInitializerError(ex);
               }
           }

           public static SessionFactory getSessionFactory() {
               return sessionFactory;
           }
}

If the configuration file name isn't hibernate.cfg.xml (in this case, it's named annotation.cfg.xml), build the session factory the using the following statement:

new  AnnotationConfiguration()
.configure("annotation.cfg.xml")
.buildSessionFactory();

There are other overloaded configure() methods that you can use appropriately.

This section uses a Data Access Object (DAO) for database operations. The DAO is a design pattern used to abstract and encapsulate all access to the data source. For this example, it contains code to create the SessionFactory and a Session object and to fetch and update data in the database:

public class BookDAO {

       /**
        * To query all details of a book
        * @return
        */
       public List<Book> readAll() {

             Session session = SessionManager.getSessionFactory().getCurrentSession();
              session.beginTransaction();
              List<Book> booksList = session.createQuery("from Book").list();
session.getTransaction().commit();
              return booksList;
       }

       /**
        * To create a book
        * @return
        */
       public void create(Book bookObj) {

              Session session=SessionManager.getSessionFactory().getCurrentSession();
              session.beginTransaction();
              session.saveOrUpdate(bookObj);
              session.getTransaction().commit();

       }

}

The readAll method queries all data from the BOOK table that's mapped in hibernate.cfg.xml. The create method inserts a new row into the BOOK table.

Using Entity Manager

Problem

Is there a generalized mechanism to configure ORM with less dependency on individual providers like Hibernate, TopLink, and so on?

Solution

A persistence context is defined by the JPA specification as a set of managed entity instances where the entity instances and their lifecycles are managed by an entity manager. Each ORM vendors provides its own entity manager, which is a wrapper around the core API and thus supports the JPA programming interfaces, JPA entity instance lifecycles, and the query language. This provides a generalized mechanism for object/relational development and configuration.

How It Works

You obtain the Hibernate EntityManager from an entity manager factory. When container-managed entity managers are used, the application doesn't interact directly with the entity manager factory. Such entity managers are obtained mostly through JNDI lookup. In the case of application-managed entity managers, the application must use the entity manager factory to manage the entity manager and the persistence context lifecycle. This example uses the application-managed entity manager.

EntityManagerFactory has the same role as the SessionFactory in Hibernate. It acts a factory class that provides the EntityManager class to the application. It can be configured either programmatically or using XML. When you use XML to configure it, the file must be named persistence.xml and must be located in your classpath.

Here's the persistence.xml file for the Book example:

<?xml version="1.0" encoding="UTF-8"?>
<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="book" transaction-type="RESOURCE_LOCAL">
                             <provider>org.hibernate.ejb.HibernatePersistence</provider>
      <class>com.hibernaterecipes.annotations.domain.Book</class>

      <properties>
         <property name="hibernate.connection.driver_class"
            value="org.apache.derby.jdbc.EmbeddedDriver"/>
         <property name="hibernate.connection.username"
            value="book"/>
         <property name="hibernate.connection.password"
            value="book"/>
         <property name="hibernate.connection.url"
            value="jdbc:derby://localhost:1527/BookShopDB"/>
         <property name="hibernate.dialect"
            value="org.hibernate.dialect.DerbyDialect"/>
     </properties>
   </persistence-unit>
</persistence>

In this persistence.xml file, the complete unit is defined by <persistence-unit>. This name should match the name used when you create a EntityManagerFactory.

The transaction-type RESOURCE_LOCAL is used here. Two transaction types define transactional behavior: JTA and RESOURCE_LOCAL. JTA is used in J2EE managed applications where the container is responsible for transaction propagation. For application-managed transactions, you can use RESOURCE_LOCAL.

The <provider> tag specifies the third-party ORM implementation you use. In this case, it's configured to use the Hibernate Persistence provider.

The entity instances are configured with the <class> tag.

The rest of the properties are similar to what you configured in hibernate.cfg.xml, including the driver class of the database you're connecting to, the connection URL, a username, a password, and the dialect.

Here's the code to create the EntityManagerFactory (EMF) from the configuration and to obtain the EntityManager from the EMF:

package com.hibernaterecipes.annotations.dao;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public class SessionManager {

           public static EntityManager getEntityManager() {
              EntityManagerFactory managerFactory = Persistence.createEntityManagerFactory("book");
              EntityManager manager = managerFactory.createEntityManager();

              return manager;
           }
}

Persistence.createEntityManagerFactory creates the EMF. The parameter that it takes is the name of the persistence unit—in this case, "book". This should be the same as the name specified in the persistence.xml file's persistence-unit tag:

<persistence-unit name="book" transaction-type="RESOURCE_LOCAL">

The entity instance Book remains the same as defined in JPA:

package com.hibernaterecipes.annotations.domain;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Entity;
import javax.persistence.Table;

@Entity
@Table (name="BOOK")
public class Book {
       @Column (name="isbn")
       @Id
       String isbn;

       @Column (name="book_Name")
       String bookName;

       @Column (name="publisher_code")
       String publisherCode;

       @Column (name="publish_date")
       Date publishDate;

       @Column (name="price")
       Long price;

       /**
        * @return the isbn
        */
public String getIsbn() {
              return isbn;
       }
       /**
        * @param isbn the isbn to set
        */
       public void setIsbn(String isbn) {
              this.isbn = isbn;
       }
       /**
        * @return the bookName
        */
       public String getBookName() {
              return bookName;
       }
       /**
        * @param bookName the bookName to set
        */
       public void setBookName(String bookName) {
              this.bookName = bookName;
       }
       /**
        * @return the publisherCode
        */
       public String getPublisherCode() {
              return publisherCode;
       }
       /**
        * @param publisherCode the publisherCode to set
        */
       public void setPublisherCode(String publisherCode) {
              this.publisherCode = publisherCode;
       }
       /**
        * @return the publishDate
        */
       public Date getPublishDate() {
              return publishDate;
       }
       /**
        * @param publishDate the publishDate to set
        */
       public void setPublishDate(Date publishDate) {
              this.publishDate = publishDate;
       }
       /**
        * @return the price
        */
       public Long getPrice() {
              return price;
       }
       /**
* @param price the price to set
        */
       public void setPrice(Long price) {
              this.price = price;
       }
}

The following is the DAO call to fetch the Book details:

public List<Book> readFromManager() {

              EntityManager manager = SessionManager.getEntityManager();
              EntityTransaction tran = manager.getTransaction();
              tran.begin();
              Query query = manager.createQuery("select b from Book b");
              List<Book> list = query.getResultList();
              tran.commit();
              manager.close();
              return list;
       }

From the main method, you invoke the DAO method to list the Book details:

List<Book> list = bookDAO.readFromManager();
                     System.out.println("List of Books - " + list.size());

Enabling Logging in Hibernate

Problem

How do you determine what SQL query is being executed by Hibernate? How do you see Hibernate's internal workings?. How do you enable logging to troubleshoot complex issues related to Hibernate?

Solution

Hibernate utilizes Simple Logging Facade for Java (SLF4J) to log various system events. SLF4J is distributed as a free software license. It abstracts the actual logging framework that an application uses. SLF4J can direct your logging output to several logging frameworks:

  • NOP: Null logger implementation

  • Simple: A logging antiframework that is very simple to use and that attempts to solve every logging problem in one package

  • Log4j version 1.2: A widely used open source logging framework

  • JDK 1.4 logging: A logging API provided by Java

  • JCL: An open source Commons logging framework that provides an interface with thin wrapper implementations for other logging tools

  • Logback: A serializable logger that, when used, logs after its deserialization depending on your chosen binding

To set up logging, you need slf4j-api.jar in your classpath together with the jar file for your preferred binding—slf4j-log4j12.jar in the case of log4j. You can also enable a property called showsql to see the exact query being executed. You can configure a logging layer like Apache log4j to enable Hibernate class- or package-level logging. And you can use the Statistics Interface provided by Hibernate to obtain some detailed information.

How It Works

You will have to configure the Hibernate property show_sql and log4J to enable logging.

Inspecting the SQL Statements Issued by Hibernate

Hibernate generates SQL statements that let you access the database behind the scene. You can set the show_sql property to true in the hibernate.cfg.xml XML configuration file to print the SQL statements to stdout:

<property name="show_sql">true</property>

Configuring Log4j

Hibernate can also use the log4j logging library to log SQL statements and parameters. Make sure the log4j.jar file is included in your project's classpath. Create a properties file named log4j.properties in the source root folder; this file is used to configure the log4j library:

### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %5p %c{1}:%L - %m%n
### direct messages to file hibernate.log ###
#log4j.appender.file=org.apache.log4j.FileAppender
#log4j.appender.file.File=hibernate.log
#log4j.appender.file.layout=org.apache.log4j.PatternLayout
#log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %5p %c{1}:%L - %m%n
log4j.rootLogger=error, stdout
log4j.logger.org.hibernate.SQL=debug
log4j.logger.org.hibernate.type=debug

Enabling Live Statistics

You can enable live statistics by setting the property hibernate.generate_statistics in the configuration file:

<property name="hibernate.generate_statistics">true</property>

You can also enable live statistics programmatically by using the Statistics Interface:

Statistics stats = sessionFactory.getStatistics();
stats.setStatisticsEnabled(true);
Transaction tx = session.beginTransaction();
List<Book> books = session.createQuery("from Book").list();
for(Book bo : books)
{
       System.out.println(bo);
}
stats.getSessionOpenCount();
stats.logSummary();
session.close();

Generating a Database Schema Using Hibernate

Problem

How can Hibernate help you generate or update a schema?

Solution

Hibernate uses apache Ant task definitions to create and update database schema.

How It Works

Creating an Ant Build File

You use Apache Ant to define the building process. (For more information about Ant, see http://ant.apache.org/.) Create the following build.xml file in the project root:

<project name="BookShop" default="schemaexport">
<property name="build.dir" value="bin" />
<property name="hibernate.home" value="c:/hibernate-3.1" />
<property name="derby.home" value="c:/derby" />
<path id="hibernate-Classpath">
<fileset dir="${hibernate.home}">
<include name="**/*.jar" />
</fileset>
<fileset dir="${derby.home}">
<include name="lib/*.jar" />
</fileset>
<pathelement path="${build.dir}" />
</path>
<!-- Defining Ant targets -->
</project>

Generating Database Schema Using SchemaExport

You use the schemaexport task provided by Hibernate to generate the SQL statements to create a database schema. It reads the dialect property from the Hibernate configuration file (hibernate.cfg.xml) to determine which brand of database you're using:

<target name="schemaexport">
<taskdef name="schemaexport"
classname="org.hibernate.tool.hbm2ddl.SchemaExportTask"
Classpathref="hibernate-Classpath" />
<schemaexport config="${build.dir}/hibernate.cfg.xml"
output="BookShop.sql" />
</target>

Updating a Database Schema Using SchemaUpdate

During the development cycle, you may change your object model frequently. It isn't efficient to destroy and rebuild the schema every time. The schemaupdate task updates an existing database schema:

<target name="schemaupdate">
<taskdef name="schemaupdate"
classname="org.hibernate.tool.hbm2ddl.SchemaUpdateTask"
Classpathref="hibernate-Classpath" />
<schemaupdate config="${build.dir}/hibernate.cfg.xml" text="no"/>
</target>

Specifying the Details of a Database Schema

In the previous mapping example, you discarded some table details, such as column length and the not-null constraint. If you generate a database schema from this mapping, you must provide these kinds of details:

<hibernate-mapping package="com.hibernaterecipes.bookshop">
<class name="Book" table="BOOK">
<id name="isbn" type="string">
<column name="ISBN" length="50" />
</id>
<property name="name" type="string">
<column name="BOOK_NAME" length="100" not-null="true" />
</property>
<property name="publishDate" type="date" column="PUBLISH_DATE" />
<property name="price" type="int" column="PRICE" />
</class>
</hibernate-mapping>

Summary

In this chapter, you've learned what object/relational mapping is and what its benefits are over JDBC. Hibernate is one of the most widely used ORM frameworks in the industry. Using JDBC directly has many disadvantages, including complicated handling of the ResultSet and the fact that it isn't portable against different databases. To overcome these issues, you can use ORM for ease of development and to maintain software efficiently.

To configure Hibernate, you need various third-party jars that must be specified in the classpath. The .hbm and .hibernate.cfg.cml XML files are required in order to configure the objects that are mapped to tables; they also contain the database connection details. You use org.hibernate.SessionFactory to create org.hibernate.Session objects that represent units of work. Other database operations are performed using the Session object.

You can also perform Hibernate configuration and database operations with annotations. These are Hibernate Annotations which implement the Java Persistence standards defined by the EJB3.0 specification, and thus all details can be specified through annotations.

..................Content has been hidden....................

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