Showing posts with label Hibernate. Show all posts
Showing posts with label Hibernate. Show all posts

Saturday, September 7, 2013

Spring Data - JPA

A lot of effort is required for configuration and using JPA. Spring Data JPA has reduced that to a major extent. Takes little effort to configure and easy to use. Let's look at this with an example.
The spring data dependency and hibernate dependencies using maven are as follows. (Note: All other spring core, bean, context dependencies are required along with the below dependencies)
        <!-- Spring Data JPA -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>1.0.2.RELEASE</version>
        </dependency>
        <!-- Hibernate -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
Create a persistence xml file in META-INF file.
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
    <!--Persistence Unit for Mysql database-->
    <persistence-unit name="testMysql" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <class>com.test.entity.Employee</class>
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
            <property name="hibernate.show_sql" value="true"/>
        </properties>
    </persistence-unit>
</persistence>
Now, need to configure Spring Data - JPA using the persistent unit "testMysql"
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--Following data source for Mysql-->
    <bean id="mysqltestDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.testurl}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--Following entity manager for Mysql database-->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="mysqltestDataSource"/>
        <property name="persistenceUnitName" value="testMysql"/>
    </bean>

    <!--Transaction manager for both H2 and Mysql-->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
    
    <jpa:repositories transaction-manager-ref="transactionManager" 
        entity-manager-factory-ref="entityManagerFactory" base-package="com.test.dao">
    </jpa:repositories>
    
</beans>
Explanation of above spring configuration Create one datasource.

  • Create one driver manager datasource named "mysqltestDataSource", by loading jdbc.properties file from the classpath using line <context:property-placeholder location="classpath:jdbc.properties"/>
  • Create entity manager factory using the datasource created and persistent unit created in persistent.xml
  • Inject entity manager factory into transaction manager. 
  • Finally configure both transaction manager and entity manager factory to set of repositories in a package (defined using base-package)
Now, create repository, an interface which extends JpaRepository inside package (defined in base-package). The typed arguments required are one entity and other is Id. In this case, entity is Employee and Id of the Employee is Long.
package com.test.dao;

import org.springframework.data.jpa.repository.JpaRepository;

import com.test.entity.Employee;

public interface EmployeeDAO extends JpaRepository<Employee, Long>
{
}
The entity is as follows
package com.test.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Table(name = "EMP")
@Entity
public class Employee
{
    private int id;
    private String name;
    private int age;
    @Id
    @Column(name = "id", unique = true, nullable = false)
    public int getId()
    {
        return id;
    }
    public void setId(int id)
    {
        this.id = id;
    }
    @Column(name = "name")
    public String getName()
    {
        return name;
    }
    public void setName(String name)
    {
        this.name = name;
    }
    @Column(name = "age")
    public int getAge()
    {
        return age;
    }
    public void setAge(int age)
    {
        this.age = age;
    }
}
That's it on the configuration part. By using the repository, we can do all types of database operations for that entity. There are numberous in-built methods are provided by JpaRepository interface. Sample code for that will be
        EmployeeDAO dao = (EmployeeDAO)context.getBean("employeeDAO");
        List list = dao.findAll();
        for(Employee e : list)
        {
            //Do Something
        }

Thursday, July 25, 2013

Hibernate Caching - Second Level Cache

