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.
- 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
- 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