Q:

Second level Cache Perfomance

Recently I was studying some possibilities to improve my code , and I did some changes on it . I installed the hibernate configurations to use the second level cache, but the result of this improvement wasn't good enough .I was expecting a shortly time in my database queries. When I made some research after the implementation, the time was bigger than I had before this modification in my code . Even if I enable the cache query , my results don't change.  What I saw after enable the query cache was worse time results . Another thing what I could see , was, if my tables has some relationship with another one , the search time increase a lot.
HibernateUtil
    package com.journaldev.hibernate.util;

import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
public class HibernateUtil {

private static SessionFactory sessionFactory;

private static SessionFactory buildSessionFactory() {
    try {
        // Create the SessionFactory from hibernate.cfg.xml
        Configuration configuration = new Configuration();
        configuration.configure("hibernate.cfg.xml");
        System.out.println("Hibernate Configuration loaded");

        ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
        System.out.println("Hibernate serviceRegistry created");

        SessionFactory sessionFactoryLocal = configuration.buildSessionFactory(serviceRegistry);

        return sessionFactoryLocal;
    } catch (Throwable ex) {
        System.err.println("Initial SessionFactory creation failed." + ex);
        ex.printStackTrace();
        throw new ExceptionInInitializerError(ex);
    }
}

public static SessionFactory getSessionFactory() {
    if (sessionFactory == null) {
        sessionFactory = buildSessionFactory();
    }
    return sessionFactory;
}}

Employee
package com.journaldev.hibernate.model;

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

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

@Entity
@Table(name = "EMPLOYEE")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region="employee")
public class Employee {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "emp_id")
private long id;

@Column(name = "emp_name")
private String name;

@Column(name = "emp_salary")
private double salary;

public Employee(String name) {
    this.name = name;
}

public Employee() {
}}

Adress
    package com.journaldev.hibernate.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

@Entity
@Table(name = "ADDRESS")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region="employee")
public class Address {

    @Id
    @GeneratedValue
    private long id;

    @Column(name = "address_line1")
    private String addressLine1;

    @Column(name = "zipcode")
    private String zipcode;

    @Column(name = "city")
    private String city;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getAddressLine1() {
        return addressLine1;
    }

    public void setAddressLine1(String addressLine1) {
        this.addressLine1 = addressLine1;
    }

    public String getZipcode() {
        return zipcode;
    }

    public void setZipcode(String zipcode) {
        this.zipcode = zipcode;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

}

Employee Relation Adress
    package com.journaldev.hibernate.model;

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

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Cascade;

@Entity
@Table(name = "EMPLOYEE")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region="employee")
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "emp_id")
    private long id;

    @Column(name = "emp_name")
    private String name;

    @Column(name = "emp_salary")
    private double salary;

    @OneToOne(mappedBy = "employee")
    @Cascade(value = org.hibernate.annotations.CascadeType.ALL)
    private Address address;

    public Employee(String name) {
        this.name = name;
    }

    public Employee() {
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public String getName() {
        return name;
    }

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

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

}

Class teste
    package com.journaldev.hibernate.main;

import org.hibernate.Session;
import com.journaldev.hibernate.model.Employee;
import com.journaldev.hibernate.util.HibernateUtil;
import java.util.Date;

public class HibernateEHCacheMain {

    public static void main(String[] args) {
        //insert10000();
        // System.out.println("Temp Dir:" + System.getProperty("java.io.tmpdir"));

        long init1 = new Date().getTime();
        list10000WithCache();
        long end1 = new Date().getTime();
        long result1 = end1 - init1;
        long init2 = new Date().getTime();
        list10000WithCache();
        long end2 = new Date().getTime();
        long result2 = end2 - init2;
        long init3 = new Date().getTime();
        list10000WithCache();
        //list10000WithoutCache();
        long end3 = new Date().getTime();
        long result3 = end3 - init3;
        System.err.println("Result 1 : " + result1 + "\nResult 2 :" + result2 + "\nResult 3 :" + result3);
        System.exit(1);
    }

