Wildfly JPA Utilize Second Level Cache

Caching is very import technique to improve performance on inquiry. Especially, if you are working with a heavy query that need to join many tables or to retrieve huge amount of the data from the database. Without caching, every single time that data query is executed, disk access happened. This is not a practical manner to handle data inquiry and lead to poor system performance. Accessing data without fetching data directly from the disk could increase data inquiring speed dramatically. Caching inquiry result in the memory and reuse that data is very common practice once it come to handle heavy query or huge amount of data.

Hibernate provides implementation of JPA. It provides first level cache, second level cache and query cache. For the first level cache, it is enable by default. In this post, I will demonstrate how to enable second level cache in both JPA and Hibernate with Wildfly.

JPA with Second Level Cache

JPA provide second level cache but not by default, you need to enable it through persistence.xml. First, you have to create JPA project

Fig 01. Create JPA Project 01

Fig 02. Create JPA Project 02

Fig 03. Create JPA Project 03

Fig 04. Create JPA Project - 04

Fig 05 Create JP Project 05

Create JPA Class from Table as below.

Fig 06 Create JPA Class


package pro.itstikk.jpa.entity;

import java.io.Serializable;

import javax.persistence.Cacheable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.Table;


/**
 * The persistent class for the stafftbl database table.
 * 
 */
@Entity
@Table(name="stafftbl")
@Cacheable
@NamedQuery(name="Staff.findAll", query="SELECT s FROM Staff s")
public class Staff implements Serializable {
private static final long serialVersionUID = 1L;

@Id
private String staffid;

private String firstname;

private String lastname;

public Staff() {
    super();
}

public String getStaffid() {
    return this.staffid;
}

public void setStaffid(String staffid) {
    this.staffid = staffid;
}

public String getFirstname() {
    return this.firstname;
}

public void setFirstname(String firstname) {
    this.firstname = firstname;
}

public String getLastname() {
    return this.lastname;
}

public void setLastname(String lastname) {
    this.lastname = lastname;
}

}

Next is to configure persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="java-ee-02-jpa-ehcache" transaction-type="JTA">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<jta-data-source>java:/sampledbs</jta-data-source>
<mapping-file>META-INF/orm.xml</mapping-file>
<class>pro.itstikk.jpa.entity.Staff</class>
<shared-cache-mode>ALL</shared-cache-mode>
<properties>
    <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
    <property name="hibernate.connection.driver_class" value="org.postgresql.Driver"/>
     <property name="hibernate.show_sql" value="true"/>
     <property name="hibernate.format_sql" value="true"/>
    <property name="hibernate.cache.use_second_level_cache" value="true" />
     <property name="hibernate.cache.use_query_cache" value="true" />
     <property name="hibernate.generate_statistics" value="true" />
</properties>
</persistence-unit>
</persistence>

For orm.xml is mapping file. I have create as below.

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings
    xmlns="http://java.sun.com/xml/ns/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
        http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"
    version="2.0">
    <named-native-query name="STAFF.SQL.00" result-set-mapping="STAFF.MAPPING">
    <query>SELECT * FROM stafftbl</query>
    </named-native-query>
    <named-native-query name="STAFF.SQL.01" result-set-mapping="STAFF.MAPPING">
    <query>SELECT * FROM stafftbl s WHERE s.staffid = :ID</query>
    </named-native-query>
    <sql-result-set-mapping name="STAFF.MAPPING">
    <entity-result entity-class="pro.itstikk.jpa.entity.Staff">
    <field-result name="staffid" column="staffid"/>
    <field-result name="firstname" column="firstname"/>
    <field-result name="lastname" column="lastname"/>
    </entity-result>
    </sql-result-set-mapping>
</entity-mappings>

Create EJB Project

To create DAO (Data Access Object), you just need to create EJB project.

Fig 07. EJB Project Creation

Create Interface and the interface implementation class.

package pro.itstikk.dao;

import java.util.List;

import javax.ejb.Remote;

import pro.itstikk.jpa.entity.Staff;

@Remote
public interface StaffDao {
public void save(Staff staff);
public List<Staff> getStaffs();
public List<Staff> list();
public Staff find(String id);
}

For the implementation class is implemented as below.

package pro.itstikk.dao;

import java.util.List;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.transaction.Transactional;

import pro.itstikk.jpa.entity.Staff;

/**
 * Session Bean implementation class StaffDaoImp
 */
@Stateless
public class StaffDaoImp implements StaffDao {
@PersistenceContext(unitName="java-ee-02-jpa-ehcache")
private EntityManager em;
    /**
     * Default constructor. 
     */
    public StaffDaoImp() {
        super();
    }

@Override
public List<Staff> getStaffs() {
TypedQuery<Staff> query = em.createNamedQuery("Staff.findAll", Staff.class);
List<Staff> list = query.getResultList();
return list;
}

@Override
public List<Staff> list() {
Query query = em.createNamedQuery("STAFF.SQL.00");
return query.getResultList();
}

@Override
public Staff find(String id) {
Query query = em.createNamedQuery("STAFF.SQL.01");
query.setParameter("ID", id);
return (Staff) query.getSingleResult();
}
@Transactional
@Override
public void save(Staff staff) {
em.persist(staff);
}

}

