May 07, 2017

Hibernate Tutorial & Interview Questions And Answers

What is Hibernate?
Hibernate is an ORM tool and is used in the data layer of an application. It implements JPA (Java Persistence API), which is a set of standards that have been prescribed for any persistence implementation.


What is ORM?
ORM (Object Relational Mapping) is the fundamental concept of Hibernate framework which maps database tables with Java Objects and then provides various API’s to perform different types of operations on the data tables.



Suppose you want to store user information in the database. Most of the time, you will be using a relational database. You will be creating a user table, which has the columns corresponding to variables present in your Java class. i.e a class corresponds to a table, and an object of a class corresponds to a row in the table.  We need to write SQL queries, to select, insert, update details in the user table.

We have objects in Java, but not in the database, so we need to take the values of member variables from Java object and map it in the SQL queries. If you add a new column in the table, then you need to make changes in Java classes.

What if an object has a reference to another object, then you need to create tables with foreign keys.

Also, you need to manually do the data-type conversion.

Hibernate solves the above pain points.

Suppose, you want to save the object in the database, without Hibernate, we need to do following things:
  • We need to create the database table.
  • We need to do JDBC configuration.
  • We need to write a model object, which is the object which we need to save.
  • A service method, which will create this model object.
  • A DAO class, which will generate the SQL queries to save the object.
If you are using Hibernate, then you need to:
  • You need to write a hibernate configuration file (hibernate.cfg.xml), which will give information to Hibernate to which database it needs to connect.
  • Create a model object.
  • Write a service method, which will create an instance of a model object and pass it to Hibernate API.
Hibernate Annotations
  • @Entity annotation is used to mark the class as an Entity bean. So the class should have at least have a package scope no-argument constructor. When a Hibernate finds a class with @Entity annotations (entry of that class should be present in hibernate.cfg.xml), it will recreate it as a table in your database. If you specify name attribute with @Entity annotation e.g @Entity (name="User_Details"), then Hibernate will insert/ update/ fetch the record from User_Details, instead of from the table, whose name is same as Entity class name.
  • @Id annotation is used to specify the identifier property of the entity bean and should be kept above the member variable which you want to be used as primary key. The placement of the @Id annotation determines the default access strategy that Hibernate will use for the mapping. If the @Id annotation is placed over the field, then filed access will be used. Instead, if it placed over the getter method of that field, then property access will be used. Here we use property access.
  • @GeneratedValue annotation will tell hibernate to automatically generate values using an internal sequence. Therefore we don’t have to set it manually. We can use it along with @Id and make our primary key automatically generated rather than setting each time. We can also specify a strategy on how the values will be generated like @GeneratedValue(strategy=GenerationType.AUTO). AUTO is the default if we don’t give a strategy. Other values are IDENTITY, SEQUENCE and TABLE, which are actually dependent on the database that we use. AUTO is the preferred option as hibernate will chose the best strategy for us automatically.
  • @Column annotation is used to specify the details of the column to which a field or property will be mapped. If the @Column annotation is not specified by default the property name will be used as the column name. 
  • @Table annotation is used to specify the table to persist the data. The name attribute refers to the table name. If @Table annotation is not specified then Hibernate will by default use the class name as the table name. 
  • @Basic annotation without any parameters is same as without having it. It just tells hibernate to treat it is a field that needs to be saved and is the same behavior without it. However, the use comes when it is used along with its parameters. By default, it is same as @Basic (optional=true) and the field can be not supplied any value (not calling getter) or supplied with a null value. @Basic (optional=false) makes it a non-null field.  If not supplied any value (not calling getter) or supplied with a null value with @Basic (optional=false), you will get an exception: org.hibernate.PropertyValueException: not-null property references a null or transient value.
  • @Transient annotation tells Hibernate not to save this field. Even without this annotation, a static variable or a transient variable is not saved. So the behavior is same for a static variable, transient variable or any other variable with @Transient annotation. If annotations are placed over getters, those placed over fields are ignored and only getters are considered. Even other fields are ignored and all other getters are considered. Hence if you have annotations over any of your getter, then a static variable, transient variable or any other variable will be saved as usual if it has a getter.
  • @Temporal annotation over a date field tells hibernate the format in which the date needs to be saved. For instance, by default, the date will be saved as a timestamp, but to save date alone we can use @Temporal(TemporalType.DATE).
  • @Lob annotation tells Hibernate that this is a large object, not a simple object. So Hibernate creates a CLOB or BLOB based on the type of the object. For instance, if @Lob comes over a string, then Hibernate assumes to use a character large object (CLOB). In the case of array, it will treat it as BLOB.
  • @Embeddable and @Embedded annotations are used when you want to embed one entity in another entity. The attributes of an entity can be common attributes of more than one entity. An embeddable entity can be embedded in more than one entity. 