First Level Cache of hibernate (Detailed in the last post) is more specific to Session. Second Level Cache is another level where the cache is maintained in different regions across the sessions. The second level cache is divided into four regions: entity, collection, query, and timestamp.
Entity and Collection regions cache data from entities and their relations. 
The Query cache caches the result set of the database against the query. 
The Timestamp cache keeps track of last updated time of each table.
There will be on timestamp cache enabled for each query cache. The following properties to be set to enable the second level caching
hibernate.cache.use_second_level_cache = true|false
hibernate.cache.use_query_cache = true|false
hibernate.cache.region.factory_class=net.sf.ehcache.hibernate.EhCacheRegionFactory
Since Hibernate4, the factory "org.hibernate.cache.ehcache.EhCacheRegionFactory" to be used instead of "net.sf.ehcache.hibernate.EhCacheRegionFactory"
In addition to above lines, the objects to be specified with the cache strategy. The line to be specified in the hibernate hbm file in <class> tag is
<cache usage="read-write|nonstrict-read-write|read-only" />
If to be specified in Annotations on the Entity Class, use
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
How it Works

  • When we fetch an object using hibernate, it searches the object in first level cache. If found, it returns the object
  • If the object not found in first level cache, checks the second level cache. If found, stores the object in first level cache returns
  • If the entity is not found both the levels, a database query is fired and stores in both the levels and returns.
  • The second level cache validates all of its entities, if any modifications are done through hibernate APIs. But, it never know if the database object is modified by any other resource unless "timeToLiveSeconds" duration has passed 

Notes:
To view the statistics of Entity fetches, second level cache hits, etc. We need to configure explicitly as.
hibernate.generate_statistics=true|false
and using "sessionFactory.getStatistics()" which retunrs an object of Statistics type. 

Hibernate Caching - First Level Cache

Caching is one of the basic feature provided by ORM frameworks. Caching make the application to run faster by reducing number of queries to the database. Hibernate provides two levels of cache for this.
  1. First Level Cache: First level cache is associated with each session. This cache is by default and you cannot switch off this. As we can create sessions on demand, there will be first level cache associated with each session.
  2. Second Level Cache: Second level cache is associated with Session Factory. Basically, there is only one second level cache as only one Session Factory is maintained by application.
When a session is closed, the first level cache associated with it will be cleared. When a session factory is cleared, full second level cache associated with it will be lost.

Example for First level cache:

   SessionFactory sessionFactory = (SessionFactory) context.getBean("sessionFactory");
   Session session = sessionFactory.openSession();
        
   Employee emp = null;
   emp = (Employee) session.load(Employee.class, new Integer(1));
   System.out.println("Employee Name : "+emp.getName());
        
   emp = (Employee) session.load(Employee.class, new Integer(1));
   System.out.println("Employee Name : "+emp.getName());
        
   Session newSession = sessionFactory.openSession();
   emp = (Employee) newSession.load(Employee.class, new Integer(1));
   System.out.println("Employee Name : "+emp.getName());
        
   session.evict(emp);
   emp = (Employee) session.load(Employee.class, new Integer(1));
   System.out.println("Employee Name : "+emp.getName());
        
   newSession.clear();
   emp = (Employee) newSession.load(Employee.class, new Integer(1));
   System.out.println("Employee Name : "+emp.getName());
The log for the above program will be :
Hibernate: select employee0_.id as id0_0_, employee0_.age as age0_0_, employee0_.name as name0_0_ from EMP employee0_ where employee0_.id=?
Employee Name : name
Employee Name : name
Hibernate: select employee0_.id as id0_0_, employee0_.age as age0_0_, employee0_.name as name0_0_ from EMP employee0_ where employee0_.id=?
Employee Name : name
Hibernate: select employee0_.id as id0_0_, employee0_.age as age0_0_, employee0_.name as name0_0_ from EMP employee0_ where employee0_.id=?
Employee Name : name
Hibernate: select employee0_.id as id0_0_, employee0_.age as age0_0_, employee0_.name as name0_0_ from EMP employee0_ where employee0_.id=?
Employee Name : name

Explanation:
First time when load method called, the object is fetched by firing a query to database. When the second time loaded, no query was fired as the object is cached in first level cache.
Even though the object is cached by session "session", the new session "newSession" is fetched the object by firing a new query.
To clear the cache from the session, evict() or clear() method can be used. Once it's cleared, the session tries to get the object from the database. The last two queries of hibernate in the log explains that.

Notes:

  • First level cache is only associated to Session. Each session will have it's own first level cache.
  • There is no extra configuration required for first level cache. No way to switch off configuration.
  • Cache will be cleared by closing the session, by using clear() method.
  • Individual object can be removed from the cache by using evict method.