Adding Records to a Database: Persistence with Hibernate and JPAThe first Hibernate tutorial on this website dealt with what was required in order to start working with and developing a Hibernate application.
The second key tutorial on Hibernate and JPA looked at the Hibernate configuration object that manages JPA annotated JavaBeans, namely the AnnotationConfiguration object. Along with the SchemaExport class, we managed to take a JPA annotated JavaBean and have Hibernate create the underlying database table that is needed to persist the state of instances of the User POJO we created.
The next logical step is to actually look at what is required to take an instance of a JavaBean and actually persists the state of that instance to the database. To do so, we'll have to familiarize ourselves with the following topics:
- creating and initializing the properties of a JavaBean
- obtaining a Hibernate SessionFactory
- starting and committing database transactions using the
Hibernate Session
- using the Hibernate API to dictate which instances will
be persisted to the database
These topics will be the focus of this tutorial, and once you're done reading it, you'll be familiar with the basic steps that are required to persist an instance of a JavaBean to the database using Hibernate & JPA annotations.
Mapping the User Class to the user
Table
This tutorial will continue with the mapping of the POJO named User to the corresponding table in the examscam database named user.
Here's the SQL code that was used to create the user table:
drop table if exists User
create table User
(id integer not null auto_increment,
password varchar(255),
primary key (id))
The User Class
I know I've printed this out before, but I like to make sure that if anyone is jumping into this tutorial, they don't have to backtrack page after page to see what we've been up to. For the sake of posterity, here's the User class as we have coded it so far. In this chapter, we'll be building upon the code inside of the main method.
The Annotated User POJO
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);
}
}
Persisting Data to the Database
So, with the User class coded, we have a fine example of a simple, JPA annotated JavaBean. Furthermore, an earlier tutorial showed us how a Hibernate configuration object can read the hibernate.cfg.xml file, ingest a JPA annotated POJO and subsequently connect to a JDBC compliant database and create the various tables needed to support POJO based persistence.
But, how can we get Hibernate to programmatically persist the state of a JavaBean instance to the database? Well, there are a few steps involved, and a few new Hibernate objects will be required, such as the Hibernate Session and the Hibernate SessionFactory, but overall, it's a pretty interesting, relatively easy, and fairly elegant process.
Editing the User's main Method
The main method in our User class creates and configures an AnnotationConfiguration object that is aware of our User class. That's really the first step in doing any type of Hibernate persistence work. In the main method of the User class, we then have Hibernate create the required database tables by calling the create method on a new SchemaExport object. Let's assume that we've already created our database, and comment out the call to the SchemaExport task. After all, we don't want to keep recreating the database every time we run our code.
public static void main (String args[]) {
/*create the configuration object*/
AnnotationConfiguration config =
new AnnotationConfiguration();
/*make hibernate aware of the User POJO*/
config.addAnnotatedClass(User.class);
/*read and process hibernate.cfg.xml
and JPA metadata*/
config.configure();
/* COMMENT OUT THE SchemaExport CALL */
// new SchemaExport(config).create(true, true);
}
Hibernate & Transient Instances
When an instance of a JavaBean is created in your Java code, the JVM treats it as a free
and unfettered spirit. As long as the instance is in scope, its properties can be referenced, and its methods can be invoked. When a JavaBean goes out of scope, it is simply forgotten, and the JVM's garbage collector takes care of the funeral arrangements. In the Hibernate arena, these types of instances are called
transient instances, because their existence is evanescent, as Hibernate remains completely ignorant of them.
So, even though the User class may have been added as an annotated class to the Hibernate configuration, the following instance of the User class would be transient in its existence, as Hibernate would be completely oblivious to it, and as such, the POJO's state would never get persisted to the underlying database:
public static void main (String args[]) {
AnnotationConfiguration config =
new AnnotationConfiguration();
config.addAnnotatedClass(User.class);
config.configure();
// new SchemaExport(config).create(true, true);
/* This User instance, u, is completely transient.
Hibernate is not aware of it,
nor does Hibernate care about it. */
User u = new User();
u.setPassword("abc123");
}
/* as the User instance, u, goes out of scope,
it is garbage collected,
and its state is not persisted. */
The Hibernate Session and the
SessionFactory
When a JavaBean is instantiated in your Java code, it is treated as a transient
instance, and Hibernate pretty much ignores it. So, the question is, how do you get Hibernate to not only pay attention to your JavaBean, but to manage the persistence lifecycle of the JavaBean as well?
In order to get Hibernate to pay attention to a JavaBean, the instance must touch, or become associated with, a Hibernate Session. The Hibernate Session is without debate, the most magical of all of the Hibernate components. Simply passing a JPA annotated instance, such as an instance of the User class, to the saveOrUpdate method of a Hibernate Session object is enough to pique Hibernate's interest in the JavaBean in question, and get Hibernate to manage all of the instance's persistence. However, the question is, where do we get our hands on a Hibernate Session? Well, as any good 18th century industrialist will tell you, you get things from a Factory, or more specifically in our case, a SessionFactory.
Sessions and the
org.hibernate.SessionFactory
The Hibernate SessionFactory is actually extracted from the configuration object through a compound method call, with the first call being to the AnnotationConfiguration object's config() method, and the second to the somewhat self-explanatory buildSessionFactory() method:
AnnotationConfiguration config =
new AnnotationConfiguration();
config.addAnnotatedClass(User.class);
config.configure();
// new SchemaExport(config).create(true, true);
/*The SessionFactory is obtained
through the config object*/
SessionFactory factory =
config.buildSessionFactory();
/* The User instance is still transient
and not associated with any Hibernate Session.*/
User u = new User();
u.setPassword("abc123");
The SessionFactory is of type org.hibernate.SessionFactory, which you may need to import if you're coding along.
Class Diagram for the SessionFactory
The SessionFactory and Resource
Allocation
The SessionFactory itself is a fairly resource intensive object to create. In the first few Hibernate tutorials on this website, we'll typically create SessionFactory objects whenever we need them, but in practice, it is imperative to control how often the SessionFactory gets created. Minimizing the number of SessionFactory objects you create can be done by perhaps making the SessionFactory a static variable that is accessed through a Singleton design pattern, or better yet, in a J2EE environment, simply have the SessionFactory bound to, and accessed through, the JNDI naming service of the J2EE application server. You really only need one SessionFactory in any application. Controlling the number of SessionFactory objects that get created, and for that matter, destroyed, will help to minimize unnecessary resource allocations.
On the other hand, the Hibernate Session, which is produced by the SessionFactory, is a very efficient object that you don't have to feel guilty about creating willy-nilly. A single SessionFactory is great at efficiently pumping out Hibernate Session objects whenever you need them. Limit the number of SessionFactory objects that are created in an enterprise application to one, but feel guilt-free about generating Hibernate Sessions whenever you need them.
Sessions and the
org.hibernate.SessionFactory
It doesn't take a genius to figure out that the job of the SessionFactory is to pump out Hibernate Session objects. Obtaining the Session is actually a pretty straight forward endeavor once you have access to the SessionFactory, as you simply have to invoke the factory's getCurrentSession() method.
AnnotationConfiguration config =
new AnnotationConfiguration();
config.addAnnotatedClass(User.class);
config.configure();
// new SchemaExport(config).create(true, true);
SessionFactory factory = config.buildSessionFactory();
Session session = factory.getCurrentSession();
/* a session is around, but
the User is still transient! */
User u = new User(); u.setPassword("abc123");
Putting the Hibernate Session on Alert
Status
Once you have your Hibernate Session all instantiated and ready to go, you need to rattle its cage a little bit and let it know that it is about to be used to perform some database interactions. The beginTransaction() method of the Hibernate Session does just that; it essentially puts the Hibernate Session on alert.
After invoking the beginTransaction() method, you do all of your JavaBean stuff, and then pass your JavaBean instances to the save, or update, or saveOrUpdate methods of the Hibernate Session. When all of that is done, you typically see the following compound method call to commit the transaction: session.getTransaction().commit().
Session session = factory.getCurrentSession();
session.beginTransaction();
/* Do some sexy JavaBean & POJO stuff in here */
session.getTransaction().commit();
Saving a Simple POJO with Hibernate
So far, we've laid the groundwork for obtaining a Hibernate Session, and we've talked about beginning a transaction, and correspondingly, the method call needed to commit a transaction. But how do we actually get the data contained in our JavaBean persisted to the database? Well, from a Java coding point of view, it is incredibly simple.
Let's think about our User object for a second; it has two properties, one of which is the id, (which through the @GeneratedValue annotation, we have already indicated that it will be generated for us by the database) and a property of type String, named password. To create a new database record representing the state of a User instance, all we have to do is create an instance of the User class, initialize the password field, and then tell Hibernate to save the state of the instance we just created to the database using the saveOrUpdate(Object obj) method call. It's just that easy!
Session session = factory.getCurrentSession();
session.beginTransaction();
/* create & initialize your User POJO */
User u = new User();
u.setPassword("abc123");
/* at this point, the User instance
is still transient */
/* ask the session to save your
POJO to the db*/
session.saveOrUpdate(u);
/*at this point, the User instance
is considered persistent*/
session.getTransaction().commit();
Persistent Objects
Once a JavaBean has touched by the Hibernate Session, which happens when an instance is passed to the saveOrUpdate method of the Session, the instance stops being transient, and becomes a persistent object. Once a JavaBean has 'touched' the Session within the scope of a transaction, Hibernate will manage the persistent state of that instance, right up until the point that the enclosing transaction is committed.

