Basic concepts of Hibernate

Hibernate vs JDBC:
1) Hibernate is data base independent, your code will work for all ORACLE,MySQL, SQLServer etc.
It is achieved by using a dialect. The database can be specified using dialect in the Hibernate configuration. 
In case of JDBC query must be data base specific.

2) We don't need Query tuning in case of Hibernate. If we use Criteria Queries in Hibernate then hibernate automatically tunes our query and returns best result with performance. In case of JDBC you need to tune your queries.

3) Hibernate supports cache which enhances performance

4) Less code for database connectivity than JDBC.


Hibernate - Caching:
Caching is all about application performance, optimization and it sits between your application and the database to avoid the number of database hits to give a better performance.


First-level cache:(uses by default)
1)The first-level cache is the Session cache and is a mandatory cache through which all requests must pass.
2)First level cache is enabled by default and you can not disable it.
3)The scope of cache objects is of session. Once session is closed, cached objects are gone forever
4)When we query an entity first time, it is retrieved from database and stored in first level cache associated with hibernate session.
-If we query same object again with same session object, it will be loaded from cache and no sql query will be executed. Hibernate first level cache is session specific. Hibernate first level cache can have old values that’s why we get the same data in same session  whereas in another session when same query is fired we get updated data. Fetching from cache is done using load() method
eg:
Stock stock = (Stock)session.load(Stock.class, new Integer(2));
-The loaded entity can be removed from session using evict() method. The next loading of this entity will again make a database call if it has been removed using evict() method.
-The whole session cache can be removed using clear() method. It will remove all the entities stored in cache.
evict() vs clear():
evict() evicts a single object from the session. clear() evicts all the objects in the session

eg:
1)Airline airline = (Airline) session.load(Airline.class,new Integer(1));
2)session.evict(airline);
3)session.clear() ;

5)If you issue multiple updates to an object, Hibernate tries to delay doing the update as long as possible to reduce the number of update SQL statements issued. If you close the session, all the objects being cached are lost and either persisted or updated in the database.
6)The first level cache data will not be available to entire application. An application can use many session object.


Second-level cache:
1)SessionFactory object holds the second level cache data.
2)Second level cache is an optional cache and first-level cache will always be consulted before any attempt is made to locate an object in the second-level cache.
3) The second-level cache can be configured on a per-class and per-collection basis and mainly responsible for caching objects across sessions.
4)The data stored in the second level cache will be available to entire application. But we need to enable it explicitly.

Second Level Cache implementations are provided by different vendors such as:EH (Easy Hibernate) Cache
Swarm Cache
OS Cache
JBoss Cache


Session vs sessionFactory
SessionFactory :
1)SessionFactory creates Session instances.
2)SessionFactory is an interface, which is available in “org.hibernate” package.
3)Usually one session factory should be created for one database.
4)Session factory is multithreaded object.
 When you have multiple databases in your application you should create multiple SessionFactory object.
5)It holds second level cache (optional) of data.

-Is SessionFactory a thread-safe object?
Yes, SessionFactory is a thread-safe object, many threads can access it simultaneously.


-How to configure multiple database?
1)You need to provide a cfg.xml for each database.
 Oracle.cfg.xml
 mssql.cfg.xml

2)Within in code you create a single SessionFactory for each database.
SessionFactory oracleSF = Configuration.configure("oracle.cfg.xml").buildSessionFfactory();

SessionFactory msSF = Configuration.configure("mssql.cfg.xml").buildSessionFfactory();


Session:
The session object provides an interface between the application and data stored in the database.
It also provides factory methods for Transaction(Insert, Update,Delete), Query and Criteria. It holds a first-level cache (mandatory) of data.

-Is Session a thread-safe object?
No, Session is not a thread-safe object, many threads can't access it simultaneously.


get() Vs load()
1) get() will always hit the database while load() hits database only when its fields(other than id) are accessed. load() will always return a “proxy”. Proxy is an object with the given identifier value, its properties are not initialized yet, it is just a fake object.

