Column Mapping
Okay, I'm pretty happy about all of the progress we've made so far. Using a fairly simple User class, we've explored all of the basic database operations, looked at some of the fundamental JPA tags we can use to decorate our JavaBeans, and we've even explored some best practices and design patterns through the HibernateUtil class. However, the fact remains that our User class is really starving from a lack of property nutrition. It's time to add some more properties to the User class, and in the process, explore the following topics:
- mapping columns with the @Column JPA
Annotation
- mapping to tables and schemas with the @Table JPA
annotation
- annotating non-persistent properties with the @Transient
annotation
- mapping advanced data types such as dates and large
character objects
Adding the LoginName Property
Now, one of the thing you may have noticed about the User example that we have been working with so far is the fact that the name of our table, along with the name of the table-columns, exactly matched the class and property names of the User POJO. There is definitely something to be said for keeping the first few examples simple; However, it's not too often that every database table and column is going map perfectly to the names used in your domain model.
One of the fields I wanted to add to my User JavaBean was a loginName field. It just makes sense that a JavaBean named User, that contains a property named password, would also provide some way of maintaining a unique, identifying name for logging a user in. So, I want to add a field, of type String, to my User class, but the column in the User table must be named login_name. I know it's not a huge difference, but it is enough to throw the Hibernate framework off a little bit. If the Hibernate framework sees a POJO with an identically named table, or a field in a JavaBean with an identically named database column, well, Hibernate does a great job of doing the Java to database mappings. However, when database and an POJO names do not coincide, you've got to add some new annotations to your JavaBeans.
The @Column Annotation
@Column(
name="columnName";
boolean unique() default false;
boolean nullable() default true;
boolean insertable() default true;
boolean updatable() default true;
String columnDefinition() default "";
String table() default "";
int length() default 255;
int precision() default 0;
int scale() default 0;
)
Adding a Column Annotation
One of the great things about Hibernate is that it makes mapping data from your JavaBeans to the database fairly easy, even when changes happen in either your object model, or your data model.
Right now, we have the challenge of mapping a JavaBean field named loginName to a column named login_name in the database. Well, doing so really isn't all that complicated; it's just a matter of adding the appropriate variable declaration, setters and getters, and finally, adding a special @Column annotation that describes to Hibernate how to perform the field to database column mapping.
import javax.persistence.Column;
@Entity
public class User {
private Long id;
private String password;
private String loginName;
@Column (name="login_name")
public String getLoginName() {
return loginName;
}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
As you can see, mapping the loginName property to the login_name field in the database is as simple as adding the @Column annotation before the getLoginName() method, and providing the (name="login_name") attribute to the annotation.
Coding for the Updated loginName Field
To take advantage of the new field, all we need to do is add a line of code in the main method of the User class that initializes the loginName property, user.setLoginName("mj");. We can then commit the transaction to the database.
public static void main(String args[]){
System.out.println("creating Hibernate config");
AnnotationConfiguration config =
new AnnotationConfiguration();
config.addAnnotatedClass(User.class);
SessionFactory factory;
factory = config.configure().buildSessionFactory();
Session session = factory.getCurrentSession();
session.beginTransaction();
System.out.println("creating user");
User user = new User();
/*here is the new stuff!!!
The instance is now named user,
just to shake it up a bit. :) */ user.setLoginName("mj");
user.setPassword("abc123");
session.save(user);
System.out.println("user saved");
session.getTransaction().commit();
System.out.println("transaction successful!!!");
}
Recreating the Database
Of course, if you try to persist the loginName field of the User to the database, you better make sure the user table in the database has a column named login_name, otherwise you'll be seeing an org.hibernate.exception.SQLGrammarException telling you about an unknown
'column name' in the 'field list.' To avoid this exception, you can run the HibernateUtil's main method to recreate the database, or just run this SQL script:
create table User
(id bigint not null auto_increment,
login_name varchar(255),
password varchar(255),
primary key (id))
The Newly Inserted User Object
As you can see, when the database is reconfigured to support the login_name field, and the main method of the User class is run, an entry is made into the database that not only includes a unique id and a password, but it also includes a login_name field with the value of 'mj' assigned to it.
User user = new User();
user.setLoginName("mj");
user.setPassword("abc123");
session.save(user);

package com.examscam.model;
import javax.persistence.GeneratedValue;
import javax.persistence.Entity;
import javax.persistence.Id;import org.hibernate.Session;
import javax.persistence.Column;
import com.examscam.HibernateUtil;
@Entity
public class User {
private Long id;
private String password;
private String loginName;
@Column (name="login_name")
public String getLoginName() {
return loginName;
}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
@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[]){
Session session=HibernateUtil.beginTransaction();
System.out.println("creating user");
User user = new User();
/*here is the new stuff!!! The instance is now named user, just to shake it up a bit.*/
user.setLoginName("mj");
user.setPassword("abc123");
session.save(user);
System.out.println("user saved");
HibernateUtil.commitTransaction();
System.out.println("transaction successful!!!");
}
}
Further Annotations
As we have seen, when a POJO is marked with an @Entity tag, Hibernate will attempt to persist the POJO to a database table with a matching name. Furthermore, Hibernate will attempt to persist the properties of a JavaBean to similarly named columns in the database table. We saw this functionality when our User POJO was magically mapped to the user table in the examscam database, and the id and password properties in the JavaBean were bewitchingly persisted to database columns with matching names. It was only when a property of the JavaBean, and the corresponding database column, did not match, that we were forced to dig into our JPA bag of tricks and pull out the @Column annotation.
Still, as reliable as Hibernate is, sometimes annotating your code, even when it's not explicitly necessary, isn't such a bad idea. It wouldn't kill us to annotate every property in our JavaBean, and it might even make our POJO a little bit more descriptive and readable. We could start by adding column annotations to each of the properties of our JavaBean, not just the loginName.
@Id
@GeneratedValue
@Column (name="id")
public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
@Column (name="login_name")
public String getLoginName() {return loginName;}
public void setLoginName(String n) {this.loginName =n;}
@Column (name="password")
public String getPassword() {return password;}
public void setPassword(String p) {this.password = p;}
@Table Annotation
With out User JavaBean, we simply provided an @Entity annotation before the class definition, and allowed the Hibernate framework to match the User class to a corresponding table in the examscam database. But you can modify this behavior by using the @Table annotation, and providing custom attributes for the table name and database schema.
import javax.persistence.Table;
@Entity
@Table (name="user", schema="examscam")
public class User {
connection.url and the Schema
Definition
Up until this point, we have never defined or mentioned the database schema name in our Java code. Just in case you had forgotten, the examscam schema name is defined as a SessionFactory property in the connection url entry in the hibernate.cfg.xml file. When the schema property is not explicitly defined using the @Table annotation, the default becomes the schema specified in the connection.url property.
<session-factory>
<property name="connection.url">
jdbc:mysql://localhost/examscam
</property>
</session-factory>
Our Heavily Annotated User Class
package com.examscam.hib.exam;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
@Entity
@Table (name="user", schema="examscam")
public class User {
private int id;
private String password;
private String loginName;
@Id
@GeneratedValue
@Column (name="id")
public int getId() {return id;}
public void setId(int id) {this.id = id;}
@Column (name="login_name")
public String getLoginName() {return loginName;}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
@Column (name="password")
public String getPassword() {return password;}
public void setPassword(String password) {
this.password = password;
}
public static void main(String args[]){ }
}
Non-Persistent JavaBean Properties
It's not unreasonable to expect that from time to time, a JavaBean will contain some properties that need to be persisted to a database, and some properties that do not need to be stored in the database. However, when you mark a POJO with the @Entity annotation, Hibernate will attempt to persist every property of the JavaBean. The default behavior of Hibernate is to persist every non-transient and non-static property of a POJO.
However, if your JavaBean has an instance level property that you do not want Hibernate to save to the database, you can simply decorate the non-persistent property with a simple little @Transient annotation.
Transient Annotation JavaDoc:
Annotation Type Transient
________________________________________
@Target(value={METHOD,FIELD})
@Retention(value=RUNTIME)
public @interface Transient
This annotation specifies that the property or field is not persistent. It is used to annotate a property or field of an entity class, mapped superclass, or embeddable class.
________________________________________
Using the @Transient Annotation
package com.examscam.hib.exam;
import javax.persistence.Column;import javax.persistence.Entity;
import javax.persistence.GeneratedValue;import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
@Entity
@Table (name="user", schema="examscam")
public class User {
private int id;
private String loginName;
private String password;
private String encryptedPassword; @Transient
public String getEncryptedPassword () {
return encryptedPassword;
}
public void setEncryptedPassword(String ep){
this. encryptedPassword = ep;
}
@Id
@GeneratedValue
@Column (name="id")
public int getId() {return id;}
public void setId(int id) {this.id = id;}
@Column (name="login_name")
public String getLoginName() {return loginName;}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
@Column (name="password")
public String getPassword() {return password;}
public void setPassword(String password) {
this.password = password;
}
public static void main(String args[]){ }
}
Non-Persistent @Transient Properties
In the latest iteration of the User POJO's main method, we actually set the encryptedPassword property of the user, and then persist the user to the database. However, as you can see from the resultset, and the underlying database table, the encryptedPassword field is not persisted to the database as a result of it being marked with the @Transient annotation in the User POJO.
public static void main(String args[]){
Session session=HibernateUtil.beginTransaction();
System.out.println("creating user");
User user = new User();
user.setLoginName("mj");
user.setPassword("abc123");
user.setEncryptedPassword("zab012");
session.save(user);
System.out.println("user saved");
HibernateUtil.commitTransaction();
System.out.println("transaction successful!!!");
}
User Table, User Class, Database
ResultSet

As you can see, when comparing the class diagram of the User to the underlying database table, the encryptedPassword property, which is marked as being @Transient, does not have any representation in the database.
Adding Fields to our User POJO
Our User class is maturing quite quickly, and as such, there are a few more helpful fields I'd like to add to the User JavaBean, namely information about the User's registration date, whether or not the User has verified their registration information, their email address, and finally, the User's last access time. My first step in bringing this vision to fruition is to add the appropriate, private, instance variables to the User class.
private
String emailAddress; private
Boolean verified; private
java.util.Date lastAccessTime; private
java.util.Calendar registrationDate; Notice that I have shaken things up a little bit by introducing some new data types. The user will now have an additional String property, a primitive of type Boolean, and two date related variables, one of type Date, and one of type Calendar. To shake things up even more, I should probably tell you that a java.util.Calendar object is actually an abstract class, meaning that it can't be directly instantiated in code, but instead, a concrete subclass has to be instantiated instead. See, I told you things would get a bit more complicated. Of course, if we're going to add instance variables, we'll need to add some setters and getters, so if you're following along, you might want to add those in as well. :)
Looking at the Annotations
When storing time based information in a database, it can be stored in one of three ways: as a time, as a date, or as a time-date combination known as a TIMESTAMP. If the Hibernate framework sees a time based property, it will be treated as a TIMESTAMP unless you tell Hibernate otherwise. For the instances of the Calendar and Date fields in our User class, we will decorate the getters for our fields with a @Temporal annotation, and use the TemporalType constants to clarify the property as being of type TIME, DATE or TIMESTAMP.
@Temporal(TemporalType.TIMESTAMP)
public java.util.Date getLastAccessTime() {
return lastAccessTime;
}
@Temporal(TemporalType.DATE)
public java.util.Calendar getRegistrationDate() {
return registrationDate;
}
Default @Basic Annotation
The other properties we added, namely the emailAddress field and the verified field of type Boolean are fairly innocuous, with the exception of the @Basic annotation placed in front of the isVerified() method.
@Basic
public Boolean isVerified() {
return verified;
}The @Basic annotation is pretty much redundant for attributes defined within a JavaBean. Any non-static field that is defined within a JavaBean has an implicit @Basic annotation defined for it by default. We will decorate the isVerified() method with the @Basic annotation, but we will leave the @Basic annotation off the emailAddress field to emphasize the fact that @Basic is an optional decoration.
package com.examscam.model;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import com.examscam.HibernateUtil;
@Entity
@Table(name = "user", schema = "examscam")
public class User {
private Long id;
private String loginName;
private String password;
private String encryptedPassword;
private String emailAddress;
private Boolean verified;
private java.util.Date lastAccessTime;
private java.util.Calendar registrationDate;
@Transient
public String getEncryptedPassword() {
return encryptedPassword;
}
public void setEncryptedPassword(String ep) {
this.encryptedPassword = ep;
}
@Id
@GeneratedValue
@Column(name = "id")
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
@Column(name = "login_name")
public String getLoginName() {
return loginName;
}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
/***** continued on next page *****/
@Column(name = "password", nullable=false)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmailAddress() {
return emailAddress;
}
@Temporal(TemporalType.TIMESTAMP)
public java.util.Date getLastAccessTime() {
return lastAccessTime;
}
@Temporal(TemporalType.DATE)
public java.util.Calendar getRegistrationDate() {
return registrationDate;
}
@Basic
public Boolean isVerified() {
return verified;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
public void setLastAccessTime
(java.util.Date lastAccessTime) {
this.lastAccessTime = lastAccessTime;
}
public void setRegistrationDate
(java.util.Calendar registrationDate){
this.registrationDate = registrationDate;
}
public void setVerified(Boolean verified) {
this.verified = verified;
}
public static void main(String args[]){ }
}
Testing the New User Fields
As we have updated the User class, it would make sense to add some code to our main method that will initialize the new properties of the User bean. This will then help us test to see if the changes to our User class have made it to the database. Of course, before we can even test our code, we must make sure the database itself has been updated to reflect the changes in our code. There's a couple of ways to sync up the database with our new and improved User class, the easiest of which is to simply run the HibernateUtil.recreateDatabase() method. Of course, if you're more of a sadist, you can always run the following SQL statement:
drop table if exists examscam.user
create table examscam.user
(id bigint not null auto_increment,
emailAddress varchar(255), lastAccessTime datetime,
login_name varchar(255), password varchar(255) not null,
registrationDate date, verified bit, primary key (id))
With the underlying database updated, a few quick updates to the main method of the User class are in order:
public static void main(String args[]){
Session session=HibernateUtil.beginTransaction();
User user = new User();
user.setLoginName("mj");user.setPassword("abc123");
user.setEncryptedPassword("zab012");
user.setEmailAddress("mj@scja.com");
user.setLastAccessTime(new java.util.Date());
user.setRegistrationDate(
new java.util.GregorianCalendar());
user.setVerified(Boolean.FALSE);
session.save(user);
HibernateUtil.commitTransaction();
System.out.println("transaction successful!!!");
}
Looking at the Results
After recreating the user table in the database by running the HibernateUtil.recreateDatabase() method, and then running the main method of the User class, you can see that a new record is added to the database, with all of the appropriate fields updated. Noteworthy additions to the database table are the lastAccessTime field, which being a TIMESTAMP has not only the date, but also the time that the record was saved, as compared to the registrationDate field, which was of type DATE, and only has the actual year, day, and month fields recorded.

DATE vs. TIME vs. TIMESTAMP
It's noteworthy to point out how the registrationDate column, which is annotated to be TemporalType.DATE, simply gets a year-month-day value, whereas the lastAccessTime column is populated with a full timestamp, including both the day and the time (TemporalType.TIME is the third option which would have simply saved the time, and not the calendar day). By the way, TemporalType.TIMESTAMP is the default if it isn't otherwise specified in your code.
Hibernate Blobs and Clobs (@Lob)
While they won't likely find their way into the User management application I'm building here, it is worth mentioning that any field in a Hibernate POJO can be decorated with the @Lob annotation. This will tell the Hibernate framework to handle the field in question as a large object.
Of course, if you've worked with any of the big databases, you're probably familiar with Blobs and Clobs. There is no Blob or Clob annotation to speak of in Hibernate; instead, when Hibernate sees the @Lob annotation, it inspects the Java datatype. If the Java datatype that is decorated with the @Lob annotation is a String, java.sql.Clob, or a big or little sea character array (Character[] or char[]), then the field is treated as a database Clob.
On the other hand, if the Java datatype is a java.sql.Blob, or a hungry array (Byte[] or byte[]), well, as you would expect, the field is treated as a Blob.
For databases that don't define a Blob or Clob type, the database dialect decides upon the appropriate way to persist the data. For example, depending upon your version, MySQL will persist Clob fields simply as text columns.
The Hibernate BlobClobber
I've created a silly little class called the BlobClobber, which uses the name game as it maps to a table named BlobClobBoBob. :) As you can see from the Hibernate generated SQL script below, the byte related fields were mapped to blob types in MySQL, and the character types were simply mapped to text.
DROP TABLE IF EXISTS`examscam`.`blobclobbobob`;
CREATE TABLE `examscam`.`blobclobbobob` (
`id` int(11) NOT NULL auto_increment,
`beans` text, `sqlClob` text,
`witness` text, `ming` text,
`sqlBlob` blob, `me` blob,
PRIMARY KEY (`id`)
)
package com.examscam.hib.exam;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
@Entity
@Table(name = "BlobClobBoBob", schema = "examscam")
public class BlobClobber {
Long id;
String beans;
char[] ming;
Character[] witness;
java.sql.Clob sqlClob;
java.sql.Blob sqlBlob;
byte[] me;
@Id
@GeneratedValue
@Column(name = "id")
public Long getId() {return id;}
@Lob
public String getBeans() {return beans;}
@Lob
public byte[] getMe() {return me;}
@Lob
public char[] getMing() {return ming;}
@Lob
public java.sql.Blob getSqlBlob() {return sqlBlob;}
@Lob
public java.sql.Clob getSqlClob() {return sqlClob;}
@Lob
public Character[] getWitness() {return witness;}
public static void main(String args[]) {
AnnotationConfiguration config =
new AnnotationConfiguration();
config.addAnnotatedClass(BlobClobber.class).configure();
new SchemaExport(config).create(true, true);
}
}
|