save, update and saveOrUpdate
Notice how we have called the saveOrUpdate method of the Hibernate Session. If a JavaBean gets passed to this method, and it has an id, Hibernate will attempt to update the existing, corresponding row, in the database ; after all, if an instance has an id, it must have a corresponding representation in the database. On the other hand, if the JavaBean passed to the saveOrUpdate method does not have a primary key attached to it, Hibernate will treat the operation as a create invocation, and create a new row in the database.
Hibernate does have separate update and save method calls, but in light of the fact that the functionality of the two is so similar, a combined saveOrUpdate method has been added to the API. Use it, it's much simpler!
|
public void
saveOrUpdate(Object object)throws HibernateException
Either save(Object) or
update(Object) the given instance, depending upon resolution of the
unsaved-value checks. This operation cascades to associated
instances if the association is mapped with cascade="save-update".
-another shameless regurgitation of
the Hibernate API JavaDoc |
Basking in the Beauty of Hibernate
Persistence
Believe it or not, but as far as the Java programming aspect goes, that's how easy it is to persist a JavaBean instance to the database using Hibernate. You create your JavaBeans just as you normally would, and then you ask the Hibernate Session to save your instances. Next thing you know, once you have committed your transaction, a new record will pop up in your database. It's amazing!
Pulling it all Together in Code
Code Figure 3-1 pulls together all of the code we have discussed so far with regards to interacting with the Hibernate Session and persisting the state of an instance of the User class to the database, all within the confines of a runnable main method. With an appropriately configured hibernate.cfg.xml file, which we created and tested in an earlier tutorial, we could run the main method of the User class as a stand-alone Java application, and the code would actually persist an instance of the User class, storing a password of abc123, to our database, along with a uniquely generated id for the database row.
Hibernate and JPA Imports
One thing to notice about the code in Figure 3-1 is the collection of import statements before the class declaration. As you can see, we are using an assortment of classes pulled from the Hibernate API's org.hibernate package, along with classes, or more accurately, annotations, from the Java Persistence API, which are found in the javax.persistence package.
Java Classes and main Methods
Now I should also point out that I'm not a big fan of using the main method of a Java class for testing purposes. Creating proper tests is not only an art, but it is an extremely important part, if not the most important part, of any enterprise application development endeavor. However, for learning, I think it is important to keep things as simple as possible, especially at the beginning, as we iteratively incorporate more and more complex ideas. For now, testing our code in the main method of a POJO should not be a punishable offence, but good Java developers should make testing frameworks such as JUnit and EasyMock an important ingredient in their daily programming bread.
Code Figure 3-1
package com.examscam.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
@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);
SessionFactory factory =
config.buildSessionFactory();
Session session = factory.getCurrentSession();
session.beginTransaction();
System.out.println("creating user");
User u = new User();
u.setPassword("abc123");
session.saveOrUpdate(u);
System.out.println("user saved");
session.getTransaction().commit();
System.out.println("transaction successful!!!");
}
}
Running the Application
With all of the Hibernate Core, Hibernate Annotations and JDBC driver JAR files are on your classpath, having properly edited the hibernate.cfg.xml file, having eliminated any compile errors in your code, and having your database up and running, you are ready to test your User class by running its executable main method. Running my main method results in the following output:
creating Hibernate config
creating user
Hibernate: insert into User (password) values (?)
user saved
log4j:
WARN No appenders could be found
for logger (org.hibernate.cfg.annotations.Version).
log4j:
WARN Please initialize the log4j system properly.
transaction successful!!!
Looking at the Database Results
After running the User class' main method, when I look at the user table in my database, I actually see that the state of the User object I created in my Java code has been persisted by Hibernate to the database.

