Wednesday, September 27, 2017

HashCode and Equals Contract

hashCode() and equals() in Java has a certain contract which must be followed in order to have the correct behavior.

In below example, Employee as an object is being used as key . So if Employee e1 and e2 are said to be equal if there Ids are equal then we've to override hashCode() as well because two objects are equal only if there hashCode is equal.

package com.hashCodeContract;
import java.util.HashMap;
public class Employee {
     String name;
     int compId;

     public Employee(String name, int compId) {
          super();
          this.name = name;
          this.compId = compId;
     }

     public Employee() {

     }

     public String getName() {
          return name;
     }

     public void setName(String name) {
          this.name = name;
     }

     public int getCompId() {
          return compId;
     }

     public void setCompId(int compId) {
          this.compId = compId;
     }

     public static void main(String[] args) {

          Employee e1 = new Employee("Paras", 10);
          Employee e2 = new Employee("Paras", 10);

          HashMap<Employee, Integer> map = new HashMap<>();
          map.put(e1, 1);
          map.put(e2, 1);

          System.out.println("Get " + map.get(e2));
          System.out.println("Equals " + e1.equals(e2));
          System.out.println("HashCode " + e1.hashCode());
          System.out.println("HashCode " + e2.hashCode());

          System.out.println("Map size " + map.size());

          System.out.println(map.get(new Employee("Paras", 10)));
     }

     public int hashCode() {
          return 123;
     }

     public boolean equals(Object emp) {
          return (this.name.equals(((Employee) emp).getName()));
     }
}

Output
Get 1
Equals()   true
hashCode() 123
hashCode() 123
Map size   1

1

Methods in Hibernate

Session interface important methods get(),load(),evict(),update() and merge()

public class HibernateGetVsLoad {
     public static void main(String[] args) {

          SessionFactory sessionFactory =
     new Configuration().configure().buildSessionFactory();
          Session session = sessionFactory.openSession();

          UserDetailsDTO user1= (UserDetailsDTO) session.get(UserDetailsDTO.class, 1);
          System.out.println(user1.getUserName());
          System.out.println(user1.getAddress());
          System.out.println(user1.getDescription());
         
    
          UserDetailsDTO user2= (UserDetailsDTO) session.get(UserDetailsDTO.class, 1);
          System.out.println(user2.getUserName());
          System.out.println(user2.getAddress());
          System.out.println(user2.getDescription());
         
     }
}

Existing Database

select * from USER_DETAILS_NEW



Scenario 1: When Hibernate is using first level caching.

Output
Hibernate: select userdetail0_.USER_ID as USER_ID1_0_0_, userdetail0_.Address as Address2_0_0_, userdetail0_.createdOn as createdO3_0_0_, userdetail0_.description as descript4_0_0_, userdetail0_.USER_NAME as USER_NAM5_0_0_ from USER_DETAILS_NEW userdetail0_ where userdetail0_.USER_ID=?
Paras
722/18 Shastri Nagar , Rohtak
Hey ,Hibernate is Amazing
Paras
722/18 Shastri Nagar , Rohtak
Hey ,Hibernate is Amazing

Scenario 2: calling evict() and removing user1 object from cache

public class HibernateGetVsLoad {
     public static void main(String[] args) {

          SessionFactory sessionFactory =
     new Configuration().configure().buildSessionFactory();
          Session session = sessionFactory.openSession();

          UserDetailsDTO user1= (UserDetailsDTO) session.get(UserDetailsDTO.class, 1);
          System.out.println(user1.getUserName());
          System.out.println(user1.getAddress());
          System.out.println(user1.getDescription());
         
          session.evict(user1);
          //session.clear();
          //session.flush();
         
          UserDetailsDTO user2= (UserDetailsDTO) session.get(UserDetailsDTO.class, 1);
          System.out.println(user2.getUserName());
          System.out.println(user2.getAddress());
          System.out.println(user2.getDescription());
         
     }
}

Output

Hibernate: select userdetail0_.USER_ID as USER_ID1_0_0_, userdetail0_.Address as Address2_0_0_, userdetail0_.createdOn as createdO3_0_0_, userdetail0_.description as descript4_0_0_, userdetail0_.USER_NAME as USER_NAM5_0_0_ from USER_DETAILS_NEW userdetail0_ where userdetail0_.USER_ID=?
Paras
722/18 Shastri Nagar , Rohtak
Hey ,Hibernate is Amazing

Hit Again to Database

Hibernate: select userdetail0_.USER_ID as USER_ID1_0_0_, userdetail0_.Address as Address2_0_0_, userdetail0_.createdOn as createdO3_0_0_, userdetail0_.description as descript4_0_0_, userdetail0_.USER_NAME as USER_NAM5_0_0_ from USER_DETAILS_NEW userdetail0_ where userdetail0_.USER_ID=?
Paras
722/18 Shastri Nagar , Rohtak
Hey ,Hibernate is Amazing

Scenario 3 calling session.clear(); clears everything within a session

