spring-boot version: 3.2.0

public interface CommentRepository extends CrudRepository<Comment, Long> {

    /**
     * select ... from comments where
     * belong_to = ? and id < ? and reply_to is null
     * order by id desc
     * limit ?
     *
     * @param belongTo the id of the object which the comments belong to
     * @param id       the id of the earliest comment in the previous page
     * @return page of comments
     */
    @Cacheable("comments-main")
    List<Comment> findByBelongToAndIdLessThanAndReplyToNullOrderByIdDesc(Long belongTo, Long id, Limit limit);

record:

@Table("comments")
@Data
@NoArgsConstructor
public class Comment implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;

    @Id
    private long id;
    @Column("user_id")
    @JsonProperty("user_id")
    private long userId;
    private String content;

    @Column("reply_to")
    @JsonProperty("reply_to")
    private Long replyTo;
    @Column("belong_to")
    @JsonProperty("belong_to")
    private long belongTo;

    @Column("created_at")
    @JsonProperty("created_at")
    @ReadOnlyProperty
    private LocalDateTime createdAt;

    @Column("reply_count")
    @JsonProperty("reply_count")
    @ReadOnlyProperty
    private int replyCount;

    @Column("like_count")
    @JsonProperty("like_count")
    @ReadOnlyProperty
    private int likeCount;


    // fields which are not in the table
    @Transient
    @JsonProperty("reply_comments")
    private List<Comment> replyComments;
    @Transient
    private boolean voted;
    @Transient
    private User user;
}

appplication.yaml for cache and redis:

spring:
  threads:
    virtual:
      enabled: true
  datasource:
    username: ${DB_USER:v1}
    password: ${DB_PASSWORD:ABCDEF}
    url: jdbc:mysql://${DB_HOST:db}:${DB_PORT:3306}/${DB_NAME:comments}?${DB_OPTIONS:useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true}
    driver-class-name: com.mysql.cj.jdbc.Driver
  cache:
    cache-names: "comments-main,comments-reply"
    redis:
      time-to-live: "600s"
  data:
    redis:
      database: 0
      host: ${REDIS_HOST:redis}
      port: ${REDIS_PORT:6379}
      timeout: 60000
logging:
  level:
    org:
      springframework:
        jdbc: DEBUG

container image build command:

mvn -Pnative spring-boot:build-image -DskipTests

I tried using @RegisterReflectionForBinding({Principal.class, ArrayList.class}) but it didn't make the deserialization of the cache work, the error is the same. this annotation only makes the deserialization of the Principal work in the jackson objectmapper created by me.


it works on serialization for caching data, but fails to deserialize. The exception will be thrown on the second call to this method with the cache annotation. log:

