While doing code-review I came across hibernate’s dynamic-insert property and found it really useful. dynamic-insert is a boolean attribute of class element as defined in hibernate-mapping-3.0.dtd. This is an optional attribute of the class element and if not defined explicitly defaults to false.
As the name says, setting this property to true makes insert query generated by hibernate dynamic i.e. it will include only non-null values of DTO in the prepared statement.
Let’s explore this with the help of an example, for which I’m using PostgreSQL 9 and Hibernate 5.1.0.
First step is to create a table called person, which has a mix of NULL and NOT NULL columns.
[code lang=”sql”]
CREATE TABLE person {
id serial NOT NULL,
age integer,
firstname character(50) NOT NULL,
lastname character(50) NOT NULL,
CONSTRAINT person_id PRIMARY KEY (id)
)
[/code]
Step 2 is to create the hibernate mapping configuration file, Person.hbm.xml
NOTE: person_id_seq is the sequence generator for the table’s primary key.
[code lang=”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="dto.Person" table="person" dynamic-insert="true" schema="public">
<meta attribute="class-description">
This class contains the Person detail.
</meta>
<id name="id" type="int" column="id">
<generator class="org.hibernate.id.enhanced.SequenceStyleGenerator">
<param name="sequence_name">person_id_seq</param>
</generator>
</id>
<property name="firstName" column="firstname" type="string" />
<property name="lastName" column="lastname" type="string"/>
<property name="age" column="age" type="java.lang.Integer"/>
</class>
</hibernate-mapping>
[/code]
Next step is to create Person.java; the DTO class for table person.
[code lang=”java”]
public class Person {
private int id;
private Integer age;
private String firstName;
private String lastName;
public Person(){
//Empty
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
[/code]
Here I’m directly using the main method for managing session, transaction and persisting the object to person table.
[code lang=”java”]
public class ApplicationRunner {
private static final SessionFactory ourSessionFactory;
static {
try {
ourSessionFactory = new Configuration().configure("hibernate.cfg.xml").buildSessionFactory();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static Session getSession() throws HibernateException {
return ourSessionFactory.openSession();
}
public static void main(final String[] args) throws Exception {
final Session session = getSession();
try {
System.out.println("querying all the managed entities…");
Transaction tx = session.beginTransaction();
Person person = new Person();
person.setFirstName("Shanti");
person.setLastName("Muchhala");
session.save(person);
tx.commit();
} finally {
session.close();
}
}
}
[/code]
As you can, age has not been set in the person object and so will be null as it’s an object of Integer wrapper.
When main method is executed we get the following output
querying all the managed entities... Hibernate: select nextval ('person_id_seq') Hibernate: insert into person (firstname, lastname, id) values (?, ?, ?)
Since the age object was null in the dto, the insert query generated by hibernate didn’t even consider the age column.
Remember, while creating the person DTO object I have used wrapper Integer and not primitive int, which will result in 0 being set instead of null since primitive types cannot hold null and is one of the most common cause of bugs where 0 is being set instead of a null value.
Let’s change the datatype of age from Integer to int along with getter and setter and re run the main class.
You will see hibernate is inserting age also.
querying all the managed entities... Hibernate: select nextval ('person_id_seq') Hibernate: insert into person (firstname, lastname, age, id) values (?, ?, ?, ?)
On checking the database table, you will see zero is inserted as age by hibernate. As stated above the default value for int is 0 and not null and thus hibernate finds a value and ends up building a query that will insert age as 0.