The simplest mapping of them all is the one Java class
to one database table mapping. We saw this type of mapping with the
User class in the first couple of tutorials, where the User
class mapped directly to the user table, and properties
of the User class were mapped pretty directly to corresponding table
columns.
Just to liven things up a bit, I'm going to create a new class
called Snafu, and similarly, map that Snafu class to a
single database table named snafu. It's the same old one
class to one table mapping that we have seen before, but with a
different class and table just to keep things fresh.
Looking at the Snafu Class Code
Looking at the code for the Snafu class, you'll notice the
basic elements of a JPA annotated class, namely the required @Entity
tag above the class declaration, along with the @Id tag before the
getId() method. The getId() method is also decorated with the
@GeneratedValue annotation to allow the database to create unique ids
when new records are added to the Snafu table.
Adding the Class to the Configuration
With the @Entity, @Id and @GeneratedValue tags added, the SNAFU
class has all of the annotations required for it to have its
persistence managed by Hibernate. We have even added a runnable main
method that leverages the HibernateUtil class to begin a transaction
and provide access to the Hibernate Session.
Be aware of the fact that in order for Hibernate to understand
how to manage the persistent state of the Snafu class, the Snafu class
must be added to the AnnotationConfiguration, which is done in the
getInitializedConfiguration() method of our HibernateUtil class.
Failing to add the Snafu class to the configuration will result in the
following runtime exception:
Exception in thread "main"
org.hibernate.MappingException:
Unknown entity:
com.examscam.mappings.Snafu at
org.hibernate.impl.SessionFactoryImpl.getEntityPersisterAs
a convenience, the full HibernateUtil class, with the added annotation
for the Snafu class, can be found at the end of this tutorial.
public class HibernateUtil {
public static Configuration getInitializedConfiguration(){
AnnotationConfiguration config = new AnnotationConfiguration();
/* add all of your JPA annotated classes here!!! */
config.addAnnotatedClass(User.class);
config.addAnnotatedClass(Snafu.class);
config.configure();
return config;
}
}
One Class to One Table Hibernate
Mapping
package com.examscam.mappings;
import javax.persistence.*;
import org.hibernate.Session;
import com.examscam.HibernateUtil;
@Entity
public class Snafu {
long id;
String situation;
@Id
@GeneratedValue
public long getId() {return id;}
public void setId(long id) {this.id = id;}
public String getSituation(){
return situation;
}
public void setSituation(String situation) {
this.situation = situation;
}
/* main not required ? just for testing */
public static void main(String args[]) {
HibernateUtil.recreateDatabase();
Snafu snafu = new Snafu();
snafu.setSituation("normal");
Session session = HibernateUtil.beginTransaction();
session.save(snafu);
HibernateUtil.commitTransaction();
}
}
Creating the snafu Database Table
Notice how the first line of code in the Snafu class' main
method is a call to HibernateUtil.recreateDatabase(). This method call is
required in order to create the underlying snafu table. If the snafu
table is already created, you can comment out this line of code.
If the snafu table is not created and you try to run this code
without the call to recreate the database, you will get the following
error:
SEVERE:
Table 'examscam.snafu' doesn't exist Exception in thread "main"
org.hibernate.exception.SQLGrammarException: could not insert:
[com.examscam.mappings.Snafu] at
org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:67)
at
org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
Of course, if you wanted to create the snafu table by trowing
some SQL at your database, here's the script; it's pretty simple:
DROP TABLE IF EXISTS `examscam`.`snafu`;
CREATE TABLE `examscam`.`snafu` (
`id` bigint(20) NOT NULL auto_increment,
`situation` varchar(255) default NULL,
PRIMARY KEY (`id`)
SNAFU is an acronym meaning roughly,
"things are in a mess ? as usual".
The
most commonly accepted rendering is "Situation Normal: All
F*c*ed Up". It is sometimes given as "Situation Normal:
All Fouled Up" or similar, in circumstances where profanity is
discouraged or censored. In modern usage, "snafu" is
often used as an interjection, as a shorthand for the sentiment
expressed by the phrase. "Snafu" is also sometimes used
as a noun or verb, referring to a situation that suddenly went
awry, or the cause of the trouble. The acronym is believed to have
originated in the US Army during World War II.
-SNAFU, Wikipedia.org |
Inspecting the Results of the Snafu's main
When the Snafu class runs, you'll likely see a statement in
your log files such as Hibernate: insert into Snafu (situation) values
(?). Furthermore, if you inspect your database, you should see a new
record containing an autogenerated id, along with a value of ?normal'
in the situation column.
The Full HibernateUtil Class
I know some people may be jumping directly to this tutorial, and
may not have read the tutorial that created the HibernateUtil class. As
a convenience, I'm going to provide the HibernateUtil class in its
entirety. Just part of my attempt to make this tutorial as easy to follow as possible.
package com.examscam;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import com.examscam.model.User;
import com.examscam.mappings.Snafu;
public class HibernateUtil {
private static SessionFactory factory;
public static Configuration getInitializedConfiguration() {
AnnotationConfiguration config=new AnnotationConfiguration();
/* add all of your JPA annotated classes here!!! */
config.addAnnotatedClass(User.class);
config.addAnnotatedClass(Snafu.class);
config.configure();
return config;
}
public static Session getSession() {
if (factory == null) {
Configuration config =
HibernateUtil.getInitializedConfiguration();
factory = config.buildSessionFactory();
}
Session hibernateSession =
factory.getCurrentSession();
return hibernateSession;
}
public static void closeSession() {
HibernateUtil.getSession().close();
}
public static void recreateDatabase() {
Configuration config;
config = HibernateUtil.getInitializedConfiguration();
new SchemaExport(config).create(true, true);
}
public static Session beginTransaction() {
Session hibernateSession;
hibernateSession = HibernateUtil.getSession();
hibernateSession.beginTransaction();
return hibernateSession;
}
public static void commitTransaction() {
HibernateUtil.getSession().getTransaction().commit();
}
public static void rollbackTransaction() {
HibernateUtil.getSession().getTransaction().rollback();
}
}
@GeneratedValue JPA AnnotationQuite often in these tutorials, we have used the @GeneratedValue
annotation to have the database generate a unique primary key for us.
We have used the default Generation Type in each of our examples, but
there are actually four different strategies for having the primary
key generated by the database. Those four options are:
- AUTO
- IDENTITY
- TABLE
- SEQUENCE
javax.persistence.GenerationType.AUTO
The AUTO generation strategy is the default, and this setting
simply chooses the primary key generation strategy that is the default
for the database in question, which quite typically is IDENTITY,
although it might be TABLE or SEQUENCE depending upon how the database
is configured. The AUTO strategy is typically recommended, as it makes
your code and your applications most portable.
javax.persistence.GenerationType.IDENTITY
The IDENTITY option simply allows the database to generate a
unique primary key for your application. No sequence or table is used
to maintain the primary key information, but instead, the database
will just pick an appropriate, unique number for Hibernate to assign
to the primary key of the entity. With MySQL, the first lowest
numbered primary key available in the table in question is chosen,
although this behavior may differ from database to database.
javax.persistence.GenerationType.Sequence
Some database vendors support the use of a database sequence
object for maintaining primary keys. To use a sequence, you set the
GenerationType strategy to SEQUENCE, specify the name of the generator
annotation, and then provide the @SequenceGenerator annotation that
has attributes for defining both the name of the sequence annotation,
and the name of the actual sequence object in the database.
Here's what the getId() method of the Snafu class would look like if
we used a SEQUENCE GenerationType:
@Id
@SequenceGenerator(name="s1", sequenceName="SEQ")
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="s1")
public long getId() {return id;}
javax.persistence.GenerationType.TABLE
The TABLE GenerationType allocates a separate database table to
keep track of the generation of unique ids. To facilitate the
description of the table to be used, the TABLE strategy works hand in
hand with the @TableGenerator annotation. So, if our Snafu class was
to use a separate table for managing primary keys, the getId() method
annotation would look like this:
@Id
@TableGenerator(name="tg", table="pk_table",
pkColumnName="name", valueColumnName="value",
allocationSize=10
)@GeneratedValue(strategy=GenerationType.TABLE, generator="tg")
public long getId() {return id;}
With this @TableGenerator annotation, a separate table in the
database named pk_table would be created with two columns, one called
name, and the other called value. The name will simply be Snafu, the
name of the class using the table, indicating the class for which the
key is being maintained. The pkColumnValue will maintain the current
iteration of key generation. Furthermore, the allocationSize
determines the increment size of the generated primary keys for a new
thread.
Using the @TableGenerator Annotation
So, if we recreated the database using the @TableGenerator
annotation,and then added three Snafu objects to the database in
separate threads, a pk_table would maintain a database column with the
name of Snafu, indicating through the value column that three
iterations of primary key generations would have occurred.
Furthermore, the Snafu table would have three records, with the
primary keys all incrementing to the nearest hundred value.
|