Explanation:
 Load method will not hit the database until any field/property(other than id) of the instance is accessed i.e
a) User u = (User) session.load(User.class, new Long(1));
b) System.out.println("User is: "+u.getName());

In the above case, the database hit is performed at step (b) where we are actually accessing a field/property of the user by calling its getName() method.

Get method will perform the database hit immediately when it is called. i.e.
a) User u = (User) session.get(User.class, new Long(1));
b) System.out.println("User is: "+u.getName());

In above case, the database hit is performed at step (a) itself.


2) get() returns null if data is not found while load() returns 'ObjectNotFoundException'.

Explanation
a) User u = (User) session.load(User.class, new Long(1));
b) System.out.println("User is: "+u.getName());

In above case, when the load is called  the database hit is performed at step 'b', if the user with id 1 is not existing in the database, we get 'ObjectNotFoundException' at step 'b' since it hits the database at step 'b'

Get method will return null if the instance with the id we specify is not found, so we can check for null values

eg.
if (u == null) {
        System.out.println("User Details not Found !! ");
        }
else{....}


3) load() cannot access the properties of a JavaBean instance after the session is closed while
get() method instances will be active even when the session is closed.

Explanation:
With load, We cannot access the properties of a JavaBean instance after the session is closed. i.e. the scope of the instance obtained from the load is limited to a session context in which it is called. i.e.
a) User u = (User) session.load(User.class, new Long(1));
b) System.out.println("User is: "+u.getName());
c) session.close();
d) System.out.println("User is: "+u.getName());

In above case, the step 'b' would work fine since we are accessing the user instance in the scope of the session. However, the step 'd' would raise 'LazyInitializationException' because the session is closed

Get method Instances will be active even when the session is closed. i.e.
a) User u = (User) session.get(User.class, new Long(1));
b) System.out.println("User is: "+u.getName());
c) session.close();
d) System.out.println("User is: "+u.getName());

In above case, both steps 'b' and 'd' would work fine


4) Choosing one between the two is purely our  choice depending on requirement ie. if we are sure that data exist we can use load() else we can use get().

5) My personal choice is get() since we can handle if data is not found as explained in point no. 2 of above example



What is lazy and eager fetching in hibernate?
Sometimes you have two entities and there's a relationship between them. For example, you might have an entity called University and another entity called Student.
The University entity might have some basic properties such as id, name, address, etc. as well as a property called students:

 
public class University {
 private String u_id;
 private String name;
 private String address;
 
 @OneToMany(targetEntity = Student.class, 
cascade = CascadeType.ALL, fetch = FetchType.LAZY)
 @JoinColumn(name = "u_id")
 private List<Student> students; //Lazy loading
 
 // setters and getters
}

Now when you load a University from the database, JPA loads its id, name, and address fields for you. But you have two options for students: to load it together with the rest of the fields (i.e. eager loading) or to load it on-demand (i.e. lazy loading) when you call the university's getStudents() method.

LAZY = fetch when needed
EAGER = fetch immediately


Session.Save() vs Session.Persist() :
1.session.save() method returns generated identity value(primary key or assigned value) as the return value of the method call, as its return type is java.io.Serializable. Whereas session.persist() method does not returns anything as its return type is void.

eg.
public Serializable save(Object object)throws HibernateException
public void persist(Object object)throws HibernateException

2. Using the return value as result session.save() method call we can get to know what record value has been added into Database, moreover we can check if value has been added in database or not using if...else condition on returned value.

eg.
EmpBean e=new EmpBean();
  e.setId(101);
  e.setEmail("diehardtechy@gmail.com");
  int i=(Integer)ses.save(e);
  if(i>0)
  {
   System.out.println("Record added to database "+" "+"Employee ID is : "+i);
  }
  else
  {
   System.out.println("Something strange has happend, need not to panic ! ");
  }
  tx.commit();


