08 - Hibernate Advance Mapping

8.1 Overview

We have seen mapping document details in chapter 7 but there are some additional attributes available which are equally important to understand. In this chapter, we will discuss those advanced mapping attributes.

8.2  Dynamic SQL Generation

Hibernate does generate all SQL statements (delete, select, insert, update) for all the persistent objects at the time of application startup. It is very important to understand the concept of Dynamic SQL generation from a performance stands point.

By default, hibernate creates an insert statement for all the columns and sends the null value if the application is not inserting. Similarly, update statement contains all the columns and hibernate sends the unmodified values in case value is not changed.  Memory consumption gets too high with this approach if  the tables are having several columns and when most of them are not required in update and insert statements.

To turn off this feature we can use dynamic-insert="true" for insert statements and

dynamic-update="true" for update statement attributes of a <class> tag.

The default value of both these attributes is false.

Let's take an example of Student POJO class to understand hibernate mapping file. 

Student.java 

package com.hibernate.tutotial 
public class Student {
    
    private int id;    
    private String name;    
    private String emailAddress;
 
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getEmailAddress() {
        return emailAddress;
    }
    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }
}

Corresponding to Student.java lets define STUDENT_INFO table 

create table STUDENT_INFO (
   id INT NOT NULL auto_increment,
   name VARCHAR(20) default NULL,
   email VARCHAR(20) default NULL,
   PRIMARY KEY (id)
);

Mapping Document ( hbm.xml )

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"  "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 

<hibernate-mapping>
   <class name="com.hibernate.tutorial.Student" table="STUDENT_INFO">
      <id name="id" type="int" column="id">
         <generator class="native"/>
      </id>
      <property name="name" column="name" type="string"/>
        <property name="emailAddress" column="email" type="string"/> 
   </class>
</hibernate-mapping>

Write a TestStudent.java program as below 

package com.tutorial.hibernate;
import java.util.Properties;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class TestStudent {

     public static void main(String a[])
    {

       Properties prop = new Properties();        
       prop.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
       prop.setProperty("hibernate.connection.driver_class", "com.mysql.jdbc.Driver");
       prop.setProperty("hibernate.connection.url", "jdbc:mysql://localhost:3306/tutorial");
       prop.setProperty("hibernate.connection.username", "root");
       prop.setProperty("hibernate.connection.password", "password");
       prop.setProperty("hibernate.hbm2ddl.auto", "create");
       prop.setProperty("hibernate.show_sql", "true");

       Configuration cfg = new Configuration();        
       cfg.addResource("student.hbm.xml");
       cfg.setProperties(prop);        
       Student s = new Student();        
       s.setName("student1");

       SessionFactory factory = cfg.buildSessionFactory();

       Session session = factory.openSession();
       session.save(s);
       s.setName("student 2");

       session.flush();
       factory.close();
    }
}

In the above test program, we are just setting the value of name and not of email address. Even though hibernate uses both columns in insert and update statemtents.

Now change the class tag of student.hbm.xml file to set dynamic-insert and dynamic-update as true 

    <class name="com.tutorial.hibernate.Student" table="student" dynamic-insert="true" dynamic-update="true">

This time only name value is used in the query 

8.3 Making  an Entity Immutable

Immutable means a state of an object cannot be changed. In database terms, we can relate it as a row, which cannot be updated. If we do not want to update the state of a persistent class we can mark the class as immutable in the mapping document. 

If the class is marked as immutable and state has been changed,   hibernate will not execute the update statement and will just ignore it ( No exception will be thrown) .

Note: This  feature is applicable only for Update statement

To achieve this behavior we can set the mutable attribute of the class tag to false. The default value is true.

If we add mutable attribute as false and re-run the Test program created in Section 8.2, update statement will not be triggered by hibernate 

<class name="com.tutorial.hibernate.Student" table="student" dynamic-insert="true" dynamic-update="true" mutable=”false”>

8.4 Controlling Insertion and Updates at property level

We saw how to make the complete object immutable in section 8.3  but we can control the insertion and deletion at a property level also. For example, we have two properties name and emailAddress in our Student object so is we want only name to be inserted and updated even though email is provided, we can achieve this behavior using insert and update attributes at a property level.

Update TestStudent program to set email address as well.