First Hibernate Application To Save And Fetch Objects using Hibernate APIs
We will be using SQL server 2012 for writing our Hibernate application.

Hibernate uses XML mapping file for the transformation of data from POJO to database tables and vice versa. We will first we need to write a configuration file.  Here is the example of hibernate.cfg.xml for SQL server
< ?xml version='1.0' encoding='UTF-8'? >
< !DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd" >
< hibernate-configuration >
    < session-factory >
     < !-- Database connection settings -- >
        < property name="hibernate.connection.driver_class" >com.microsoft.sqlserver.jdbc.SQLServerDriver< /property >
        < property name="hibernate.connection.url" >jdbc:sqlserver://197.108.81.180:1233;databaseName=scrutinydb< /property >
        < property name="hibernate.connection.username" >khs< /property >
        < property name="hibernate.connection.password" >khs< /property >
       
        < !-- JDBC connection pool (use the built-in) -- >
        < property name="connection.pool_size" >1< /property >

        < !-- SQL dialect -- >
        < property name="dialect" >org.hibernate.dialect.SQLServer2012Dialect< /property >

        < !-- Disable the second-level cache  -- >
        < property name="cache.provider_class" >org.hibernate.cache.internal.NoCacheProvider< /property >

        < !-- Echo all executed SQL to stdout -- >
        < property name="show_sql" >true< /property >

        < !-- Drop and re-create the database schema on startup -- >
        < property name="hbm2ddl.auto" >create< /property >

        < !-- Names the annotated entity class -- >
        < mapping class="com.test.khs.UserDetails"/ >
    < /session-factory >
< /hibernate-configuration >


We need to write the Model Class with annotations and mention it in 'mapping' tag of hibernate.cfg.xml
package com.test.khs;
import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class UserDetails {
    @Id
    private int userId;
    private String userName;

    public int getUserId() {
        return userId;
    }
    public void setUserId(int userId) {
        this.userId = userId;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
}


Hibernate will create UserDetails, with two columns userId, userName and when you call save() method, the object will be saved in UserDetails table.
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import com.test.khs.UserDetails;
public class TestHibernate {
    public static void main(String[] args) {
        UserDetails user1=new UserDetails();
        user1.setUserId(1);
        user1.setUserName("Himaanshu Shuklaa");
       
        //create a session factory
        SessionFactory sessionFactory=new Configuration().configure().buildSessionFactory();
        //get the session from session factory       
        Session savesession=sessionFactory.openSession();
       
        savesession.beginTransaction();
        //to save the object in hibernate
        savesession.save(user1);
        savesession.getTransaction().commit();
        savesession.close();
       
        //fetching
        user1=null;
        Session fetchsession=sessionFactory.openSession();
        fetchsession.beginTransaction();
        //fetch based on the primary key
        user1=fetchsession.get(UserDetails.class, 1);
        System.out.println("*****fetched UserDetails:"+user1.getUserName()+", Id="+user1.getUserId());
        fetchsession.close();
    }
}


If you execute TestHibernate again, then it will delete the previous record the UserDetails table and insert the new one again. This happens because of 'hbm2ddl.auto' property in hibernate.cfg.xml, which was,
< property name="hbm2ddl.auto" > create < /property >

To retain the previous data, you need to change 'create' to 'update'.

If you set hbm2ddl.auto to update and then execute TestHibernate class again, then it will insert a new record in the table.

Suppose you have executed TestHibernate with hbm2ddl.auto as 'create' or with 'update' and then added a new variable in UserDetails class. An error 'Invalid column name' when you try to execute TestHibernate with hbm2ddl.auto as 'update'.

Hibernate creates and insert the record in the UserDetails table, where the name of the table is same as Entity class name. What if the class name is different? Or you want to insert the record in a table with the different name? To resolve this issue by specifying the name in @Entity annotation. Also, by default the property name will be used as the column name, you can change it with the name property of @Column annotation.

Example of Hibernate Annotations @Embeddable and @Embedded

This example will add Address details in the Employee table.
1).
import javax.persistence.Column;
import javax.persistence.Embeddable;