3.session.save() takes comparatively more time to execute than session.persist() method.

4.Persist method can be used only within the boundary of a transaction whereas save method can be used inside or outside the transaction boundaries.

eg.
Session session = factory.openSession(); //start of session
Transaction t = session.beginTransaction(); //start of Transaction

t.commit();       //end of session
session.close(); //end of Transaction


saveOrUpdate vs save vs update
saveOrUpdate:
Calls either save or update depending on some checks. E.g. if "no" identifier exists, save is called. Otherwise update is called.


save:
Save method stores an object into the database. It will Persist the given transient instance, first assigning a generated identifier. It returns the id of the entity created.


update:
It updates a record in db using an existing identifier. If no identifier exists, an exception is thrown.

session.flush():
It is used to write changes from session to database. By default, Hibernate will flush changes automatically:
before some query executions
when a transaction is committed

Persistent vs transient vs Detached
1. Persistent State:
A persistent instance has a representation in the database , an identifier value and is associated with a Session. You can make a transient instance persistent by associating it with a Session:

Long id = (Long) session.save(user);  // session.save is use of session
// user is now in a persistent state 


2. Transient State:
A New instance of  a persistent class which is not associated with a Session, has no representation in the database and no identifier value is considered transient by Hibernate:

UserDetail user = new UserDetail(); 
user.setUserName("Dinesh Rajput"); 
// user is in a transient state 


3. Detached State:
Now, if we close the Hibernate Session, the persistent instance will become a detached instance: it isn't attached to a Session anymore (but can still be modified and reattached to a new Session later though).

session.close(); 
//user in detached state

Transient vs Detached States
The detached object have corresponding entries in the database while Transient does'nt.



update() vs merge():

  1. update() updates an object in the session.
  2. If the object is not in the session, you should call merge. 
  3. Calling update() for a detached instance(which is not in session) will result in an exception org.hibernate.NonUniqueObjectException.



Eg.(1)
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Student s1 = (Student)session.get(Student .class, 1);
transaction.commit();
session.close();
//s1 is now detached instance.

session = sessionFactory.openSession();
transaction = session.beginTransaction();
Student s2 = (Student )session.get(Student .class, 1);

//As you can see above, s2 represents the same persistent row as s1.
//When we try to reattach s1, an exception is thrown
session.update(s1);
transaction.commit();
session.close();


o/p:
Exception in thread "main" org.hibernate.NonUniqueObjectException: a
different object with the same identifier value was already associated
with the session: [com.intertech.domain.Student#1]
at
org.hibernate.engine.StatefulPersistenceContext.checkUniqueness
(StatefulPersistenceContext.java:699)


Eg.(2)
The merge( ) method is used to deal with situation. After executing below code :

Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Student s1 = (Student )session.get(Student .class, 1);
transaction.commit();
session.close();

//s1 is now detached.
session = sessionFactory.openSession();
transaction = session.beginTransaction();
Student s2 = (Student )session.get(Student .class, 1);
Student s3 = (Student ) session.merge(s1);
if (s2 == s3) {
System.out.println("s2 and s3 are equal");
}
transaction.commit();
session.close();

o/p:
s2 and s3 are equal


cascade
1]Cascade:  (default value of cascade =”none” )
- Cascading is about deletion of one object from the database causing deletion of other (dependent) objects(db rows) ie.  if we apply insert (or update or delete) operation on parent class object, then child class objects will also be stored into the database.

- Used in  parent child relationship

1)cascade="all"
 means to cascade both save-update and delete, as well as calls to evict and lock
eg.
@OneToMany(targetEntity = Test.class, cascade = CascadeType.ALL, fetch = FetchType.LAZY)

2) cascade="none"
The default, tells Hibernate to ignore the association

3) CascadeType.PERSIST
If the parent entity is saved in DB, the related entity will also be saved.
@OneToMany(cascade=CascadeType.PERSIST, fetch = FetchType.LAZY)

