Introduction

Data Persistence

Components of Data Persistence

Data : Data can be of two types

	1. Raw Data
	2. Java Object

Medium : Medium can be of three types

	1. Java I/O Streams
	2. Serialization / De-serialization
	3. JDBC (Java Database Connectivity)

Storage : Storage can be of two types

	1. File
	2. Database

Data Persistence 1

Data Persistence 2

Serialization & De-serialization

Serialization : It is a process of converting Java object into a stream of bytes. Now these stream of bytes can be stored in a file or can be transferred over a network.

De-Serialization : It is the processing converting a stream of bytes into a Java object.

Drawbacks of using Files

	1. Data Redundancy
	2. Data Inconsistency
	3. Data Security
	4. Storage
	5. No transaction support
	6. No constraints support

JDBC

Requirement : I have an Employee object, I want to store the Employee object into a Database using JDBC API ?

	executeUpdate(employee)    --->    error
	executeQueryCemployee)    --->    error
	insert into emp_table values (employee)    --->    error

Solution :

Need of ORM Tools

Note: We can't transfer raw data using ORM tools.

JDBC vs ORM 1

JDBC vs ORM 2

ORM

What is Hibernate ?

What is JPA ?

JPA

Q) What is the difference between writing the DAO classes directly using Hibernate and using JPA ?

Hibernate

Features of Hibernate

HQL (Hibernate Query Language)

	select * from emp    --->    SQL
	select e from Employee e    --->    HQL
	
	select empno, ename from emp    --->    SQL
	select e.employeeNo, e.employeeName from Employee e    --->    HQL

Caching

Caching in Hibernate

Lazy Loading

Connection Pooling

Criteria API

	select * from emp    --->    untuned query
	select empno, ename, sat, deptno from emp    --->    tuned query

Locking

Associations

Files Required for Hibernate Application

POJO

	// Example-1
	class A {
		// variables
		// methods
	}
	
	// Example-2
	class B extends A {
		// variables
		// methods
	}

Java Bean

Note: Every Java bean class is a POJO class. But every POJO class is not a Java Bean class.

Mapping File

Mapping Java class with DB table

Writing Mapping File
	<!-- student.hbm.xml -->
	<hibernate-mapping>
		<class name="in.ashokit.entity.Student" table="TBL_STUDENT">
			<id name="sid" column="SID" />
			<property name="sname" column="SNAME" />
			<property name="gender" column="GENDER" />
			<property name="marks" column="MARKS" />
		</class>
	</hibernate-mapping>
	<!-- student.hbm.xml -->
	<hibernate-mapping>
		<class name="in.ashokit.entity.Student" table="TBL_STUDENT">
			<id name="sid" />
			<property name="sname" />
			<property name="gender" />
			<property name="marks" />
		</class>
	</hibernate-mapping>

Configuration File

Writing Configuration File
	<!-- hibernate.cfg.xml -->
	<hibernate-configuration>
		<session-factory>
			<!-- connection properties -->
			<property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>
			<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hiberdb</property>
			<property name="hibernate.connection.username">ashokit</property>
			<property name="hibernate.connection.password">AshokIT@123</property>
	
			<!-- hibernate properties -->
			<property name="hibernate.dialect">org.hibernate.dialect.MySQL8Dialect</property>
			<property name="hibernate.show_sql">true</property>
			<property name="hibernate.format_sql">true</property>
			<property name="hibernate.hbm2ddl.auto">update</property>
	
			<!-- mapping resources -->
			<mapping resource="student.hbm.xml" />
		</session-factory>
	</hibernate-configuration>

Note: If our application is using two databases (Ex: MySQL, Oracle) then two configuration files need to be created.

Client Program

Maven

Steps to setup Maven

Maven Archetypes

Maven Project Coordinates

	Group Id    --->    a unique id to identify a group of projects of a client.
	Artifact ld    --->    project name
	Version    --->    project version

Maven Dependencies

	<!-- Hibernate Dependency -->
	<dependency>
		<groupId>org.hibernate</groupId>
		<artifactId>hibernate-core</artifactId
		<version>5.3.1.Final</version>
	</dependency>

