Guava Cache: How to handle null values

Viewed 7002

I've build a cache that returns a value in list format when you enter the parameters. If that value is not in the cache, it goes to the database and retrieves it, putting it in the cache for future reference:

private ProfileDAO profileDAO;
private String[] temp;
    private LoadingCache<String, List<Profile>> loadingCache = CacheBuilder.newBuilder()
            .refreshAfterWrite(5, TimeUnit.MINUTES)
            .expireAfterWrite(5, TimeUnit.MINUTES)
            .build(
                    new CacheLoader<String, List<Profile>>() {
                        @Override
                        public List<Profile> load(String key) throws Exception {
                            logger.info("Running method to retrieve from database");
                            temp = key.split("\\|");
                            String instance = temp[0];
                            String name = temp[1];
List<Profile> profiles= profileDAO.getProfileByFields(id, name);
                            if (profiles.isEmpty()) {
                                List<Profile> nullValue = new ArrayList<Profile>();
                                logger.info("Unable to find a value.");
                                return nullValue;
                            }
                            logger.info("Found a value");
                            return profileDAO.getProfileByFields(id, name);
                        }
                    }
            );

public List<Profile> getProfileByFields(String id, String name) throws Exception {
        String key = id.toLowerCase() + "|" + name.toLowerCase()
        return loadingCache.get(key);
    }

This seems to work fine, but it does not take into account null values. If I look for an entry that does not exist, I get an exception for :

com.google.common.cache.CacheLoader$InvalidCacheLoadException: CacheLoader returned null for key A01|Peter

I'd like to simply return an empty List(Profile) if there is no match in the database, but my if statement has failed. Is there any way around this error for this particular use case?

3 Answers

Though this feels a bit hacky, I think it's a more complete solution (Suresh's answer only really applies to collections).

Define a singleton object that will represent null, and insert that value into the cache instead of null (converting to null at retrieval time):

class MyDAO
{
    static final Object NULL = new Object();

    LoadingCache<String,Object> cache = CacheBuilder.newBuilder()
        .build( new CacheLoader<>()
        {
            public Object load( String key )
            {
                Object value = database.get( key );
                if( value == null )
                    return NULL;
                return value;
            }
        });

    Object get( String key )
    {
        Object value = cache.get( key );
        if( value == NULL ) // use '==' to compare object references
            return null;
        return value;
    }
}

I believe this approach is preferable, in terms of efficiency, to any involving the use of exceptions.

Make changes in your code to check first profiles is null or not as(using profiles == null ...) :

private ProfileDAO profileDAO;
private String[] temp;
    private LoadingCache<String, List<Profile>> loadingCache = CacheBuilder.newBuilder()
            .refreshAfterWrite(5, TimeUnit.MINUTES)
            .expireAfterWrite(5, TimeUnit.MINUTES)
            .build(
                    new CacheLoader<String, List<Profile>>() {
                        @Override
                        public List<Profile> load(String key) throws Exception {
                            logger.info("Running method to retrieve from database");
                            temp = key.split("\\|");
                            String instance = temp[0];
                            String name = temp[1];
List<Profile> profiles= profileDAO.getProfileByFields(id, name);
                            if (profiles == null || profiles.isEmpty()) {
                                List<Profile> nullValue = new ArrayList<Profile>();
                                logger.info("Unable to find a value.");
                                return nullValue;
                            }
                            logger.info("Found a value");
                            return profileDAO.getProfileByFields(id, name);
                        }
                    }
            );

public List<Profile> getProfileByFields(String id, String name) throws Exception {
        String key = id.toLowerCase() + "|" + name.toLowerCase()
        return loadingCache.get(key);
    }

Please check this code is working for you null values or not..

Using Optional class Optional<Object> as the cache value is the easiest and cleanest way to do it.

Related