4) CascadeType.MERGE
If the parent entity is updated, the related entity will also be updated.

3) cascade="save-update"

4) cascade="delete" or CascadeType.REMOVE
If the parent entity is removed from DB, the related entity will also be removed.
Used with @OneToOne and @OneToMany

5) cascade="all-delete-orphan"
what is orphan record ..?
An orphan record means it is a record in child table but it doesn’t have association with its parent in the application.

When an object is save/update/delete, check the associations and save/update/delete all the objects found. In additional to that, when an object is removed from the association and not associated with another object (orphaned), also delete it.


Eg.

<!-- Stock.hbm.xml -->
<set name="stockDailyRecords" cascade="save-update" table="stock_daily_record">
      <key>
            <column name="STOCK_ID" not-null="true" />
      </key>
      <one-to-many class="com.common.StockDailyRecord" />
</set>



<!-- Java file -->
Stock stock = new Stock();
StockDailyRecord stockDailyRecords = new StockDailyRecord();
//set the stock and stockDailyRecords  data

stockDailyRecords.setStock(stock);       
stock.getStockDailyRecords().add(stockDailyRecords);

session.save(stock);

Output:
Hibernate:
    insert into stock (STOCK_CODE, STOCK_NAME)
    values (?, ?)

Hibernate:
    insert into stock_daily_record
    (STOCK_ID, PRICE_OPEN, PRICE_CLOSE, PRICE_CHANGE, VOLUME, DATE)
    values (?, ?, ?, ?, ?, ?)


<bag> vs <set> vs <list>  
 Set - No duplicates and No order
 Bag - Can contain Duplicates and No Order
 List : An ordered collection;  duplicates are permitted


Eg.

Mapping <set>
-------------------------

 <set name="employees" table="employee"
            inverse="true" lazy="true" fetch="select">
        <key>
            <column name="department_id" not-null="true" />
        </key>
        <one-to-many class="net.viralpatel.hibernate.Employee" />
    </set>

Mapping <bag>
--------------------------------
  <bag name="employees" table="employee"
                inverse="true" lazy="true" fetch="select">
            <key>
                <column name="employee_id" not-null="true" />
            </key>
            <one-to-many class="net.viralpatel.hibernate.Employee" />
        </bag>



Generator
1)assigned (default value):

-This generator supports in all the databases
-This is the default generator class used by the hibernate
-If generator class is assigned, then the programmer is responsible for assigning the primary key value to object



2)Increment:

-This generator supports in all the databases
- Hibernate will generate the id value
-If we manually assigned the value for primary key for an object, then hibernate doesn’t considers that value
-If there is no record initially in the database, then for the first time this will saves primary key value as 1


3)sequence:

-No MYsql Support
-sql> create sequence MySequence incremented by 5;
first it will starts with 1 by default
though you send the primary key value., hibernate uses this sequence concept only
But if we not create any sequence, then first 1 and increments by 1.... in this case hibernate is creating, so ensure we have hbm2ddl.auto enabled in the configuration file



4)identity:

-No Oracle support
-id value is generated by the database, but not by the hibernate, but in case of increment hibernate will take over this



5)hilo

-Supported by all
-for the first record, the id value will be inserted as 1
-for the second record the id value will be inserted as 32768
-for the next records the id value will be incremented by 32768

5)native:

-when we use this generator class, it first checks whether the database supports identity or not, if not checks for sequence and if not, then hilo will be used

final order
-identity
-sequence
-hilo



Annotations:
1)@GeneratedValue(strategy=GenerationType.AUTO)--> is equivalent to the native generator
2)@GeneratedValue(strategy=GenerationType.IDENTITY)--> equivalent to Hibernate's identity
3)@GeneratedValue(strategy=GenerationType.SEQUENCE)-->equivalent to Hibernate's sequence



N+1 problem:
Assume
A has 3 children [B, C, D]
B has 3 children [X, Y, Z]. C & D are childless.