Important Directories in Maven Project

	src/main/java    --->    source code files
	src/test/java    --->    test code files
	src/main/resources    --->    for other resources/files (xml/properties/txt)

Hibernate App Folder Structure

Hibernate Application Folder Structure

hibernate.hbm2ddl.auto Property

create : Hibernate drops the existing tables and creates new tables in DB to perform operations.

update : Hibernate uses existing tables to perform DB operations. If tables not exists it creates new tables in DB. (Only in Selected Databases).

create-drop : Hibernate creates new tables to perform operations and drops the tables at the end of the application. Mostly used in unit-testing.

validate : Hibernate validates the tables and columns in the Database. If a table/column doesn't exist, it will throw an exception. This is the default value of this property.

get() vs load()

get()

load()

Q) Can we map a table without primary key to a java class ?
Ans : Yes, we can map a table which does not have a primary key to a java class and we can insert the records into the table also. But as the table does not have a primary key we can not retrieve the data from the table.

Level1 Cache

How Level1 Cache Works ?

	// How Level1 Cache Works
	Session first_session = factory.openSession();
	Session second_session = factory.openSession();
	
	Student s1 = first_session.get(Student.class, 11011);
	Student s2 = first_session.get(Student.class, 11011);
	Student s3 = second_session.get(Student.class, 11011);

Level1 Cache

Q) How many times Hibernate hits the database for the below code ?

	Student s1 = first_session.get(Student.class, 11011);
	Student s2 = first_session.get(Student.class, 22022);
	Student s3 = first_session.get(Student.class, 11011);
	Student s4 = second_session.get(Student.class, 11011);

Ans : 3 times

Some Important Points

Q) Can we delete the cache of a session explicitly ?
Ans : We can't delete a cache explicitly. It is removed when session is closed .

Q) I want to remove a specific object from the cache ?
Ans : Yes. call evict() method to remove the object from cache.

	session.evict(s1);

Evict Method

Q) I want to remove all the objects from cache at a time?
Ans : Yes. call clear() method to remove all objects from the cache.

	session.clear();

Clear Method

Q) what is the drawback of the cache ?
Ans : If any changes are made to the object in database, they are not reflected in cache automatically. So sometimes we can get stale data from cache. This is the drawback.

Solution: You have to explicitly refresh the cache for every some time interval to make the object in cache are in sync with database.

Level1 Cache  Drawback

JPA

What is Annotation ?

JPA Annotations for Mapping

@Entity : It will declare that a POJO class is an entity class.

@Table : It is used to map a POJO class with a table in the database. If the POJO class and database table name is same then writing this annotation is optional.

@Id : It is used to map a variable in our Java class with primary key column in the table.

@Column : It is used to map a variable with a column in the table. If the variable name and column name is same then writing this annotation is optional.

JPA Mapping

Q) Can we map a java class with a table, which has more variables ?
Ans: Yes. Use @Transient annotation for a variable which does not have a corresponding column in the table.

JPA Mapping using Transient

Q) Can we map a java class with table having more columns ?
Ans: Yes. But if the remaining columns of the table does not contain not null constraint.

JPA Mapping when table have more Columns

JPA Configuration File

	<!-- persistence.xml -->
	<persistence xmlns="http://java.sun.com/xml/ns/persistence"
	             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
	             version="2.0">
	    <persistence-unit name="test" transaction-type="RESOURCE_LOCAL">
	        <!-- Persistence provider -->
	        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
	        <!-- Entity classes -->
	        <class>in.ashokit.entity.ProductEntity</class>
	        
	        <properties>
	            <property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver" />
	            <property name="javax.persistence.jdbc.url"    value="jdbc:mysql://localhost:3306/hiberdb" />
	            <property name="javax.persistence.jdbc.user" value="ashokit" />
	            <property name="javax.persistence.jdbc.password" value="AshokIT@123" />
	            
	            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect"/>
	            <property name="hibernate.hbm2ddl.auto" value="update" />
	            <property name="hibernate.show_sql" value="true"/>
	            <property name="hibernate.format_sql" value="true"/>
	        </properties>
	    </persistence-unit>
	</persistence>

Mapping Composite Primary Key

