miércoles, 18 de diciembre de 2013

Ehcache Cache Search API Fundamentals

Introduction

In this entry I want to give out an excerpt of my book Instant Effective Caching with Ehcache. I have chosen the Ehcache Search API chapter so we can analyze a very simple example about its internals and a practical implementation. If you would like to purchase a copy of the book please follow the links:

Amazon:
http://www.amazon.com/Instant-Effective-Caching-Ehcache-Daniel/

PacktPub:
http://www.packtpub.com/effective-caching-with-ehcache/book


Getting Ready

EhCache search API allows us to query and search for elements that are already cached. This is achieved thanks to the ability to use indexing on keys and/or values pertaining to the object being cached. Ehcache provides queries that let us create arbitrary complex searches based on conditions, making this tool indispensable for professional caching.

To get the most of this tutorial download this book's recipe source code from github (or optionally clone the repository to get all the book's recipes source code) here:

https://github.com/danielwind/EhCache-Effective-Caching/tree/master/Recipe%206


How to do it

I will assume that you are using Maven to build your Java Project. If this is not the case, then you just need to make sure you are linking the following library jars in your project:

Step 1: Add Ehcache and SLF4j dependencies in your POM.xml

<dependency>

 <groupid>org.slf4j</groupid>

   <artifactid>slf4j-log4j12</artifactid>

 <version>1.7.2</version>

</dependency>

<dependency>

   <groupid>net.sf.ehcache</groupid>

   <artifactid>ehcache</artifactid>

   <version>2.6.0</version>

   <type>pom</type>

</dependency>


Step 2: Add to your ehcache.xml the searchable tag (to enable search) and search attributes to map your POJO getters:

<cache name="employeeCache" maxElementsInMemory="100" eternal="true" overflowToDisk="false">

   ...


   <!-- Adding Ehcache searchable capabilities -->

   <searchable>

       <searchAttribute name="firstname" expression="value.getFirstName()"/>

       <searchAttribute name="profile" expression="value.getProfile()"/>      
       
       <searchAttribute name="social" expression="value.getSocialNumber()"/>
       
       <!-- Profile Children Nodes -->
       <searchAttribute name="department" 
                       expression="value.getProfile().getDepartment()"/>
       
       <searchAttribute name="role" 
                       expression="value.getProfile().getRole()"/>
     
       <searchAttribute name="salary"
                      expression="value.getProfile().getSalary()"/>
       
       <!-- Adding a custom attribute extractor for performance reason -->
       <searchAttribute name="lastname"
                        class="com.foo.cache.LastNameAttributeExtractor"/>
   </searchable>


</cache>



Step 3: Create a Map to hold references to the search attributes defined previously in step 2:

 /**
 * load the attributes (as defined in ehcache.xml)
 * and store them in the reusable Cache Attributes Map
 **/   

HashMap> cacheAttributes = new HashMap>();

cacheAttributes.put("lastname", cache.getSearchAttribute("lastname"));
cacheAttributes.put("profile", cache.getSearchAttribute("profile"));
cacheAttributes.put("department", cache.getSearchAttribute("department"));
cacheAttributes.put("role", cache.getSearchAttribute("role"));
cacheAttributes.put("salary", cache.getSearchAttribute("salary"));
cacheAttributes.put("social", cache.getSearchAttribute("social")); 


Step 4: (Optional) Create an Attribute Extractor to improve performance:

...
import net.sf.ehcache.search.attribute.AttributeExtractor;
import net.sf.ehcache.search.attribute.AttributeExtractorException;

public final class LastNameAttributeExtractor implements AttributeExtractor {

 private static final long serialVersionUID = 1L;

 public Object attributeFor(Element element) throws AttributeExtractorException {

  return attributeFor(element);
 }

 public Object attributeFor(Element element, String attributeName) {

  return ((YourPojo) element.getValue()).getLastName();
 }

} 


Step 5: Implement a method that uses Ehcache Search API:

public List searchEngineers() {

 log.info("Searching all employees whose role is: " + Role.ENGINEER);

 List employees = new ArrayList();

 //create Ehcache Search API Query
 Query query = cache.createQuery();

 //cast attribute from HashMap
 Attribute attribute = (Attribute) cacheAttributes.get("role");

 //add query criteria like: 'select {key, value} from Employee where role = ${@param role}'
 query.addCriteria(attribute.eq(Role.ENGINEER));

 //get the results list and loop on them
 Results results = query.execute();

 for(Result result : results.all()){
  
  log.info("Key: " + result.getKey());
  log.info("Value: " + result.getValue());
  log.info("Value Class: " + result.getValue().getClass());

  Employee employee = (Employee) result.getValue();
  employees.add(employee);
 }

 return employees;
}  


How It Works

Ehcache provides a simple to use Search API that allows us to retrieve objects from cache layer based on conditions and other criteria. The implementation resembles that one of simple JDBC – SQL queries.

  1. We start by editing the Ehcache configuration by adding the searchable directive. This tells Ehcache that for that particular cache we want it to be searchable.

  2. We then define the Search Attributes. These are index mappings that tell Ehcache what attribute of your object you want to be searchable and a name to refer to it (always use “value” as your object reference).

  3. Now, in our code we need to create a map to hold attributes (not strictly necessary but a good practice and convenient way to access the attributes in an ordered way).

  4.  Finally we create methods (at your convenience) that hold the search queries logic.


Optionally, if your cache object layer is complex, you will want to implement a custom accessor that can help you to increase performance while lookups. This can be easily achieved by implementing the Ehcache AttributeExtractor interface and redefining the behavior you desire. The procedure is just like if you were defining an index in a SQL table.

You can find a fully working sample project in my github repository (please refer to the top of this entry for details on how to get it).

Thanks,

No hay comentarios:

Publicar un comentario