    private static void insert10000() {
        Session session = HibernateUtil.getSessionFactory().openSession();
        session.beginTransaction();
        for (int i = 0; i < 10000; i++) {
            session.save(new Employee(i + ""));
            if (i % 20 == 0) {
                session.flush();
                session.clear();
            }
        }
        session.getTransaction().commit();
        session.close();
    }

    private static void list10000WithoutCache() {
        Session session = HibernateUtil.getSessionFactory().openSession();
        session.beginTransaction();
        session.createQuery("from Employee").list();
        session.getTransaction().commit();
        session.close();
    }

    private static void list10000WithCache() {
        Session session = HibernateUtil.getSessionFactory().openSession();
        session.beginTransaction();
        session.createQuery("from Employee").setCacheable(true).list();
        session.getTransaction().commit();
        session.close();
    }

}

Hibernate.cfg
    <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration SYSTEM "classpath://org/hibernate/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.password">pass</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost/mydb</property>
        <property name="hibernate.connection.username">user</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="hibernate.default_batch_fetch_size">20</property>
        <property name="hibernate.current_session_context_class">thread</property>
        <property name="hibernate.show_sql">true</property>

        <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>

        <!-- For singleton factory -->
        <!-- <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</property>
        -->
        <property name="hibernate.hbm2ddl.auto">update</property>
        <!-- enable second level cache and query cache -->
        <property name="hibernate.cache.use_second_level_cache">true</property>
        <property name="hibernate.cache.use_query_cache">true</property>
        <property name="net.sf.ehcache.configurationResourceName">/myehcache.xml</property>

        <mapping class="com.journaldev.hibernate.model.Employee" />
        <mapping class="com.journaldev.hibernate.model.Address" />
    </session-factory>
</hibernate-configuration>

myehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
    monitoring="autodetect" dynamicConfig="true">

    <diskStore path="java.io.tmpdir/ehcache" />

    <defaultCache maxEntriesLocalHeap="10000" eternal="false"
        timeToIdleSeconds="120" timeToLiveSeconds="120" diskSpoolBufferSizeMB="30"
        maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120"
        memoryStoreEvictionPolicy="LRU" statistics="true">
        <persistence strategy="localTempSwap" />
    </defaultCache>

    <cache name="employee" maxEntriesLocalHeap="10000" eternal="false"
        timeToIdleSeconds="5" timeToLiveSeconds="10">
        <persistence strategy="localTempSwap" />
    </cache>

    <cache name="org.hibernate.cache.internal.StandardQueryCache"
        maxEntriesLocalHeap="5" eternal="false" timeToLiveSeconds="120">
        <persistence strategy="localTempSwap" />
    </cache>

    <cache name="org.hibernate.cache.spi.UpdateTimestampsCache"
        maxEntriesLocalHeap="5000" eternal="true">
        <persistence strategy="localTempSwap" />
    </cache>
</ehcache>

A:

How Query Caching works ?
Query Cache is used to cache the results of a query. When the query cache is turned on, the results of the query are stored against the combination query and parameters. Every time the query is fired the cache manager  checks for the combination of parameters and query. If the results are found in the cache, they are returned, otherwise a database transaction is initiated.
Why Query Cache should not use ?
As you can see, it is not a good idea to cache a query if it has a number of parameters, because then a single parameter can take a number of values. For each of these combinations the results are stored in the memory. This  can lead to extensive memory usage.
They are many pitfalls in enabling query second level caching.Please go through this link.
I dont see any issues in your configuration.All you need to enable statistic on sessionfactory to find second level caching is working or not.

Statistics statistics = sessionFactory.getStatistics();
statistics.setStatisticsEnabled(true);

You can check below values

statistics.getEntityFetchCount()
statistics.getSecondLevelCacheHitCount()
statistics.getSecondLevelCachePutCount()
statistics.getSecondLevelCacheMissCount()

Go with this post for precise explanation on second level caching with statistics.