Composite Primary Key

	// Embeddable class
	@Embeddable
	public class StudentCompositeKey implements Serializable {
		@Column(name="roll_no")
		private Integer rollNo;
		
		@Column(name="section")
		private String section;
		
	    // setters & getters
	}
	// Entity class with embeddable id
	@Entity
	@Table(name="tbl_student")
	public class StudentEntity {
		@EmbeddedId
		private StudentCompositeKey compositeKey;
		
		@Column(name="name")
		private String name;
		
		@Column(name="marks")
		private Integer marks;
	        
	    // setters & getters
	}

Entity States in JPA

Transient State : Whenever a new object is created for an entity class then that entity will be in transient state.

Persistent State : After setting the data to the entity, we are going to persist the entity to the database. Now the entity is in persistent state.

Detached State : When entity manager is closed or when cleared or when an object is detached then the entity enters into detached state.

Entity States in JPA

JPQL

Q) Suppose I'm calling find() method then how many objects can be loaded at a time from the database ?
Ans: one object

Q) Suppose I'm calling remove() method then how many objects can be removed at a time from the DB ?
Ans: one object

Q) How can I load or update or delete multiple objects or bulk objects at a time from the Database ?
Ans: Use JPQL or SQL queries.

	select * from emp    --->    SQL
	select e from EmployeeEntity e    --->    JPQL
	from EmployeeEntity e    --->    JPQL

	select ename, sal from emp    --->    SQL
	select e.employeeName, e.salary from EmployeeEntity e    --->    JPQL

	select * from emp where sal > ?    --->    SQL
	select e from EmployeeEntity e where e.salary > ?1    --->    JPQL
	from EmployeeEntity e where e.salary > ?1    --->    JPQL

	select * from emp where deptno = ? and sal > ?    --->    SQL
	select e from EmployeeEntity e where e.deptNo = ?1 and e.salary > ?2    --->    JPQL
	from EmployeeEntity e where e.deptNo = ?1 and e.salary > ?2    --->    JPQL
	from EmployeeEntity e where e.deptNo = :dno and e.salary > :esal    --->    JPQL

	update emp set sal = ? where deptno = ?    --->    SQL
	update EmployeeEntity e set e.salary = ?1 where e.deptNo = ?2    --->    JPQL
	update EmployeeEntity e set e.salary = :esal where e.deptNo = :dno    --->    JPQL

	select e.ename, d.dname from emp e join dept d on e.deptno = d.deptno    --->    SQL
	select e.empName, d.deptName from EmployeeEntity e join DepartmentEntity d on e.deptNo = d.deptNo    --->    JPQL

How to run JPQL Queries

	// Selecting employees based on deptNo using JPQL
	String jpql = "from EmployeeEntity e where e.deptNo = ?1";
	Query q = entityManager.createQuery(jpql); // step-1
	q.setParameter(1, 20); // step-2
	// retrive multiple entities
	List listOfEmployees = q.getResultList(); // step-3
	// Selecting employee based on empNo using JPQL
	String jpql = "from EmployeeEntity e where e.empNo = ?1";
	Query q = entityManager.createQuery(jpql); // step-1
	q.setParameter(1, 7788); // step-2
	// retrives a single entity
	Object obj = q.getSingleResult(); // step-3
	// Updating salaries based on deptNo using JPQL
	String jpql = "update EmployeeEntity e set e.salary = :esal where e.deptNo = :dno";
	Query q = entityManager.createQuery(jpql); // step-1
	q.setParameter("esal", 20000.0); // step-2
	q.setParameter("dno", 30); // step-2

	// for non-select operations tx is required
	EntityTransaction tx = entityManager.getTransaction();
	tx.begin();
	int rowsEffected = q.executeUpdate(); // step-3
	tx.commit();

Type of JPA Queries

Typed Queries

	// Selecting employee based on empNo using Untyped query
	String jpql = "from EmployeeEntity e where e.empNo = ?1";
	Query q = entityManager.createQuery(jpql);
	q.setParameter(1, 7788);
	Object obj = q.getSingleResult();
	EmployeeEntity e = (EmployeeEntity) obj; // casting
	// Selecting employee based on empNo using Typed query
	String jpql = "from EmployeeEntity e where e.empNo = ?1";
	TypedQuery<EmployeeEntity> tq = entityManager.createQuery(jpql, EmployeeEntity.class);
	tq.setParameter(1, 7788);
	EmployeeEntity e = tq.getSingleResult(); // casting not required
	// How to select specific properties of a entity using Typed query
	String jpql = "select e.empName, e.salary from EmployeeEntity e";
	TypedQuery<Object[]> tq = entityManager.createQuery(jpql, Object[].class);
	List<Object[]> list = tq.getResultList();

