Caching objects

Scalability is a major concern in web application development. Generally, most web traffic is focused on some special set of information. So, only those records are queried very often. If we can cache these records, then the performance and scalability of the system will increase immensely.

The Spring Framework provides support for adding caching into an existing Spring application. In this section, we'll work with EhCache, the most widely used caching solution. Download the latest EhCache JAR from the Maven repository; the URL to download version 2.7.2 is http://mvnrepository.com/artifact/net.sf.ehcache/ehcache/2.7.2.

Spring provides two annotations for caching: @Cacheable and @CacheEvict. These annotations allow methods to trigger cache population or cache eviction, respectively.

The @Cacheable annotation is used to identify a cacheable method, which means that for an annotate method the result is stored into the cache. Therefore, on subsequent invocations (with the same arguments), the value in the cache is returned without actually executing the method.

The cache abstraction allows the eviction of cache for removing stale or unused data from the cache. The @CacheEvict annotation demarcates the methods that perform cache eviction, that is, methods that act as triggers to remove data from the cache.

The following are the steps to build a cacheable application with EhCache:

  1. Create a serializable Employee POJO class in the com.packt.cache package to store the employee ID and name. The following is the class definition:
    public class Employee implements Serializable {
      private static final long serialVersionUID = 1L;
      private final String firstName, lastName, empId;
    
      public Employee(String empId, String fName, String lName) {
        this.firstName = fName;
        this.lastName = lName;
        this.empId = empId;
      }
      //Getter methods
    }
  2. Spring caching supports two storages: the ConcurrentMap and ehcache libraries. To configure caching, we need to configure a manager in the application context. The org.springframework.cache.ehcache.EhCacheCacheManager class manages ehcache. Then, we need to define a cache with a configurationLocation attribute. The configurationLocation attribute defines the configuration resource. The ehcache-specific configuration is read from the resource ehcache.xml.

    Create an applicationConext file under the com.packt.cache package with the following details:

    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:cache="http://www.springframework.org/schema/cache"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
    http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.1.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd ">
       <context:component-scan base-package="com.packt.cache" />
       <cache:annotation-driven/>
    
       <bean id="cacheManager" class="org.springframework.cache.
              ehcache.EhCacheCacheManager"
          p:cacheManager-ref="ehcache"/>
        <bean id="ehcache" class="org.springframework.cache.
        ehcache.EhCacheManagerFactoryBean"
          p:configLocation="classpath:com/packt/cache/ehcache.xml"/>
    </beans>

    The <cache:annotation-driven/> tag informs the Spring container that the caching and eviction is performed in annotated methods. We defined a cacheManager bean and then defined an ehcache bean. The ehcache bean's configLocation points to an ehcache.xml file. We'll create the file next.

  3. Create an XML file, ehcache.xml, under the com.packt.cache package and add the following cache configuration data:
    <ehcache>
        <diskStore path="java.io.tmpdir"/>
        <cache name="employee"
               maxElementsInMemory="100"
               eternal="false"
               timeToIdleSeconds="120"
               timeToLiveSeconds="120"
               overflowToDisk="true"
               maxElementsOnDisk="10000000"
               diskPersistent="false"
               diskExpiryThreadIntervalSeconds="120"
               memoryStoreEvictionPolicy="LRU"/>
    
    </ehcache>

    The XML configures many things. Cache is stored in memory, but memory has a limit, so we need to define maxElementsInMemory. EhCache needs to store data to disk when max elements in memory reaches the threshold limit. We provide diskStore for this purpose. The eviction policy is set as an LRU, but the most important thing is the cache name. The name employee will be used to access the cache configuration.

  4. Now, create a service to store the Employee objects in a HashMap. The following is the service:
    @Service
    public class EmployeeService {
        private final Map<String, Employee> employees = new ConcurrentHashMap<String, Employee>();
    
        @PostConstruct
        public void init() {
            saveEmployee (new Employee("101", "John", "Doe"));
            saveEmployee (new Employee("102", "Jack", "Russell"));
        }
    
        @Cacheable("employee")
        public Employee getEmployee(final String employeeId) {
          System.out.println(String.format("Loading a employee with id of : %s", employeeId));
          return employees.get(employeeId);
        }
    
        @CacheEvict(value = "employee", key = "#emp.empId")
        public void saveEmployee(final Employee emp) {
          System.out.println(String.format("Saving a emp with id of : %s", emp.getEmpId()));
          employees.put(emp.getEmpId(), emp);
        }
    }

    The getEmployee method is a cacheable method; it uses the cache employee. When the getEmployee method is invoked more than once with the same employee ID, the object is returned from the cache instead of the original method being invoked. The saveEmployee method is a CacheEvict method.

  5. Now, we'll examine caching. We'll call the getEmployee method twice; the first call will populate the cache and the subsequent call will be responded to by the cache. Create a JUnit test, CacheConfiguration, and add the following lines:
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations="classpath:com/packt/cache/applicationContext.xml")
    public class CacheConfiguration {
        @Autowired
         ApplicationContext context;
      
    @Test public void jobTest() throws Exception {
      EmployeeService employeeService = 
          (EmployeeService)context.getBean(EmployeeService.class);
      
      long time = System.currentTimeMillis();
      employeeService.getEmployee("101");
      System.out.println("time taken ="+(System.currentTimeMillis() - time));
        
      time = System.currentTimeMillis();
      employeeService.getEmployee("101");
      System.out.println("time taken to read from cache ="+(System.currentTimeMillis() - time));
    
      time = System.currentTimeMillis();
      employeeService.getEmployee("102");
      System.out.println("time taken 
            ="+(System.currentTimeMillis() - time));
      
      time = System.currentTimeMillis();
      employeeService.getEmployee("102");
      System.out.println("time taken to read from cache ="+(System.currentTimeMillis() - time));
    
      employeeService.saveEmployee(new Employee("1000", "Sujoy", "Acharya"));
        
      time = System.currentTimeMillis();
      employeeService.getEmployee("1000");
      System.out.println("time taken ="+(System.currentTimeMillis() - time));
        
      time = System.currentTimeMillis();
      employeeService.getEmployee("1000");
      System.out.println("time taken to read from cache ="+(System.currentTimeMillis() - time));
      }
      
    }

    Note that the getEmployee method is invoked twice for each employee, and we recorded the method execution time in milliseconds. You will find from the output that every second call is answered by the cache, as the first call prints Loading a employee with id of : 101 and then the next call doesn't print the message but prints the time taken to execute. You will also find that the time taken for the cached objects is zero or less than the method invocation time.

    The following screenshot shows the output:

    Caching objects
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.225.255.178