Version 31

    Overview of Hibernate Walk Thru

     

    This guide is designed to provide a concise Hibernate 3 tour. We will start with a database schema and work our way back to working Hibernate code. This is not intended to be an indepth guide, just and introduction. The Hibernate Reference Manual provides the detail needed to leverage more advanced features: http://hibernate.org/5.html. I used MySQL for this walk-thru, but other major databases should work as well because Hibernate is database agnostic.

     

    Table Definitions

     

    Basically, we're going to start with a subset of tables from Hibernate's CaveatEmptor sample application which can be found at: http://caveatemptor.hibernate.org/. The Hibernate in Action book, http://www.hibernate.org/beamto.php?action=beam&beam_id=2, goes into more detail on how to fully leverage Hibernate, but we're just interested in a few basic mappings. Here's the data model that we're going to use:

     

     

    And, here's the DDL:

     

    
    create database caveatemptor;
    
    create table caveatemptor.ITEM (
       ITEM_ID integer not null auto_increment,
       VERSION integer not null,
       NAME varchar(255) not null,
       DESCRIPTION varchar(255) not null,
       CREATED datetime not null,
       USER_ID integer,
       BID_ID integer,
       primary key (ITEM_ID)
    );
    
    create table caveatemptor.USER (
       USER_ID integer not null auto_increment,
       VERSION integer not null,
       FIRSTNAME varchar(255) not null,
       LASTNAME varchar(255) not null,
       USERNAME varchar(16) not null unique,
       `PASSWORD` varchar(12) not null,
       EMAIL varchar(255) not null,
       primary key (USER_ID)
    );
    
    create table caveatemptor.BID (
       BID_ID integer not null auto_increment,
       AMOUNT numeric(19, 2) not null,
       AMOUNT_CURRENCY varchar(255) not null,
       CREATED datetime not null,
       ITEM_ID integer not null,
       USER_ID integer not null,
       primary key (BID_ID)
    );
    alter table caveatemptor.ITEM add index FK1_USER_ID (USER_ID), 
    add constraint FK1_USER_ID foreign key (USER_ID) references caveatemptor.USER (USER_ID);
    alter table caveatemptor.BID add index FK2_USER_ID (USER_ID), 
    add constraint FK2_USER_ID foreign key (USER_ID) references caveatemptor.USER (USER_ID);
    alter table caveatemptor.BID add index FK1_ITEM_ID (ITEM_ID), 
    add constraint FK1_ITEM_ID foreign key (ITEM_ID) references caveatemptor.ITEM (ITEM_ID);
    
    

     

     

    As you can see, we've got a few constraints and one-many relationship between

    • USER and ITEM

    • USER and BID

     

    Also, we have an implicit one-many relationship between

    • ITEM and BID

     

    The ITEM table doesn't have an explicit constraint because that's the way the DBA designed it.

     

    Key Hibernate Configuration Tidbits

     

    Most of what needs to be configured at runtime is handled in hibernate.cfg.xml. The file for the walk through looks like this:

     

    <?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>
            <!-- This is the database connection information -->
            <property name="hibernate.connection.driver_class">org.gjt.mm.mysql.Driver</property>
            <property name="hibernate.connection.password">password</property>
            <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/caveatemptor</property>
            <property name="hibernate.connection.username">root</property>
            <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
           
            <!-- Flag that outputs SQL so you can see the SQL that's generated -->
         <property name="show_sql">true</property>
        
            <!-- Use the Hibernate built-in pool for tests. -->
         <property name="connection.pool_size">1</property>
    
         <!-- You setup an in-memory cache to reduce database load. -->
         <property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
         <property name="cache.use_query_cache">false</property>
         <property name="cache.use_minimal_puts">false</property>     
        
            <!-- These mappings bind the Java classes to the database tables. -->
         <mapping resource="org/jboss/hibernate/demo/model/User.hbm.xml"></mapping>
         <mapping resource="org/jboss/hibernate/demo/model/Item.hbm.xml"></mapping>
         <mapping resource="org/jboss/hibernate/demo/model/Bid.hbm.xml"></mapping>     
         
     </session-factory>     
    </hibernate-configuration>
    

     

    You can also make the hibernate output even more granular by tweaking the log4j.properties entries for application level logging.

     

    #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}: %m%n
    
    #Set log levels - for more verbose logging change 'info' to 'debug
    log4j.rootLogger=warn, stdout
    
    #CaveatEmptor
    log4j.logger.org.jboss.hibernate=warn
    
    #Hibernate Core
    log4j.logger.org.hibernate=info
    
    #Log cache activity
    log4j.logger.org.hibernate.cache=warn
    
    #Log JDBC bind parameters
    log4j.logger.org.hibernate.type=warn
    

     

    Java Table Objects And XML Table Mappings

     

    Each database table typically has a corresponding Java class. In this sample, we have 3 different Java classes that map directly to a database table:

     

    • org.jboss.hibernate.demo.model.User maps to User.hbm.xml which maps to caveatemptor.User

    • org.jboss.hibernate.demo.model.Item maps to Item.hbm.xml which maps tocaveatemptor.Item

    • org.jboss.hibernate.demo.model.Bid maps to Bid.hbm.xml which maps to caveatemptor.Bid

     

     

    Here's the XML file for the User table. It's included to demonstrate the glue between the database and the Java classes. This file contains the one-many mappings for the Item and Bid table as well as all of the User table column definitions. And, at the bottom of the file there are 2 HQL queries that are SQL like, but simpler and more object oriented. Again, this HQL query language is developer friendly. Think of it a lightweight SQL for Java guys that keeps them from writing bad SQL:

     

    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
    <!-- Generated Apr 26, 2006 3:10:51 PM by Hibernate Tools 3.1.0 beta3 -->
    <hibernate-mapping package="org.jboss.hibernate.demo.model">
        <class name="User" table="user">
            <id name="userId" type="int">
                <column name="USER_ID" ></column>
                <generator class="assigned" ></generator>
            </id>
            <version name="version" type="int">
                <column name="VERSION" not-null="true" ></column>
            </version>
            <property name="firstname" type="string">
                <column name="FIRSTNAME" not-null="true" ></column>
            </property>
            <property name="lastname" type="string">
                <column name="LASTNAME" not-null="true" ></column>
            </property>
            <property name="username" type="string">
                <column name="USERNAME" length="16" not-null="true" ></column>
            </property>
            <property name="password" type="string">
                <column name="PASSWORD" length="12" not-null="true" ></column>
            </property>
            <property name="email" type="string">
                <column name="EMAIL" not-null="true" ></column>
            </property>
            
            <!-- Mapping for Item association. -->
            <set name="items"
                   cascade="delete"
                   lazy="true"
                   inverse="true">
              <key>
                   <column name="USER_ID" not-null="true"></column>
              </key>
              <one-to-many class="Item"></one-to-many>
           </set>
           
            <!-- Mapping for Bid association. -->
            <set name="bids"
                   cascade="delete"
                   lazy="true"
                   inverse="true">
              <key>
                   <column name="USER_ID" not-null="true"></column>
              </key>
              <one-to-many class="Bid"></one-to-many>
           </set>
        </class>
        
        <query name="deleteUser"><![CDATA[
              delete User u where u.username = :username
        \]\]\></query>   
        
        <query name="selectUserBids"><![CDATA[
              from User u inner join fetch u.bids where u.username = :username order by u.bids.amount
        \]\]\></query>       
        
        
    </hibernate-mapping>
    
    

     

    Here's the User Java class. It's only included to demonstrate the one to one mapping between the table and the Java code and to let you know that Hibernate will actually handle the mapping of one-many data for the programmer. This is handy when inner join type logic is needed. Basically, Hibernate makes this type of query incredibly simple and straightforward, which is why so many programmers love the framework.

     

    package org.jboss.hibernate.demo.model;
    import java.util.HashSet;
    import java.util.Set;
    
    public class User  implements java.io.Serializable {
         
         // Fields    
         private int userId;
         private int version;
         private String firstname;
         private String lastname;
         private String username;
         private String password;
         private String email;
         private Set bids;
         private Set items;
    
        /** default constructor */
        public User() {
        }
        
        /** full constructor */
        public User(int userId, String firstname, String lastname, String username, String password, String email) {
            this.userId = userId;
            this.firstname = firstname;
            this.lastname = lastname;
            this.username = username;
            this.password = password;
            this.email = email;
        }
       
        // Property accessors
        public int getUserId() {
            return this.userId;
        }
        
        public void setUserId(int userId) {
            this.userId = userId;
        }
    
        public int getVersion() {
            return this.version;
        }
        
        public void setVersion(int version) {
            this.version = version;
        }
    
        public String getFirstname() {
            return this.firstname;
        }
        
        public void setFirstname(String firstname) {
            this.firstname = firstname;
        }
    
        public String getLastname() {
            return this.lastname;
        }
        
        public void setLastname(String lastname) {
            this.lastname = lastname;
        }
    
        public String getUsername() {
            return this.username;
        }
        
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return this.password;
        }
        
        public void setPassword(String password) {
            this.password = password;
        }
    
        public String getEmail() {
            return this.email;
        }
        
        public void setEmail(String email) {
            this.email = email;
        }
        
        //Constructors
        public Set getBids() {
         return bids;
        }
    
        public void setBids(Set bids) {
         this.bids = bids;
        }
    
        public Set getItems() {
         return items;
        }
    
        public void setItems(Set items) {
         this.items = items;
        }
         
        public void addItem(Item item){
         item.setUser(this);
         if (items == null)
         {
             items = new HashSet();               
         }
         items.add(item);
        }
    }
    

     

    The last piece of this puzzle is the actual database logic code. In our sample application, the code is very concise. Basically, we've eliminated almost all of the nasty plumbing JDBC code that contributes to nasty programming bugs and longer development cycles. Also, the Hibernate generated JDBC often performs better than hand coded JDBC. Here's a Java method for User table inserts/updates:

     

    
    public void insertOrUpdateUser(User user) throws InfrastructureException {
            HibernateUtil.beginTransaction();
         Session s = HibernateUtil.getSession();
    
         s.merge(user);
    
         HibernateUtil.commitTransaction();
         HibernateUtil.closeSession();
    }
    
    

     

    Pretty nice, but that's not all. Here's a find method that will dynamically generate the necessary WHERE clauses for any combination of attributes that are set in the Java Class:

     

    
    public Collection findByExample(User exampleUser)throws InfrastructureException {
    
            Collection users;
    
         Criteria crit = HibernateUtil.getSession().createCriteria(User.class);
         users = crit.add(Example.create(exampleUser)).list();
    
         return users;
    }
    

     

    How about DELETE:

     

    public void deleteUser(User user) throws InfrastructureException {
         HibernateUtil.beginTransaction();
              
         Query q = HibernateUtil.getSession().getNamedQuery("deleteUser");
         q.setString("username", user.getUsername());
         q.executeUpdate();
    
         HibernateUtil.commitTransaction();
         HibernateUtil.closeSession();
    }
    

     

    Last but not least, how about overriding the Hibernate generated SQL with a DBA written query:

     

    XML Mapping and actual SQL query

    
    <sql-query name="getAllItems">
        <return alias="item" class="Item"></return>
        SELECT * from ITEM order by CREATED
    </sql-query>
    
    

     

    Java Class Query that Limits results

    
    public List getAllItems() {
         HibernateUtil.beginTransaction();
         Session s = HibernateUtil.getSession();
              
         List items = s.getNamedQuery("getAllItems")
                         .setMaxResults(20)
                      .list();
    
         HibernateUtil.commitTransaction();
         HibernateUtil.closeSession();
    
         return items;
    }
    
    

     

    Conclusion

     

    Hopefully this quick walk-thru provided enough insight into Hibernate to spark additional interest. A good next step is downloading the code used for this walk-thru, which is attached as caveatemptor-slim.zip. The project can be easily imported into eclipse and the Java classes in org.jboss.hibernate.demo.test can be run as JUnit test cases. All hibernate binaries are included, and the only manual steps involve:

     

    • Creating the database - sample script inlcuded

    • Setup a database userid/password

    • Add the JDBC driver of your database to the /lib directory and build path

     

    If you're not eclipse or JUnit savvy, no worries. I will be provided a follow-up wiki with screenshots.

     

    I also highly recommend http://hibernate.org, and the Hibernate in Action book.