@Embeddable
public class Address {
    @Column(name = "street")
    private String street;
    @Column(name = "city")
    private String city;
   
    public String getStreet() {
        return street;
    }
    public void setStreet(String street) {
        this.street = street;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }   
}

2).
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "employee_details")
public class Employee {
    @Id
    @Column(name = "id")
    private int empId;
    @Column(name = "emp_name")
    private String empName;
    @Column(name = "emp_address")
    @AttributeOverrides({
            @AttributeOverride(name = "street", column = @Column(name = "emp_add_street")),
             @AttributeOverride(name = "city", column = @Column(name = "emp_add_city"))})
    private Address empAddress;
    public int getEmpId() {
        return empId;
    }
    public void setEmpId(int empId) {
        this.empId = empId;
    }
    public String getEmpName() {
        return empName;
    }
    public void setEmpName(String empName) {
        this.empName = empName;
    }
    public Address getEmpAddress() {
        return empAddress;
    }
    public void setEmpAddress(Address empAddress) {
        this.empAddress = empAddress;
    }   
}

3).
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class EmbedEmployeeTest {
    public static void main(String[] args) {
        Employee employee=new Employee();
        Address empAddress=new Address();
        empAddress.setCity("Kempten");
        empAddress.setStreet("Alfred-weitnauer-straße");       
        employee.setEmpId(1);
        employee.setEmpName("K Himaanshu Shuklaa");
        employee.setEmpAddress(empAddress);       
       
        //create a session factory
        SessionFactory sessionFactory=new Configuration().configure().buildSessionFactory();
        //get the session from session factory       
        Session savesession=sessionFactory.openSession();
       
        savesession.beginTransaction();
        //to save the object in hibernate
        savesession.save(employee);
        savesession.getTransaction().commit();
        savesession.close();
    }
}


When you execute EmbedEmployeeTest, it will create employee_details table with id, emp_add_city, emp_add_street, emp_name as the columns with values 1, Kempten, Alfred-weitnauer-straße, K Himaanshu Shuklaa respectively.

What is the purpose of @ElementCollection annotation in Hibernate?
@ElementCollection annotation is used to persist the collections object in Hibernate.

JPA 1.0 only supported collection holding entity types using (@ManyToOne, @OneToMany) annotations. @ElementCollection is introduced in JPA 2.0, and it can be used with any collection of type Collection, List, Set, Map.

If you do not mention @JoinTable annotation, along with @ElementCollection, then it will create a table with user name 'ENTITYCLASSNAME_COLLECTIONMEMBERVARIABLENAME' e.g Department_listOfEmployees

FetchType: @ElementCollection supports Eager and Lazy types. What does these FetchType means? Suppose, you have an Department class, each department has list of Employees who are working in that department. You have fetched Department from 'session.get()', then only the member variables of Department class is fetched and Employees list is not retrieved from the database. The Employees list is retrieved when you call the getter for that embedded collection. This is called lazy initialization fetch in hibernate.

The alternative is called eager initialization fetch, in which the embedded collections are retrieved upfront.

To select the initialization type, you need to mention FetchType.EAGER or FetchType.LAZY along with @ElementCollection annotation. e.g '@ElementCollection (fetch=FetchType.EAGER)'. If you don’t specify the fetch parameter, the default is FetchType.LAZY.

In case of Lazy loading, after retrieving embedding class instance, if we will close session and then try to print the values of the embedded collection, we will get an exception as:org.hibernate.LazyInitializationException: failed to lazily initialize a collection…

This is because when you call session.get for embedding class, only embedding class fields (except the embedded collection) is retrieved. When we try to print the embedded collection details after session.close, there is no session available to retrieve the address details lazily and hence it throws an exception.
e.g:
Session fetchsession = sessionFactory.openSession();
fetchsession.beginTransaction();
Department department = fetchsession.get(Department.class, 1);
fetchsession.close();
department.getListOfEmployees();


Example Of Saving Collections In Hibernate
In the example, we will store Department details and multiple Employees work in the Department. So we will have an Employee class, and a Department has a Set of Employees. To do this, we need to use @Elementcollection annotation.

1).
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.Table;

import org.hibernate.annotations.CollectionId;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Type;

