Validating the Hibernate Setup
Okay, so the first tutorial discussed the
basics of setting up a Hibernate environment, including the core
configuration steps of:
- --installing a database and creating a schema named examscam
- --installing the JDK, version 1.5 or greater
- --downloading the Hibernate Core and Hibernate Annotations
libraries
- --downloading the appropriate JDBC driver for your database
and placing it on the CLASSPATH
- --appropriately editing the hibernate.cfg.xml file
Now, once you've done that, you'll
probably want to verify that all of those components are integrated
together and working properly. Here's what we're going to do to make
sure everything is set up and ready to go:
First, we're going to create a simple
User JavaBean. Coding and being able to compile this JavaBean, aka
POJO (Plain Ordinary Java Object), will confirm that the JDK has been
installed properly. Furthermore, the User bean will employ some very
kewl Java Persistence API annotations, which will verify that the JDK
is linking to the downloaded Hibernate and JPA libraries properly. And
finally, we're going to tell the Hibernate framework all about the
existence of this User JavaBean, and ask the Hibernate framework to
create a database table under, the umbrella of the examscam schema,
that will support this User JavaBean. Yeah, that's right: if you tell
Hibernate about a JPA annotated JavaBean, Hibernate can create an
underlying database table for you, right there on the fly! If
everything goes well, this chapter should result in the creation of a
database table, without us ever having to use a database tool to
create it!
A Quick Review
Again, here's what we're going to do in
this chapter:
- --create a JavaBean, aka POJO, aka class, named User
- --annotate the User class with JPA annotations
- --feed the User bean to the Hibernate framework
- --ask the Hibernate framework to create a database table
based on the properties of the User class
Designing The Object Model - Looking
Ahead
One of the things I'd like to work
towards in the first part of this tutorial is the creation of a
simple, JSP based application that manages user information. Given a
user, our primitive online application will be able to add, update,
delete, search for, and create, new users; the content of which would
be persisted to an underlying database. As you could imagine, the core
artifact of this online application will be a Java class named User,
which will map to a database table in our examscam schema that will
very uncreatively be named user. (notice the lower case 'u'?)
With this online, user management application in mind, let's take our
first step towards leveraging Java and the Hibernate framework by
creating a very simple class named User.
The User class will contain two very
simple properties: an id of type Long, and a password of type String.
Yes, I know, this is a pretty lame Java class, but that's just fine
for right now. The current goal is to create a simple class that we
can decorate with JPA (Java Persistence API) annotations and
subsequently use to validate our development environment, making sure
that Hibernate, our database, and all of the required drivers and
libraries are linked up and working together properly. Keeping things
relatively simple right now is a good thing.
The Un-Annotated User Class
I've placed the User class in a package
named com.examscam.model. This is a good place for our
domain model classes to go.
package com.examscam.model;
/* The User Class without JPA annotations */
public class User {
private Long id;
private String password;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
This User class may look relatively
simple, but rest assured, this simple little class will grow to become
the cornerstone of our user management, online, JSP-based,
web-application.
Of Tables and Java Classes
So, as just a bit of a reminder about
where we're going in this chapter, let me rehash what we're doing. We
have created a simple Java class named User. This Java class has two
properties, one of type Long named id, and another of type String that
is smartly named password. Now, our application is going to have to
store and retrieve User information, and in order to do that, we're
going to need a database table; one that has columns for both the
User's id and the User's password. Essentially, our application must
persist the data in the class named User to corresponding columns in a
database table.
package com.examscam.model;
public class User {
private Long id;
private String password;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Annotating POJO's with JPA Tags
So, you're probably wondering how
Hibernate is supposed to know how to persist the state of a JavaBean,
like our User class, to a database. Well, as it stands right now,
Hibernate doesn't have a clue about how to persist our User bean. In
fact, Hibernate doesn't even know of its existence! If we want
Hibernate to know how to persist the state of our User POJO to the
database, we must decorate our Java code using special constructs
known as JPA, or Java Persistence API, annotations.
Annotations New with Java 5!!!
Annotations are a new concept with Java
5, so some legacy Java developers might not be familiar with the
concept. Basically, an annotation allows a developer to provide extra
information about the code that is being written. For our User class,
we will provide a number of annotations, the most important of which
will be the @Entity annotation that indicates that the User class is
in fact a database related entity, whose state Hibernate should
persist to the database. This is done by adding a special @Entity tag
immediately before the class declaration.
Furthermore, we need to tell Hibernate
that the User's id field represents a unique primary key. We indicate
this by adding the @Id annotation before the getter of the id field.
Also, since we are relying on our database to generate unique primary
keys for us, we need to provide the @GeneratedValue annotation as
well. Again, we will add the @Id and the @GeneratedValue annotations
immediately before the getId() method of the User class.
The JPA Annotated User POJO
package com.examscam.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;@Entity
public class User {
private Long id;
private String password;
@Id
@GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Notice the addition of the JPA
annotations to the code, including the @Entity, @Id and
@GeneratedValue tags.
The @Entity annotation tells Hibernate
that the User class is a persistent entity that needs to have its
state persisted to the database. Furthermore, the @Id annotation
indicates that the field named id in the JavaBean represents a primary
key, or unique identifier. Finally, the @GeneratedValue annotation
informs Hibernate that when a new entity is persisted to the database,
the database will generate a unique primary key for the record; given
this generated value, Hibernate will then set the id in the User POJO
instance accordingly.
Hibernate Annotations and JPA
One interesting thing to note about these
annotations is that they actually come from the Java Persistence API,
or JPA. This fact is evidenced by the surreptitiously added set of
import statements at the top of the User class:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
The Java Persistence API is not a
standard part of the JDK installation or the core Hibernate download.
As such, being able to use JPA annotations in our code requires the
appropriate libraries to be available at compile and runtime. These
libraries are available by downloading the Hibernate annotations
extension package. The bytecode embodiment of these annotations and
related files are found in the following jar files:
ejb3-persistence.jar, hibernate-commons-annotations.jar and
hibernate-annotations.jar.
If you download the Hibernate Annotations
module from hibernate.org, the
compressed file you receive will contain these three jar files. Make
sure these jar files are on the classpath of your Java development
environment.
Annotations and Java 5
Furthermore, annotations are not only a
new module for Hibernate, but they are a relatively new concept in the
world of Java programming, having been introduced for the very first
time in a standard edition with Java 5, or the JDK 1.5. That's a
pretty important point, because even if you've downloaded and placed
all of the required JAR files on your classpath, if you're not using
JDK 1.5 or greater, your annotations are not going to work.
You can always confirm your version of
Java by typing in the following command and switch on the command
line:
java version
Here's an example of what you might get
as output. Notice that I'm using a flavor of Java 1.6!
C:\>java version
java version "1.6.0_02"
Communicating with the Hibernate
Framework
Okay, so you've got yourself a JPA
annotated JavaBean named User, and you have all of the required
Hibernate Core and Hibernate Annotations JAR files on your classpath,
and now you want to use the Hibernate framework in the job of mapping
your User POJO to an actual database table. The question is, how do
you do it?
JavaBeans and the
AnnotationConfiguration
Well, the first thing you need to do to
get Hibernate to map the User bean to the database is write some Java
code that tells Hibernate to configure itself to be able to handle an
annotated JavaBean, which in our case will be the User class. That
first requires the creation of Hibernate's AnnotationConfiguration
object, which can be easily created by calling the
AnnotationConfiguration's constructor.
AnnotationConfiguration config =
new AnnotationConfiguration();
java.lang.Object
--org.hibernate.cfg.Configuration
--org.hibernate.cfg.AnnotationConfiguration
public class AnnotationConfiguration
extends org.hibernate.cfg.Configuration
Similar to the parent Configuration
object but handles EJB3 and Hibernate specific annotations as a
metadata facility.
An instance of Configuration allows the
application to specify properties and mapping documents. Usually an
application will create a single Configuration. The Configuration is
meant only as an initialization-time object.
--Hibernate API JavaDoc
Telling Hibernate about your Annotated
POJOs
After creating the
AnnotationConfiguration object, we must add to it all of
our annotated JavaBeans, which for now is just our lonely, yet stoic,
JPA annotated User class.
config.addAnnotatedClass(User.class);
This addAnnotatedClass method call tells
Hibernate which JavaBeans to read and manage as persistent objects.
For every class you annotate with JPA tags, you must add it as an
annotated class to the AnnotationConfiguration object, just as we have
done for the User class.
public AnnotationConfiguration
addAnnotatedClass(Class persistentClass)
throws org.hibernate.MappingException
Reads a mapping from the class annotation metadata (JSR 175).
Parameters: persistentClass - the mapped class
Returns: the configuration object
Throws: org.hibernate.MappingException
--a minor regurgitation of the Hibernate API JavaDoc
Once we have added all of our persistent,
JPA annotated Java classes to the config object, we invoke the
configure() method of the AnnotationConfiguration instance. This gets
the Hibernate configuration object to read the hibernate.cfg.xml file,
allowing Hibernate to understand how to access the underlying
database, while at the same time, telling the configuration instance
to process all of the JPA annotations on any decorated Java classes
that have been added to the config.
AnnotationConfiguration config =
new AnnotationConfiguration();
config.addAnnotatedClass(User.class);
config.configure();
*Sometimes I'll say config, or
configuration object, or config instance when I'm really talking about
the AnnotationConfiguration object. Just different ways of referring
to the same thing.
Neat AnnotationConfiguration Stuff
So, now that we've got ourselves a fully
initialized AnnotationConfiguration object, what can we do with it?
Well, we can do all sorts of neat stuff with it. The
AnnotationConfiguration object has the ability to kick off magical
objects called SessionFactories, which can in turn create even more
mystical objects known as Hibernate Sessions, which can be used to
perform all of the basic CRUD (Create, Retrieve, Update and Delete)
operations against the underlying database.
However, for our current purposes, we
want to use the AnnotationConfiguration object to create the various
tables that are needed to persist the state of our User class to the
database. To do this, we leverage the facilities of a special
Hibernate component known as the SchemaExport object, whose
create method can be passed an AnnotationConfiguration instance, with
which the SchemaExport object subsequently connects to the underlying
database and generates the various tables required by the Java based
object model. All of this can be accomplished by invoking the
SchemaExport object's very sensibly named create method.
new SchemaExport(config).create(true, true);
org.hibernate.tool.hbm2ddl
Class SchemaExport
java.lang.Object
org.hibernate.tool.hbm2ddl.SchemaExport
________________________________________
public class SchemaExport extends Object
Commandline tool to export table schema
to the database. This class may also be called from inside an
application.
--Hibernate API JavaDoc
AnnotationConfiguration Class Diagram
SchemaExport Class Diagram
The SchemaExport Create Method
You will notice that the create method of
the SchemaExport object takes two boolean parameters. The first
boolean value indicates whether or not you want the generated database
creation script to be printed out to the log file. The second
parameter indicates whether you want the generated script to be
executed against the underlying database. Passing two true values to
the create method will cause the database generation scripts to be
printed out to the log files, while also triggering the execution of
the database creation scripts, which would mean dropping the existing
tables in the database, and subsequently recreating them.
public void create(boolean script,
boolean export)
Run the schema creation script.
Parameters:
script - print the DDL to the console
export - export the script to the
database
As was mentioned before, the instance of
the AnnotationConfiguration object holds all of the information about
how to connect to your database, along with all of the information
gleaned from the JPA annotated JavaBeans that have been added as
annotated classes. With all of this delicious information, connecting
to the appropriate database schema and running a database create
script really isn't such a difficult endeavor.
AnnotationConfiguration config = new AnnotationConfiguration();
config.addAnnotatedClass(User.class);
config.configure();
new SchemaExport(config).create(true, true);
hibernate.cfg.xml Again
I keep mentioning that the
AnnotationConfiguration object reads from the hibernate.cfg.xml file
to glean information about how to connect to the database. I just
though it'd be polite to show you the hibernate.cfg.xml file again,
just so you don't have to flick back to an earlier tutorial where it
was first defined. Key properties for connecting to the database
include the connection.url, the dialect, the driver class and of
course, the username and password.
<?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.url">
jdbc:mysql://localhost/examscam
</property>
<property name="connection.username">root
</property>
<property name="connection.password">password
</property>
<property name="connection.driver_class">
com.mysql.jdbc.Driver
</property>
<property name="dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="transaction.factory_class">
org.hibernate.transaction.JDBCTransactionFactory
</property>
<property name="current_session_context_class">
thread
</property>
<!-- this will show us all sql statements -->
<property name="hibernate.show_sql">true
</property>
</session-factory>
</hibernate-configuration>
Running the Code
The following snippet of code can be used
to not only verify the proper configuration of our Hibernate
environment, but when it is executed, it will also connect to our
database and create the database related artifacts that are needed to
support the persistence of instances of the User class.
AnnotationConfiguration config =
new AnnotationConfiguration();
config.addAnnotatedClass(User.class);
config.configure();
new SchemaExport(config).create(true, true);
The question is, where are we going to
put this code? Personally, I like to use JUnit tests to run code, even
when I'm just experimenting, as opposed to writing a main method.
Using main methods to test code is very 1990's. Still, it's probably
the simplest way to run four lines of code, so I'm going to go
against my better judgment, and just add a main method to the User
class that includes all of the lines of code that we have written that
lead to the SchemaExport(config).create(true, true) command. Here's
how the main method looks, followed by the entire class definition:
public static void main(String args[]) {
AnnotationConfiguration config =
new AnnotationConfiguration();
config.addAnnotatedClass(User.class);
config.configure();
new SchemaExport(config).create(true, true);
}
Note that compiling and running this main
method will also require a few import statements as well:
package com.examscam.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.tool.hbm2ddl.SchemaExport; @Entity
public class User {
private Long id;
private String password;
@Id
@GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public static void main(String args[]) {
AnnotationConfiguration config =
new AnnotationConfiguration();
config.addAnnotatedClass(User.class);
config.configure();
new SchemaExport(config).create(true, true);
}
}
Before Running the User's main Method
Running the main method of the User class
should result in the creation of a table named user in the examscam
database schema. Now, one thing you must remember is that Hibernate
can create all the tables in the world, but it cannot create the
database schema for you. You have to go into the database and actually
create the schema yourself, a task that will involve a unique set of
steps depending upon the database you use.
Quickly Creating a Database Schema in
MySQL
I discussed creating a schema in the
previous tutorial, but I'll mention it again, just for the sake of
posterity.
Using the MySQL Query Browser GUI Tool,
creating a new schema is just two or three simple steps. You first
select the ?Create New Schema? option from the right-click, context
menu. In the dialog box that appears, you enter the name examscam, and
then click ?OK.? Once ?OK? is clicked, your database schema is
created!
Each database is different though, and if
you're having trouble, you may need to go to the reference material
for your particular database, or better yet, ask someone in the know,
or even post a query on a message board such as JavaRanch.com.
Running the User's main Method
Running the main method of the user class
sent the following output to my System.out.log file. There's some
interesting stuff here, although it's the SQL statement that gets spit
out at the end that I find most interesting. Take a look at it:
org.hibernate.cfg.annotations.Version
INFO: Hibernate Annotations 3.3.0.GA
org.hibernate.cfg.Environment INFO: Hibernate 3.2.5
org.hibernate.cfg.Environment
INFO: hibernate.properties not found
org.hibernate.cfg.Environment buildBytecodeProvider
INFO: Bytecode provider name : cglib
org.hibernate.cfg.Environment
INFO: using JDK 1.4 java.sql.Timestamp handling
org.hibernate.cfg.Configuration configure
INFO: configuring from resource: /hibernate.cfg.xml
org.hibernate.cfg.Configuration getConfigurationInputStream
INFO: Configuration resource: /hibernate.cfg.xml
org.hibernate.cfg.Configuration doConfigure
INFO: Configured SessionFactory: null
org.hibernate.dialect.Dialect
INFO: Using dialect: org.hibernate.dialect.MySQLDialect
org.hibernate.cfg.AnnotationBinder bindClass
INFO: Binding entity from annotated class: com.mcnz.olgc.data.User
org.hibernate.cfg.annotations.EntityBinder bindTable
INFO: Bind entity com.mcnz.olgc.data.User on table User
org.hibernate.validator.Version
INFO: Hibernate Validator 3.0.0.GA
org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: Running hbm2ddl schema export
org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: exporting generated schema to database
org.hibernate.connection.DriverManagerConnectionProvider configure
INFO: Using Hibernate built-in connection pool
org.hibernate.connection.DriverManagerConnectionProvider configure
INFO: Hibernate connection pool size: 20
org.hibernate.connection.DriverManagerConnectionProvider configure
INFO: autocommit mode: false
org.hibernate.connection.DriverManagerConnectionProvider configure
INFO: using driver: com.mysql.jdbc.Driver at URL: jdbc:mysql://localhost/examscam
org.hibernate.connection.DriverManagerConnectionProvider configure
INFO: connection properties: {user=root, password=****}
drop table if exists User
create table User
(id integer not null auto_increment,
password varchar(255), primary key (id))
org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: schema export complete
org.hibernate.connection.DriverManagerConnectionProvider close
INFO: cleaning up connection pool: jdbc:mysql://localhost/examscam
The Results of Running SchemaExport
From the log file that is generated by
running the User's main method, you'll notice a few interesting
tidbits of information, not the least of which is the following SQL
statement:
drop table if exists User
create table User
(id integer not null auto_increment,
password varchar(255),
primary key (id))
Hibernate created this SQL command based
on the JPA annotated User class. Furthermore, this was executed
against the database using the connection URL of:
jdbc:mysql://localhost/examscam, as was defined in the
hibernate.cfg.xml file. After executing this code and refreshing the
database schema, you should see a new table named user, with two
fields, an id field, and a password field. Hibernate magically created
this table for us. Pretty kewl, eh?
Inspecting the Created Database Table
Digging into some of the GUI tools for
MySQL, I can view information about the database table named user that
was created by Hibernate. While the id and password fields of the User
class were of type Long and String respectively, Hibernate knew enough
to create these fields in the MySQL database as INTEGER and
VARCHAR(45) values.
Furthermore, you will notice that the
primary key is defined as a NOT NULL column, along with the option to
AUTO INC the primary key as needed. The AUTO INC setting was a direct
result of the @GeneratedValue JPA annotation that was defined
immediately before the getId() method in the User class.
Class to Table Mappings
The promise of Hibernate is to make the
job of persisting and managing the state of your JavaBeans easy, or at
least, as non-intrusive to your Java applications as possible. As a
first pass through Hibernate, we have seen how easy it is to simply
decorate a typical JavaBean with some JPA annotations, and have the
Hibernate framework consume that JavaBean and subsequently create the
underlying database tables that are needed to persist and manage the
state of that JavaBean.
Hibernate really is an amazing,
lightweight framework, that allows Java developers to concentrate on
their object models, without burdening those same developers with the
onerous task of figuring out how to map their JavaBeans to the
underlying datastore.
|