Native Queries

	// Selecting employees based on deptno using Native query
	String sql = "select * from employee where deptno = ?";
	Query nativeQuery = entityManager.createNativeQuery(sql, EmployeeEntity.class);
	nativeQuery.setParameter(1, 30);
	List empList = nativeQuery.getResultList();
	// Updating salaries based on deptno using Native query
	String sql = "update employee set sal = ? where deptno = ?";
	Query nativeQuery = entityManager.createNativeQuery(sql);
	nativeQuery.setParameter(1, 20000.0);
	nativeQuery.setParameter(2, 30);

	// for non-select operations tx is required
	EntityTransaction tx = entityManager.getTransaction();
	tx.begin();
	int count = nativeQuery.executeUpdate();
	tx.commit();

Named Queries

	// Entity class with named query & named native query
	@Entity
	@Table(name="employee")
	@NamedQuery(name="query1", query="from EmployeeEntity e where e.empId = :eid")
	@NamedNativeQuery(name="query2", query="select * from employee", resultClass = EmployeeEntity.class)
	public class EmployeeEntity {
		@Id
		@Column(name="emp_id")
		private Integer empId;
		
		@Column(name="emp_name")
		private String empName;
		
		@Column(name="emp_sal")
		private Double empSalary;
		
		@Column(name="dept_no")
		private Integer deptNo;
		
		// getters & setters
	}

Note: We use wrapper classes instead of primitive types for entity attributes in Java because wrapper classes have a default value of null. This ensures that if no value is provided, null will be stored in the database.

How to execute Named Queries

	// Executing named query
	TypedQuery<EmployeeEntity> namedQuery = entityManager.createNamedQuery("query1", EmployeeEntity.class);
	namedQuery.setParameter("eid", 7788);
	EmployeeEntity emp = namedQuery.getSingleResult();
	// Executing named native query
	Query namedQuery = entityManager.createNamedQuery("query2");
	List resultList = namedQuery.getResultList();

Criteria API

	// Selecting employee names and salaries using Criteria API
	CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
	CriteriaQuery<Object[]> criteriaQuery = criteriaBuilder.createQuery(Object[].class);
	Root<EmployeeEntity> root = criteriaQuery.from(EmployeeEntity.class);
	criteriaQuery.select(criteriaBuilder.array(root.get("empName"), root.get("empSalary")));
	
	TypedQuery<Object[]> query = entityManager.createQuery(criteriaQuery);
	List<Object[]> resultList = query.getResultList();
	// Selecting employees based on deptNo using Criteria API
	CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
	CriteriaQuery<EmployeeEntity> criteriaQuery = criteriaBuilder.createQuery(EmployeeEntity.class);
	Root<EmployeeEntity> root = criteriaQuery.from(EmployeeEntity.class);
	criteriaQuery.select(root).where(criteriaBuilder.equal(root.get("deptNo"), 20));
	
	TypedQuery<EmployeeEntity> query = entityManager.createQuery(criteriaQuery);
	List<EmployeeEntity> resultList = query.getResultList();