Hibernate: select userdetail0_.USER_ID as USER_ID1_0_0_, userdetail0_.Address as Address2_0_0_, userdetail0_.createdOn as createdO3_0_0_, userdetail0_.description as descript4_0_0_, userdetail0_.USER_NAME as USER_NAM5_0_0_ from USER_DETAILS_NEW userdetail0_ where userdetail0_.USER_ID=?
Paras
722/18 Shastri Nagar , Rohtak
Hey ,Hibernate is Amazing

Hit Again to Database

Hibernate: select userdetail0_.USER_ID as USER_ID1_0_0_, userdetail0_.Address as Address2_0_0_, userdetail0_.createdOn as createdO3_0_0_, userdetail0_.description as descript4_0_0_, userdetail0_.USER_NAME as USER_NAM5_0_0_ from USER_DETAILS_NEW userdetail0_ where userdetail0_.USER_ID=?
Paras
722/18 Shastri Nagar , Rohtak
Hey ,Hibernate is Amazing

Scenario 4When Id doesn’t exist and we’re calling session.get()

UserDetailsDTO user1= (UserDetailsDTO) session.get(UserDetailsDTO.class, 1);                                   
UserDetailsDTO user2= (UserDetailsDTO) session.get(UserDetailsDTO.class, 3);
Output
Hibernate: select userdetail0_.USER_ID as USER_ID1_0_0_, userdetail0_.Address as Address2_0_0_, userdetail0_.createdOn as createdO3_0_0_, userdetail0_.description as descript4_0_0_, userdetail0_.USER_NAME as USER_NAM5_0_0_ from USER_DETAILS_NEW userdetail0_ where userdetail0_.USER_ID=?
Paras
722/18 Shastri Nagar , Rohtak
Hey ,Hibernate is Amazing
Hibernate: select userdetail0_.USER_ID as USER_ID1_0_0_, userdetail0_.Address as Address2_0_0_, userdetail0_.createdOn as createdO3_0_0_, userdetail0_.description as descript4_0_0_, userdetail0_.USER_NAME as USER_NAM5_0_0_ from USER_DETAILS_NEW userdetail0_ where userdetail0_.USER_ID=?
Exception in thread "main" java.lang.NullPointerException
 at com.hibernateInterviewQuestions.HibernateGetVsLoad.main(HibernateGetVsLoad.java:23)

Scenario 5: get() and load() when entry doesn’t exist

public class HibernateGetVsLoad {
     public static void main(String[] args) {

          SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
          Session session = sessionFactory.openSession();
          try {
              UserDetailsDTO user1 = (UserDetailsDTO) session.get(UserDetailsDTO.class, 3);
              System.out.println("Using get() "+user1);
          } catch (Exception e) {
              System.out.println("Exception thrown " +e);
          }

          try {
              UserDetailsDTO user2 = (UserDetailsDTO) session.load(UserDetailsDTO.class, 3);
              System.out.println("Using load() "+user2);
          } catch (Exception e) {
              System.out.println("Exception thrown " +e);
          }

     }
}

Output
Hibernate: select userdetail0_.USER_ID as USER_ID1_0_0_, userdetail0_.Address as Address2_0_0_, userdetail0_.createdOn as createdO3_0_0_, userdetail0_.description as descript4_0_0_, userdetail0_.USER_NAME as USER_NAM5_0_0_ from USER_DETAILS_NEW userdetail0_ where userdetail0_.USER_ID=?

Using get() null

Hibernate: select userdetail0_.USER_ID as USER_ID1_0_0_, userdetail0_.Address as Address2_0_0_, userdetail0_.createdOn as createdO3_0_0_, userdetail0_.description as descript4_0_0_, userdetail0_.USER_NAME as USER_NAM5_0_0_ from USER_DETAILS_NEW userdetail0_ where userdetail0_.USER_ID=?

Exception thrown org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.hibernateInterviewQuestions.UserDetailsDTO#3]

Difference between get() and load()

1. Behavior when Object is not found in Session Cache
Apart from performance this is another difference between get and load which is worth remembering. get method of Hibernate Session class returns null if object is not found in cache as well as on database while load() method throws ObjectNotFoundException if object is not found on cache as well as on database but never return null.

2. Database hit
Get method always hit database while load() method may not always hit the database, depending upon which method is called.

3. Proxy
Get method never returns a proxy, it either returns null or fully initialized Object, while load() method may return proxy, which is the object with ID but without initializing other properties, which is lazily initialized. If you are just using returned object for creating relationship and only need Id then load() is the way to go.

4. Performance
By far most important difference between get and load in my opinion. get method will return a completely initialized object if  Object is not on the cache but exists on Database, which may involve multiple round-trips to database based upon object relational mappings while load() method of Hibernate can return a proxy which can be initialized on demand (lazy initialization) when a non identifier method is accessed. Due to above reason use of load method will result in slightly better performance, but there is a caveat that proxy object will throw ObjectNotFoundException later if corresponding row doesn’t exists in database, instead of failing immediately so not a fail fast behavior.

5. load method exists prior to get method which is added on user request.