Here
1) A is parent
2) B,C,D is child of A
3) X,Y,Z is GranChild of A (child of B)


So if you have a parent with “N” children, “N+1” queries is required to fetch all the grandchildren; which is a performance bottleneck. This is the infamous “N+1” problem.


How to avoid N+1 problem
-------------------------------------
Suppose
There are different Mobile phone vendors and each vendor has a collection of PhoneModels they offer(one-to-many relationship).

We want to print out all the details of phone models.

1) Criteria query

Criteria criteria = session.createCriteria(MobileVendor.class);
criteria.setFetchMode("phoneModels", FetchMode.EAGER);



2)HQL join fetch

"from MobileVendor mobileVendor join fetch mobileVendor.phoneModel PhoneModels"




JOIN vs JOIN FETCH
FROM Employee emp
JOIN emp.department dep

and

FROM Employee emp
JOIN FETCH emp.department dep


In this two queries, you are using JOIN to query all employees that have at least one department associated.

In the first query you are returning only the Employees for the Hibernate. In the second query, you are returning the Employees and all Departments associated.

So, if you use the second query, you will not need to do a new query to hit the database again to see the Departments of each Employee.

Note: JOIN FETCH will have it's effect if you have (fetch = FetchType.LAZY) property for a collection inside entity else it will fetch records from both tables.


createQuery vs createSQLQuery:
createQuery  is used to write queries in HQL  while createSQLQuery() is used for native SQL .


Inheritance Mapping
3 types of Inheritance Mappings or Hibernate Hierarchy:

1)Table per class hierarchy (Single Table Strategy)
2)Table per sub-class hierarchy
3)Table per concrete class hierarchy




1)Table per class hierarchy(Single Table Strategy)
----------------------------------------------------------
1) In table per hierarchy mapping, single table is required to map the whole hierarchy, an extra column known as discriminator column is added for parent class to identify the class.
2) Subclass have an extra column known as discriminator value to identify the class
3) But nullable values are stored in the table

eg.
<class name="str.Payment" table="PAYMENT">
<id name="paymentId" column="pid" />
<discriminator column="dcolumn" type="string" length="5"/>
<property name="amount" column="amt" />

<subclass name="str.CreditCard" discriminator-value="CC">
<property name="CreditCardType" column="cctype" length="10" />
</subclass>



click to zoom



2)Table per sub-class hierarchy
-----------------------------------
1)Tables are created as per class but related by foreign key. So there are no duplicate columns.
2)Once we save the derived class object, then derived class data and base class data will be saved in the derived class   related table in the database
3)For this type we need the tables for derived classes, but not mandatory for the base class
4)In the mapping file <joined-subclass> is used

eg:
<class name="str.Payment" table="PAYMENT">
<id name="paymentId" column="pid" />
<property name="amount" column="amt" />

<joined-subclass name="str.CreditCard" table="CreditCard">
<key column="dummy1" />
<property name="CreditCardType" column="cctype" length="10" />
</joined-subclass>


Click to zoom

Note:
In the mapping file,  <key –> element(Key is always foriegn key) is because, hibernate will first save the baseclass object then derived class object, so at the time of saving the derived class object hibernate will copy the primary key value of the base class into the corresponding derived class



3)Table per concrete class hierarchy
--------------------------------------------------------------
1)Tables are created as per class, but duplicate columns are added in subclass tables. If there are 2 column in parent class then that same 2 columns will also be added in subclass.
2) Once we save the derived class object, then derived class data and base class data will be saved in the derived class    related table in the database
3) In the mapping file <union-subclass> is used

eg.
<class name="str.Payment" table="PAYMENT">
<id name="paymentId" column="pid" />
<property name="amount" column="amt" />

<union-subclass name="str.CreditCard">
<property name="CreditCardType" column="cctype" length="10" />
</union-subclass>


Click to zoom

No comments:

Post a Comment