Working with Stored Procedures

	-- PLSQL procedure to calculate experience of employee
	CREATE OR REPLACE PROCEDURE EMP_PROCEDURE(
	  EMP_NO IN NUMBER,
	  EMP_NAME OUT VARCHAR2,
	  YOE OUT NUMBER
	)
	IS
	  DOJ DATE;
	BEGIN
	  SELECT ENAME, HIREDATE
	  INTO EMP_NAME, DOJ
	  FROM EMP
	  WHERE EMPNO = EMP_NO;
	
	  YOE := FLOOR((SYSDATE - DOJ) / 365);
	END;
	/
	// Entity class with stored procedure
	@Entity
	@Table(name="emp")
	@NamedStoredProcedureQuery(
			name="procedure",
			procedureName = "emp_procedure",
			parameters = {
					@StoredProcedureParameter(mode = ParameterMode.IN, type = Integer.class, name = "emp_no"),
					@StoredProcedureParameter(mode = ParameterMode.OUT, type = String.class, name ="emp_name"),
					@StoredProcedureParameter(mode = ParameterMode.OUT, type = Integer.class, name ="yoe")
			}
			)
	public class EmpEntity {
		// entity class code
	}
	// Executing stored procedure using JPA
	StoredProcedureQuery procedureQuery = entityManager.createNamedStoredProcedureQuery("procedure");
	procedureQuery.setParameter("emp_no", 7788);
			
	tx.begin();
	procedureQuery.execute();
	String name = (String) procedureQuery.getOutputParameterValue("emp_name");
	int yoe = (Integer) procedureQuery.getOutputParameterValue("yoe");
	tx.commit();
			
	System.out.println(name+", "+yoe);

JPA Associations

One to Many

cascade

fetch

Requirement: When a CRUD operation is performed on Category, we would like to propagate that operation onto the Product.

One to Many Association

Solution: apply one to many relationship between category and products. Here, source of operations is category and target of the operations is product.

	// Source entity 
	@Entity
	@Table(name="tbl_categories")
	public class CategoryEntity {
		@Id
		@Column(name="category_id")
		private Integer categoryId;
		
		@Column(name="category_name")
		private String categoryName;
		
		@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
		@JoinColumn(name = "category_id_fk")
		private List<ProductEntity> productList; // one to many field
		
		// getters & setters
	}
	// Target entity
	@Entity
	@Table(name="tbl_products")
	public class ProductEntity {
		@Id
		@Column(name="product_id")
		private Integer productId;
		
		@Column(name="product_name")
		private String productName;
		
		private Double price;
		
		// getters & setters
	}

Many to One

Many to One Association

	// Source entity
	@Entity
	@Table(name="tbl_loans")
	public class LoanEntity {
		
		@Id
		@Column(name="loan_id")
		private Integer loanId;
		
		@Column(name="loan_type")
		private String loanType;
	
		private Double amount;
		
		@ManyToOne(cascade = CascadeType.ALL)
		@JoinColumn(name="customer_id")
		private CustomerEntity customer; // many to one field
		
		// getters & setters
	}
	// Target entity
	@Entity
	@Table(name="tbl_customers")
	public class CustomerEntity {
		
		@Id
		@Column(name="customer_id")
		private Integer customerId;
		
		@Column(name="customer_name")
		private String customerName;
		
		// getters & setters
	}

One to Many (Bi-directional)

One to many bi-directional 01

One to many bi-directional 02

	// Source entity
	@Entity
	@Table(name="tbl_categories")
	public class CategoryEntity {
		@Id
		@Column(name="category_id")
		private Integer categoryId;
		
		@Column(name="category_name")
		private String categoryName;
		
		@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "category")
		private List<ProductEntity> productList;
		
		// getters & setters
	}
	// Source entity
	@Entity
	@Table(name="tbl_products")
	public class ProductEntity {
		@Id
		@Column(name="product_id")
		private Integer productId;
		
		@Column(name="product_name")
		private String productName;
		
		private Double price;
	
		@ManyToOne(cascade = CascadeType.ALL)
		@JoinColumn(name = "category_id")
		private CategoryEntity category;
		
		// getters & setters
	}

Many to Many

	Many to Many    =    One to Many    +    Inverse One to Many

	one Book has many Authors    =    One to Many
	one Author has written many Books    =    Inverse One to Many
	Now the relationship is Many to Many

Many to many Association 01

Many to many Association 02

	// Source entity
	@Entity
	@Table(name = "tbl_books")
	public class BookEntity {
	
		@Id
		@Column(name = "book_id")
		private Integer bookId;
	
		@Column(name = "book_name")
		private String bookName;
	
		@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
		@JoinTable(name = "tbl_books_authors",
					joinColumns = @JoinColumn(name = "book_id"),
					inverseJoinColumns = @JoinColumn(name = "author_id")
		)
		private List<AuthorEntity> listOfAuthors;

		// getters & setters
	}
	// Target entity
	@Entity
	@Table(name = "tbl_authors")
	public class AuthorEntity {
	
		@Id
		@Column(name = "author_id")
		private Integer authorId;
	
		@Column(name = "author_name")
		private String authorName;
	
		@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "listOfAuthors")
		private List<BookEntity> listOfBooks;

		// getters & setters
	}