import java.util.Properties;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class TestStudent {
  
    public static void main(String a[])
    {
        Properties prop = new Properties();        
        prop.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
        prop.setProperty("hibernate.connection.driver_class", "com.mysql.jdbc.Driver");
        prop.setProperty("hibernate.connection.url", "jdbc:mysql://localhost:3306/tutorial");
        prop.setProperty("hibernate.connection.username", "root");
        prop.setProperty("hibernate.connection.password", "password");
        prop.setProperty("hibernate.hbm2ddl.auto", "create");
        prop.setProperty("hibernate.show_sql", "true");

        Configuration cfg = new Configuration();        
        cfg.addResource("student.hbm.xml");
        cfg.setProperties(prop);        
        Student s = new Student();        
        s.setName("student1");
        s.setEmailAddress("a@b.com");
 
        SessionFactory factory = cfg.buildSessionFactory();

        Session session = factory.openSession();
        session.save(s);
        s.setName("student 2");
        s.setEmailAddress("c@d.com");
        session.flush();
        factory.close();

        }
}

Run the program. You will see both email address and name are sent to insert and update query .

Now add the insert =”false” and update=’false” and re-run the program . You will see only email address sent to insert and update query.

Let's modify the hbm file to add insert and update attribute to set it to false. Default values are true.

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
 "-//Hibernate/Hibernate Mapping DTD//EN"
 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 

<hibernate-mapping>
   <class name="com.tutorial.hibernate.Student" table="student" dynamic-insert="true" dynamic-update="true" >
      <id name="id" type="int" column="id">
         <generator class="native"/>
      </id>
      <property name="name" column="name" type="string"/>
        <property name="emailAddress" column="email" type="string" insert="false" update="false"/> 
   </class>
</hibernate-mapping>

8.5  Property Access Strategies

Hibernate allows us to specify the property access strategy. There are two ways with which a hibernate can access the properties.

  1. Get/Set methods – this approach requires getter / setter methods corresponding to the field and we have used this approach till now. This is the recommended approach
  2. Field- with this approach , we do not need to create a get / set methods and hibernate uses reflection to access the fields directly. To do so, we can add access attribute as field in property tag

    <property name="emailAddress" column="email" type="string"  access="field" />

8.6 Turning Off Package import

By default hibernate automatically imports all the package names which allow us to use only the class name instead of complete package in HQLs.  We would want to turn off this feature if we have the same class name in different packages.

Note this feature is applicable for HQL and not for mapping document. In mapping document we need to add fully qualified class names.

To do so add autoimport="false" attribute in <hibernate-mapping> root element.

Alternatively we can assign a new name to a class and can use the alias in HQL

    

      <hibernate-mapping>

          <import class="com.tutorial.hibernate.Student" rename="IStudent"/>

     </hibernate-mapping>

Now in HQL we can simply use IStudent in place of com.tutorial.hibernate.Student

8.7 Declare Package Name Globally

If mapping document contains multiple class definitions and all the classes are in same package then we can package attribute of hibernate-mapping root element and then can simply use class name in class tags .

For example we can redefine the student.hbm.xml as

<hibernate-mapping package="com.tutorial.hibernate"> 

<class name="Student" table="student" dynamic-insert="true" dynamic-  update="true" >
      <id name="id" type="int" column="id">
        <generator class="native"/>
      </id>
      <property name="name" column="name" type="string"/>
      <property name="emailAddress" column="email" type="string" /> 
   </class>
</hibernate-mapping>

8.8 Quoting SQL Identifiers

By default Hibernate does not quote the column names in the generated SQL. However, we want to have the column names quoted , we can use backticks “`” in the column name of mapping attribute. This feature is important for the scenarios where we have used the database reserved keywords.

Refer below example to Quote email identifier. With this in all the Hibernate generated SQLs , email will be quoted 

       <property name="emailAddress" column="`email`" type="string" />

 

8.9  Implement Naming Strategy

In chapter 4 , we touched upon Naming Strategy overview . Let's implement Custom Naming Strategy to understand it practically.

Custom Naming Strategy needs to extend ImprovedNamingStrategy class which provides default implementation  and provide the implementation.

Most important methods of ImprovedNamingStrategy  which can be overridden  are –

  • classToTableName() - is called only if a <class>  tag  does not  specify table  attribute.
  • propertyToColumnName() – is called only if a property has no  column name.
  • tableName() and columnName()  - are called when the table and column attributes are provided  explicitally.

We will implement a strategy to prefix the table names with the word “Hibernate_” 

package com.tutorial.hibernate;
import org.hibernate.cfg.ImprovedNamingStrategy;

public class CustomNaming extends ImprovedNamingStrategy {
     public String tableName(String tableName) {
        return "Hibernate_" + tableName;
    }    
}

Naming Strategy can be configured in Configuration Object using setNamingStrategy() method like below

    Configuration cfg = new Configuration();                          

    cfg.setNamingStrategy(new CustomNaming());

    Now tables used will be Hibernate_student              

Like us on Facebook