The id of the new record is 1, and the password is abc123, which is the value that was hard-coded into the main method of the User class.
Showing SQL in the Output
In the console output generated from running my main method, which is displayed above, you will notice that I bolded the insert SQL statement. This statement was printed out by Hibernate as a result of the hibernate.show_sql property that was set to true in the Hibernate configuration file. This is a great property to have enabled during development time, although you should disable this setting in production as this will tend to slow down your application as it swamps your JVM log files with needless SQL verbosity.
<property
name="hibernate.show_sql">true</property>
Setting up log4J
You will also notice a little log4J error message in my output:
log4j:WARN No appenders could be
found for logger
log4j:WARN Please initialize the log4j system properly. This error is due to the fact that Hibernate is using Log4J, and a properties file that Log4J uses to initialize itself cannot be found on the classpath. The Hibernate core download contains a sample log4J.properties file you can place on your classpath that will eliminate this error message and subsequently direct logging entries to the System.out log. Alternatively, you can create your own log4J.properties file with the following settings:
### 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{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - ###
log4j.rootLogger=
warn, stdout log4j.logger.org.hibernate=info
### log JDBC bind parameters ###
log4j.logger.org.hibernate.type=info
### log schema export/update ###
log4j.logger.org.hibernate.tool.hbm2ddl=debug
Make sure the file is named log4j.properties, and is placed on the classpath of the JVM. Then you'll get all sorts of crazy log messages!
Revisiting the hibernate.cfg.xml File
<?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>
<!-- mapping files -->
</session-factory>
</hibernate-configuration>
|