@Entity
@Table(name = "dept_details")
public class Department {
    @Id
    @Column(name = "dept_id")
    private int deptId;
    @Column(name = "dept_name")
    private String deptName;
   
    /*
    @ElementCollection
    @JoinTable (name="deptwise_employeedtls",
    joinColumns=@JoinColumn(name="deptid_fk"))
   
    private Set < Employee > listOfEmployees=new HashSet < Employee > ();
    public Set < Employee > getListOfEmployees() {
        return listOfEmployees;
    }
    public void setListOfEmployees(Set < Employee > listOfEmployees) {
        this.listOfEmployees = listOfEmployees;
    }
    */
   
    @ElementCollection
    @JoinTable (name="deptwise_employeedtls",
    joinColumns=@JoinColumn(name="deptid_fk"))
    @GenericGenerator(name="hilo-gen", strategy="hilo")
    @CollectionId(columns={@Column(name="employe-id")},generator="hilo-gen",type=@Type(type="long"))
    //if you want to define an primary key/id in Employee table, use list instead of set
    private Collection < Employee >  listOfEmployees=new ArrayList < Employee > ();   
    public Collection < Employee >  getListOfEmployees() {
        return listOfEmployees;
    }
    public void setListOfEmployees(Collection < Employee > listOfEmployees) {
        this.listOfEmployees = listOfEmployees;
    }
    public int getDeptId() {
        return deptId;
    }
    public void setDeptId(int deptId) {
        this.deptId = deptId;
    }
    public String getDeptName() {
        return deptName;
    }
    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }       
}

2).
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

//@Entity
@Table(name = "employee_details")
@Embeddable
public class Employee {
    //@Id
    @Column(name = "emp_id")
    private int empId;
    @Column(name = "emp_name")
    private String empName;

    public int getEmpId() {
        return empId;
    }
    public void setEmpId(int empId) {
        this.empId = empId;
    }
    public String getEmpName() {
        return empName;
    }
    public void setEmpName(String empName) {
        this.empName = empName;
    }   
}

3).
import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import com.test.khs.UserDetails;

public class SaveDepartmentTest {
    public static void main(String[] args) {
        Department department=new Department();
        department.setDeptName("Import And Export");
       
        Employee employee1=new Employee();               
        employee1.setEmpId(1);
        employee1.setEmpName("K Himaanshu Shuklaa");
        Employee employee2=new Employee();               
        employee2.setEmpId(2);
        employee2.setEmpName("Tiger Shroff");
       
        /*
        Set < Employee > listOfEmployees=new HashSet < Employee > ();
        listOfEmployees.add(employee1);
        listOfEmployees.add(employee2);
        department.setListOfEmployees(listOfEmployees);
        */
       
        //create a session factory
        SessionFactory sessionFactory=new Configuration().configure().buildSessionFactory();
        //get the session from session factory       
        Session savesession=sessionFactory.openSession();
       
        savesession.beginTransaction();
        //to save the object in hibernate
        savesession.save(department);
        savesession.getTransaction().commit();
        savesession.close();
       
        // fetching
        department = null;
        Session fetchsession = sessionFactory.openSession();
        fetchsession.beginTransaction();
        // fetch based on the primary key
        department = fetchsession.get(Department.class, 1);
        System.out.println("List Of Employees: for department name "+department.getDeptName()+" is "+ department.getListOfEmployees());
        fetchsession.close();
    }
}

Association Mappings
In our applications, most of the times our database tables are associated with each other. They can be associated by one-to-one, one-to-many, many-to-one and many-to-many relationship. These can be further divided into unidirectional and bidirectional mappings. Today we will look into implementing Hibernate One to One Mapping using XML configuration as well as using annotation configuration.

Component Mappings
It is very much possible that an Entity class can have a reference to another class as a member variable. If the referred class does not have it's own life cycle and completely depends on the life cycle of the owning entity class, then the referred class hence therefore is called as the Component class.

The mapping of Collection of Components is also possible in a similar way just as the mapping of regular Collections with minor configuration differences.

Example of One-To-One relationship
In the below example, we will create a User Details entity, and we assume each user can have ONLY one vehicle.

1).
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "vehicle_details")
public class Vehicle {
    @Id @GeneratedValue
    private int vehicleId;
    private String vehicleName;

    public int getVehicleId() {
        return vehicleId;
    }
    public void setVehicleId(int vehicleId) {
        this.vehicleId = vehicleId;
    }
    public String getVehicleName() {
        return vehicleName;
    }
    public void setVehicleName(String vehicleName) {
        this.vehicleName = vehicleName;
    }
}