xxxToMany

	FetchType    --->    LAZY
	Variable    ---> Collection Variable

xxxToOne

	FetchType    --->    EAGER
	Variable    --->    Reference Variable

One to One

One to One Association

	// Source entity
	@Entity
	@Table(name="tbl_persons")
	public class PersonEntity {
		
		@Id
		@Column(name="person_id")
		private Integer personId;
		
		@Column(name="person_name")
		private String personName;
		
		@OneToOne(cascade = CascadeType.ALL)
		@JoinColumn(name = "passport_id", unique = true, nullable = false)
		private PassportEntity passport;
		
		// getters & setters
	}
	// Target entity
	@Entity
	@Table(name="tbl_passports")
	public class PassportEntity {
		
		@Id
		@Column(name="passport_id")
		private Integer passportId;
		
		@Column(name="expire_date")
		private LocalDate expDate;
		
		// getters & setters
	}

JPQL Joins

Q) I want to fetch the name of employee and his department name with JPQL join query ?

	select e.empName, d.deptName from Department d join d.listOfEmployees e;

Q) I want to fetch the category name and product name with JPQL query ?

	select c.categoryName, p.productName from CategoryEntity c join c.listOfProducts p;

Q) I want to fetch category name and product name where product name should contain a letter 'm' in uppercase or in lowercase ?

	select c.categoryName, p.productName from CategoryEntity c join c.listOfProducts p where p.productName like '%m%' or '%M%';

@GeneratedValue Annotation

	@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "idgen")
	@SequenceGenerator(name = "idgen", sequenceName = "my_seq", allocationSize = 3)

Level2 Cache

Level 2 Cache

How Level2 Cache Works

How to Enable Level2 Cache

Step-1

	<property name="hibernate.cache.region.factory_class" value="ehcache"/>
	<property name="hibernate.cache.use_query_cache" value="true"/>

Step-2

	<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
	ALL    --->    all entities will be cached
	NONE    --->    no entities will be cached
	ENABLE_SELECTIVE    --->    only entities specified as cacheable will be cached

Note: @Cacheable annotation is used to specify an entity as cacheable.

Step-3

	<!-- Hibernate EntityManager dependency -->
	<dependency>
		<groupId>org.hibernate</groupId>
		<artifactId>hibernate-entitymanager</artifactId>
		<version>5.6.8.Final</version>
	</dependency>
	
	<!-- EHCache dependency -->
	<dependency>
		<groupId>org.hibernate</groupId>
		<artifactId>hibernate-ehcache</artifactId
		<version>5.6.8.Final</version>
	</dependency>

Note: Version of hibernate-core dependency and hibernate-entitymanager dependency must be same.

Step-4

	<!-- ehcache.xml -->
	<ehcache>
		<!-- default region -->
		<defaultCache
			maxEntriesLocalHeap = "100"
	        eternal = "false"
	        timeToIdleSeconds = "50"
	        timeToLiveSeconds = "120">
	    </defaultCache>
	    
	    <!-- ProductEntity region -->
	    <cache name = "in.ashokit.entity.ProductEntity"
	        maxEntriesLocalHeap = "10"
	        eternal = "false"
	        timeToIdleSeconds = "20"
	        timeToLiveSeconds = "60">
	    </cache>
	</ehcache>
	name    --->    to specify target entity class (it will be used as region name)
	maxEntriesLocalHeap    --->    maximum no of entity objects that can be cached
	eternal    --->    to specify whether the objects can automatically expire or not
	timeToIdleSeconds    --->    after how many idle seconds object will remove from cache
	timeToLiveSeconds    --->    maximum time a object can spent in cache regardless of usage

Query Cache

	query.setHint("org.hibernate.cacheable", true);

Introduction

Data Persistence

Hibernate

Features of Hibernate

Files Required for Hibernate Application

Maven

Level1 Cache

JPA

JPQL

Type of JPA Queries

JPA Associations

Level2 Cache