Roi Ezra
Roi Ezra

Reputation: 506

Caching Java 8 Optional with Spring Cache

I have a method:

@Cacheable(key = "#jobId")
public Optional<JobInfo> getJobById(String jobId) {
    log.info("Querying for job " + jobId);
    counterService.increment("queryJobById");
    Job job = jobsRepository.findOne(jobId);
    if (job != null) {
        return Optional.of(createDTOFromJob(job));
    }
    return Optional.empty();
}

When I am trying to retrieve the cached item I am getting the following exception:

2016-01-18 00:01:10 ERROR [trace=,span=] http-nio-8021-exec-2 [dispatcherServlet]:182 - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.data.redis.serializer.SerializationException: Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [java.util.Optional]] with root cause java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [java.util.Optional]

Upvotes: 13

Views: 23737

Answers (3)

Ishan Ojha
Ishan Ojha

Reputation: 400

Just implement the Serializable interface in your DTO

@Document(collection = "document_name")
public class Document implements Serializable {

    private static final long serialVersionUID = 7156526077883281623L;

Upvotes: 14

steven
steven

Reputation: 560

Because your serialized object is not implement RedisSerializer, or you can extend class JdkSerializationRedisSerializer, which have implement RedisSerializer.

example code:

import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

public class YourDTOObject  extends JdkSerializationRedisSerializer implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = 1L;

....
}

More details and principle, please visit my blog

Upvotes: 5

Cyril
Cyril

Reputation: 2436

Spring supports caching Optional. The issue is your Redis serializer (JdkSerializationRedisSerializer probably). It uses Java based serialization which requires the classes to be Serializable. You can solve this by configuring the RedisCacheManager to use another serializer that doesn't have this limitation. For example you can use Kryo (com.esotericsoftware:kryo:3.0.3):

@Bean
RedisCacheManager redisCacheManager (RedisTemplate<Object, Object> redisOperations) {
    // redisOperations will be injected if it is configured as a bean or create it: new RedisTemplate()...
    redisOperations.setDefaultSerializer(new RedisSerializer<Object>() {
        //use a pool because kryo instances are not thread safe
        KryoPool kryoPool = new KryoPool.Builder(Kryo::new).build();

        @Override
        public byte[] serialize(Object o) throws SerializationException {
            ByteBufferOutput output = new ByteBufferOutput();
            Kryo kryo = kryoPool.borrow();
            try {
                kryo.writeClassAndObject(output, o);
            } finally {
                kryoPool.release(kryo);
                output.close();
            }

            return output.toBytes();
        }

        @Override
        public Object deserialize(byte[] bytes) throws SerializationException {
            if(bytes.length == 0) return null;

            Kryo kryo = kryoPool.borrow();
            Object o;
            try {
                o = kryo.readClassAndObject(new ByteBufferInput(bytes));
            } finally {
                kryoPool.release(kryo);
            }
            return o;
        }
    });

    RedisCacheManager redisCacheManager = new RedisCacheManager(redisOperations);
    redisCacheManager.setCachePrefix(new DefaultRedisCachePrefix("app"));
    redisCacheManager.setTransactionAware(true);
    return redisCacheManager;
}

Note that this is just an example, I didn't test this imeplementation. But I use the Kryo serializer in production in the same manner for redis caching with Spring.

Upvotes: 9

Related Questions