2).
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;

@Entity
@Table(name = "user_details")
public class UserDetails {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int userid;
    private String userName;
    @OneToOne
    @JoinColumn(name="vehicle_id_fk")
    private Vehicle vehicle;
    public int getUserid() {
        return userid;
    }
    public void setUserid(int userid) {
        this.userid = userid;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public Vehicle getVehicle() {
        return vehicle;
    }
    public void setVehicle(Vehicle vehicle) {
        this.vehicle = vehicle;
    }
}

3).
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class UserOneToOneMappingTest {
    public static void main(String[] args) {
        Vehicle vehicle=new Vehicle();
        vehicle.setVehicleName("Mercedes Benz");
        UserDetails userDetails=new UserDetails();
        userDetails.setUserName("Andrew Matthews");
        userDetails.setVehicle(vehicle);
       
        // create a session factory
        SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
        // get the session from session factory
        Session savesession = sessionFactory.openSession();
        savesession.beginTransaction();
        // to save the object in hibernate
        savesession.save(userDetails);
        savesession.save(vehicle);
        savesession.getTransaction().commit();
        savesession.close();
    }
}


When you execute UserOneToOneMappingTest class, user_details and vehicle_details are created. User details are inserted in user_details, after that vehicle details are inserted in vehicle_details, later vehicle_id_fk is updated in user_details table.

Hibernate: select next value for hibernate_sequence
Hibernate: select next value for hibernate_sequence
Hibernate: insert into user_details (userName, vehicle_id_fk, userid) values (?, ?, ?)
Hibernate: insert into vehicle_details (vehicleName, vehicleId) values (?, ?)
Hibernate: update user_details set userName=?, vehicle_id_fk=? where userid=?


If you save vehicle first and then userDetails, then vehicle details are inserted in vehicle_details and then User details are inserted in user_details, along with vehicle id (update query is not fired).

If you have not saved the user by calling save on vehicle object 'savesession.save(vehicle)', then you will get an error message:
'object references an unsaved transient instance - save the transient instance beforeQuery flushing: com.onetoone.mapping.Vehicle'

 Example of One-To-Many relationship
In the below example, we will create a User Details entity, and each user can have multiple vehicle.
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

1).
@Entity
@Table(name = "vehicle_details")
public class Vehicle {
    @Id @GeneratedValue
    private int vehicleId;
    private String vehicleName;

    //getters and setters
}
2).
import java.util.ArrayList;
import java.util.Collection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "user_details")
public class UserDetails {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int userid;
    private String userName;
    @OneToMany
    @JoinTable(joinColumns=@JoinColumn(name="user_id_fk"),
              inverseJoinColumns=@JoinColumn(name="vehicle_id_fk"))
    private Collection < Vehicle > vehicle=new ArrayList < Vehicle > ();
    //getters and setters
}

3).
public class UserOneToManyMappingTest {
    public static void main(String[] args) {
        Vehicle vehicle1=new Vehicle();
        vehicle1.setVehicleName("Mercedes Benz");
        Vehicle vehicle2=new Vehicle();
        vehicle2.setVehicleName("Lamborghini");
        UserDetails userDetails=new UserDetails();
        userDetails.setUserName("K Himaanshu Shuklaa");
        userDetails.getVehicle().add(vehicle1);
        userDetails.getVehicle().add(vehicle2);
       
        // create a session factory
        SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
        // get the session from session factory
        Session savesession = sessionFactory.openSession();
        savesession.beginTransaction();
        // to save the object in hibernate       
        savesession.save(vehicle1);
        savesession.save(vehicle2);
        savesession.save(userDetails);
        savesession.getTransaction().commit();
        savesession.close();
    }
}

When you execute UserOneToManyMappingTest class, following queries will be fired:
Hibernate: insert into vehicle_details (vehicleName, vehicleId) values (?, ?)
Hibernate: insert into vehicle_details (vehicleName, vehicleId) values (?, ?)
Hibernate: insert into user_details (userName, userid) values (?, ?)
Hibernate: insert into user_details_vehicle_details (user_id_fk, vehicle_id_fk) values (?, ?)
Hibernate: insert into user_details_vehicle_details (user_id_fk, vehicle_id_fk) values (?, ?)

No comments:

Post a Comment