Create Web Dynamic Project

To demonstrate interface to EJB project and CRUD operation, I just create Web Dynamic Project

Fig 08. Create Dynamic Web Project

to map xhtml view interface, create managed bean

package pro.itstikk.bean;

import java.util.List;

import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;

import pro.itstikk.dao.StaffDao;
import pro.itstikk.jpa.entity.Staff;
@Named("staffBean")
@RequestScoped
public class StaffBean {
@EJB
private StaffDao staffDao;
private List<Staff> list;
private String staffid;
private Staff staff;
public StaffBean() {
    super();
}

@PostConstruct
public void init() {
    staff = new Staff();
}
public List<Staff> save(){
    staffDao.save(staff);
    list = staffDao.list();
    staff = new Staff();
    return list;
}

public Staff find(){
    staff = staffDao.find(staffid);
    return staff;
}

public List<Staff> getList() {
    list = staffDao.list();
    return list;
}

public void setList(List<Staff> list) {
    this.list = list;
}

public String getStaffid() {
    return staffid;
}

public void setStaffid(String staffid) {
    this.staffid = staffid;
}

public Staff getStaff() {
    return staff;
}

public void setStaff(Staff staff) {
    this.staff = staff;
}
}

Here is index.xhtml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:rich="http://richfaces.org/rich"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:a4j="http://richfaces.org/a4j"
      xmlns:h="http://java.sun.com/jsf/html"> 

<h:head>
<title>JPA Cache</title>
</h:head> 
<body>
    <rich:panel>
        <f:facet name="header">
            Write your own custom rich components with built-in AJAX support
        </f:facet>
       <div style="margin: 30px 0 30px 0;">
       <rich:dataTable var="item" value="#{staffBean.list}">
           <f:facet name="header">
               <rich:columnGroup>
                    <rich:column rowspan="1">
                        <h:outputText value="STAFFID" />
                    </rich:column>
                    <rich:column rowspan="1">
                        <h:outputText value="FIRSTNAME" />
                    </rich:column>
                    <rich:column rowspan="1">
                        <h:outputText value="LASTNAME" />
                    </rich:column>
              </rich:columnGroup>
            </f:facet>

             <rich:column rowspan="1">
                <h:outputText value="#{item.staffid}" />
            </rich:column>
            <rich:column rowspan="1">
                <h:outputText value="#{item.firstname}" />
            </rich:column>
            <rich:column rowspan="1">
                <h:outputText value="#{item.lastname}" />
            </rich:column>
      </rich:dataTable>
</div>
</rich:panel>
</body> 
</html>

Create Search Staff Search page and result page search.xhtml

<!DOCTYPE html>
<ui:composition xmlns="http://www.w3.org/1999/xhtml" 
xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core" 
    xmlns:ui="http://java.sun.com/jsf/facelets" 
    xmlns:a4j="http://richfaces.org/a4j"
    xmlns:rich="http://richfaces.org/rich">

<h:head>
<title>search</title>
</h:head> 
<body>
    <rich:panel>
        <f:facet name="header">
        Write your own custom rich components with built-in AJAX support
        </f:facet>
        <h:form id="form">
            <h:outputLabel value="STAFF ID:" for="staffid"/>
            <rich:inplaceInput defaultLabel="000" value="#{staffBean.staffid}" id="staffid" style="width:100px; margin: 2px 10px 2px 10px; text-align:right;"/>
            <a4j:commandButton action="#{staffBean.find()}" execute="@form" value="search" render="out" />
        </h:form>
        <a4j:outputPanel id="out">
            <rich:panel style="margin-top: 10px;">
            <p>
                <h:outputLabel value="staff id :" for="sid"/>
                <h:outputText value="#{staffBean.staff.staffid}" id="sid"/>
            </p>
            <p>
                <h:outputLabel value="firstname :" for="firstname"/>
                <h:outputText value="#{staffBean.staff.firstname}" id="firstname"/>
            </p>
            <p>
                <h:outputLabel value="lastname :" for="lastname"/>
                <h:outputText value="#{staffBean.staff.lastname}" id="lastname"/>
            </p>
        </rich:panel>
    </a4j:outputPanel>
</rich:panel>
</body>
</ui:composition>

After deploy to Wildlfy, you should see as below result

Fig 09 List up data

Fig 10. Search Data


Reference

  1. Using Hibernate second level cache
  2. Hibernate Caching with Ehcache.



No comments:

Post a Comment

Feature Recently

Running Wildfly Application Server in Domain Mode

  Wildfly application server provides two modes of how to run application one wildfly application server. It is very simple if you run your ...

Most Views