Saturday, 22 January 2011

Second Level Caching

JPA has two levels of caching. The first level of caching is the persistence context (which can be either transaction scoped or extended.), and the second level of caching, introduced in JPA 2.0, sites in between the entity manager and the database.

With second level caching entities not found in the persistence context will be loaded from the second level cache, and if not found there, from the database. The ideal type of entity to live in the second level cache are those that are rarely updated, or those that are constantly read.

To mark an entity as requiring caching, you can use the @Cacheable annotation as below:

@Entity
@Cacheable(true)
public class Person {

    @Id @GeneratedValue
    private Long id;
    private String name;

    // etc ...
}

To override the provider-specific defaults for managing cached entities, you can set the shared-cache-mode value in the persistence.xml. The possible values are below:
  • ALL- all entities are cached
  • DISABLE_SELECTIVE - all entities cached apart from those with the annotation @Cacheable(false)
  • ENABLE_SELECTIVE - only entities with the annotation @Cacheable(true) are cached
  • NONE - no caching for the persistence unit
  • UNSPECIFIED - provider-specific default
JPA 2.0 also introdcued the Cache interface which can be used to evict or invalidate entities in the second level cache. It can claso be used to check whether ot not an entity exisits in the cache as well.

Finally, you cannot implement a caching strategy without also considering a locking strategy (but that's for another post!)

Friday, 21 January 2011

CriteriaBuilder and Dynamic Queries in JPA 2.0

A major new feature of Java EE 6 is JPA 2.0 and in particular the addition of the Criteria API which provides the ability to dynamically construct object-based queries.

This resolves some of the problems which arise when building dynamic native queries. The below example shows how to find customer entities with two search parameters:

public List<CustomerEntity> findCustomers(
    final String firstName, final String surname) {

    StringBuilder queryBuilder = new StringBuilder(
        "select c from Customer where ");
    List<String> paramList = new ArrayList<String>();
    paramList.add(" upper(c.firstName) like '%?%'"
        .replace("?", firstName.toUpperCase()));
    paramList.add(" upper(c.surname) like '%?%'"
        .replace("?", surname.toUpperCase()));

    Iterator itr = paramList.iterator();
    while(itr.hasNext()) {
        queryBuilder.append(itr.next());
        if (itr.hasNext()) {
            queryBuilder.append(" and ");
        }
    }

    final Query query = entityManager.createNativeQuery(
        queryBuilder.toString());

    List<Object> resultList = (List<Object>)query.getResultList();

    // iterate, cast, populate and return a list
}


The problem with the above is that it is not type safe and involves iterating over a List of Object where those Objects are themselves Object arrays. Also should Customer contain any child elements, these would have to be retrieved in a separate call.

Using the CriteriaBuilder, the same results can be achieved as shown below:

public List<CustomerEntity> findCustomers(final String firstName, final String surname) {

    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    CriteriaQuery<CustomerEntity> query = builder.createQuery(CustomerEntity.class);
    Root<CustomerEntity> cust = query.from(CustomerEntity.class);
    query.select(cust);

    List<Predicate> predicateList = new ArrayList<Predicate>();

    Predicate firstNamePredicate, surnamePredicate;

    if ((firstName != null) && (!(firstName.isEmpty()))) {
        firstNamePredicate = builder.like(
            builder.upper(cust.<String>get("firstName")), "%"+firstName.toUpperCase()+"%");
        predicateList.add(firstNamePredicate);
    }

    if ((surname != null) && (!(surname.isEmpty()))) {
        surnamePredicate = builder.like(
            builder.upper(cust.<String>get("surname")), "%"+surname.toUpperCase()+"%");
        predicateList.add(surnamePredicate);
    }

    Predicate[] predicates = new Predicate[predicateList.size()];
    PredicateList.toArray(predicates);
    query.where(predicates);

    return entityManager.createQuery(query).getResultList();
} 


There is some type safety in the above but it can be furthered tied down by using the metamodel class for the entity, by using the metamodel class's public static members instead of text strings for the entity's attributes. The code would now look like this:

    firstNamePredicate = builder.like(
        builder.upper(cust.get(CustomerEntity_.firstName)),
            "%"+firstName.toUpperCase()+"%");

    surnamePredicate = builder.like(
        builder.upper(cust.get(CustomerEntity_.surname)), 
            "%"+surname.toUpperCase()+"%"); 


Having built metamodel classes using Maven, it's questionable whether it's a worthwhile exercise as any mistakes in the text based approach to finding attribute names should be flagged up by comprehensive unit testing.

Java Blogging and Tweeting

The idea behind this blog is to remind myself of all the useful bits of code I come across whilst working on projects and contracts. On numerous occasions, I've spent hours resolving problems on one contract only to hit the same problems on another so by putting nuggets of Java code up on the web, I should have no excuse in spending hours resolving the same problems I've encountered before. It'll also be retrospective and not just about problems I come across on current contracts, so if I can remember useful stuff from the past then I'll post about those too.

The posts may stray into the realms of build tools like Maven, test frameworks like Mockito, application servers like Glassfish and JBoss, and platforms like Android but in the main it'll be about Core and Enterprise Java.

As for the Twiiter account, there will be tweets about new blog posts plus me writing down all those daft thoughts that enter your head whilst staring out of the window at work. Also in amongst the tweets will the odd football rant about the ups and downs of Swansea City !