comments-app-1      | 2023-11-28T05:40:00.922Z ERROR 1 --- [omcat-handler-6] [                                                 ] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.data.redis.serializer.SerializationException: Cannot deserialize] with root cause
comments-app-1      | 
comments-app-1      | java.io.InvalidClassException: java.util.ArrayList; no valid constructor
comments-app-1      |   at java.base@21/java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:173) ~[na:na]
comments-app-1      |   at java.base@21/java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:792) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:na]
comments-app-1      |   at java.base@21/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2253) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:na]
comments-app-1      |   at java.base@21/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1762) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:na]
comments-app-1      |   at java.base@21/java.io.ObjectInputStream.readObject(ObjectInputStream.java:540) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:na]
comments-app-1      |   at java.base@21/java.io.ObjectInputStream.readObject(ObjectInputStream.java:498) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:na]
comments-app-1      |   at org.springframework.core.serializer.DefaultDeserializer.deserialize(DefaultDeserializer.java:71) ~[na:na]
comments-app-1      |   at org.springframework.core.serializer.support.DeserializingConverter.convert(DeserializingConverter.java:75) ~[na:na]
comments-app-1      |   at org.springframework.core.serializer.support.DeserializingConverter.convert(DeserializingConverter.java:37) ~[na:na]
comments-app-1      |   at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.deserialize(JdkSerializationRedisSerializer.java:106) ~[na:na]
comments-app-1      |   at org.springframework.data.redis.serializer.DefaultRedisElementReader.read(DefaultRedisElementReader.java:46) ~[na:na]
comments-app-1      |   at org.springframework.data.redis.serializer.RedisSerializationContext$SerializationPair.read(RedisSerializationContext.java:277) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:3.2.0]
comments-app-1      |   at org.springframework.data.redis.cache.RedisCache.deserializeCacheValue(RedisCache.java:392) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:3.2.0]
comments-app-1      |   at org.springframework.data.redis.cache.RedisCache.lookup(RedisCache.java:210) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:3.2.0]
comments-app-1      |   at org.springframework.cache.support.AbstractValueAdaptingCache.get(AbstractValueAdaptingCache.java:58) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.springframework.cache.interceptor.AbstractCacheInvoker.doGet(AbstractCacheInvoker.java:73) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.springframework.cache.interceptor.CacheAspectSupport.findInCaches(CacheAspectSupport.java:494) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.springframework.cache.interceptor.CacheAspectSupport.findCachedValue(CacheAspectSupport.java:457) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:407) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:371) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:74) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:249) ~[na:na]
comments-app-1      |   at jdk.proxy4/jdk.proxy4.$Proxy48.findByBelongToAndIdLessThanAndReplyToNullOrderByIdDesc(Unknown Source) ~[na:na]
comments-app-1      |   at io.sixwaaaay.sharingcomment.service.CommentService.getMainCommentList(CommentService.java:52) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:na]
comments-app-1      |   at java.base@21/java.lang.reflect.Method.invoke(Method.java:580) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:na]
comments-app-1      |   at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:352) ~[na:na]
comments-app-1      |   at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:713) ~[na:na]
comments-app-1      |   at io.sixwaaaay.sharingcomment.service.CommentService$$SpringCGLIB$$0.getMainCommentList(<generated>) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:na]
comments-app-1      |   at io.sixwaaaay.sharingcomment.controller.CommentController.getMainCommentList(CommentController.java:52) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:na]
comments-app-1      |   at java.base@21/java.lang.reflect.Method.invoke(Method.java:580) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:na]
comments-app-1      |   at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:254) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:182) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:917) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:829) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.0]
comments-app-1      |   at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.0]
comments-app-1      |   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205) ~[na:na]
comments-app-1      |   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[na:na]
comments-app-1      |   at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:10.1.16]
comments-app-1      |   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[na:na]
comments-app-1      |   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[na:na]
comments-app-1      |   at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[na:na]
comments-app-1      |   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[na:na]
comments-app-1      |   at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[na:na]
comments-app-1      |   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[na:na]
comments-app-1      |   at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:109) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[na:na]
comments-app-1      |   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[na:na]
comments-app-1      |   at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:6.1.1]
comments-app-1      |   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[na:na]
comments-app-1      |   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[na:na]
comments-app-1      |   at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[na:na]
comments-app-1      |   at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[na:na]
comments-app-1      |   at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:10.1.16]
comments-app-1      |   at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) ~[na:na]
comments-app-1      |   at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:10.1.16]
comments-app-1      |   at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[na:na]
comments-app-1      |   at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:340) ~[na:na]
comments-app-1      |   at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391) ~[na:na]
comments-app-1      |   at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:10.1.16]
comments-app-1      |   at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896) ~[na:na]
comments-app-1      |   at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744) ~[na:na]
comments-app-1      |   at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:10.1.16]
comments-app-1      |   at java.base@21/java.lang.VirtualThread.runWith(VirtualThread.java:341) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:na]
comments-app-1      |   at java.base@21/java.lang.VirtualThread.run(VirtualThread.java:311) ~[io.sixwaaaay.sharingcomment.SharingCommentApplication:na]
comments-app-1      |   at java.base@21/java.lang.VirtualThread$VThreadContinuation$1.run(VirtualThread.java:192) ~[na:na]

Comment From: wilkinsona

You may need to provide some reflection or serialization hints so that Graal can create the ArrayList instance during deserialisation. A RuntimeHintsRegistrar should allow you to do that. Ideally, this would be done automatically by Spring Data Redis during AOT processing of your application. You may want to open an issue with them to explore that possibility.