分页查询 + Redis缓存 + 缓存中有值时,报错。

Blade 未结 3 981
anxiaole
anxiaole 剑童 2022-01-29 15:20

一、该问题的重现步骤是什么?

  1. 当查询数据库时使用分页查询 并且使用缓存:  

@Cacheable(cacheNames = "my-service-web", key = "#root.method.name + ':' + #root.args[0] + ':' + #root.args[1]")
public IPage<User> selectUserByUserName(String userName, Query query) {
    IPage<User> page = Condition.getPage(query);
    page.setRecords(userMapper.selectByMobile(page, userName));
    return page;
}

2、此时,如果缓存中没有,就会查询数据库,并保存到缓存中。(缓存中没有值时,不会报错。)

3、但是:当第二次调用此方法,此时缓存中有值,就会报错。

4、注意:必须满足三个条件:

 - 使用分页查询     返回值为IPage 

 - 使用缓存 @Cacheable    保存到redis中。(如果缓存是本地jvm内存没有试过)

 - 当redis缓存中有数据时

满足以上三个条件,就一定会报错。



二、你期待的结果是什么?实际看到的又是什么?

       期待:使用分页查询 并且 当命中redis缓存数据时,可以正常从缓存中获取分页数据。


三、你正在使用的是什么产品,什么版本?在什么操作系统上?

       bladex 商业授权版 2.8.1.RELEASE   

       本地和linux服务器上均出现此问题,无论是什么环境,只要缓存中有分页数据,就报错。

       


四、请提供详细的错误堆栈信息,这很重要。

java.lang.UnsupportedOperationException
	at java.util.AbstractList.add(AbstractList.java:148)
	at java.util.AbstractList.add(AbstractList.java:108)
	at io.protostuff.runtime.RuntimeRepeatedFieldFactory$5.setValue(RuntimeRepeatedFieldFactory.java:311)
	at io.protostuff.runtime.PolymorphicSchemaFactories$11$1.setValue(PolymorphicSchemaFactories.java:247)
	at io.protostuff.runtime.ObjectSchema.mergeFrom(ObjectSchema.java:350)
	at io.protostuff.ByteArrayInput.mergeObjectEncodedAsGroup(ByteArrayInput.java:518)
	at io.protostuff.ByteArrayInput.mergeObject(ByteArrayInput.java:490)
	at io.protostuff.runtime.RuntimeRepeatedFieldFactory$5.mergeFrom(RuntimeRepeatedFieldFactory.java:269)
	at io.protostuff.runtime.RuntimeSchema.mergeFrom(RuntimeSchema.java:466)
	at io.protostuff.runtime.ObjectSchema.readObjectFrom(ObjectSchema.java:693)
	at io.protostuff.runtime.ObjectSchema.mergeFrom(ObjectSchema.java:350)
	at io.protostuff.ByteArrayInput.mergeObjectEncodedAsGroup(ByteArrayInput.java:518)
	at io.protostuff.ByteArrayInput.mergeObject(ByteArrayInput.java:490)
	at io.protostuff.runtime.RuntimeUnsafeFieldFactory$15$1.mergeFrom(RuntimeUnsafeFieldFactory.java:1217)
	at io.protostuff.runtime.RuntimeSchema.mergeFrom(RuntimeSchema.java:466)
	at io.protostuff.IOUtil.mergeFrom(IOUtil.java:45)
	at io.protostuff.ProtostuffIOUtil.mergeFrom(ProtostuffIOUtil.java:104)
	at org.springblade.core.redis.serializer.ProtoStuffSerializer.deserialize(ProtoStuffSerializer.java:59)
	at org.springframework.data.redis.serializer.DefaultRedisElementReader.read(DefaultRedisElementReader.java:48)
	at org.springframework.data.redis.serializer.RedisSerializationContext$SerializationPair.read(RedisSerializationContext.java:272)
	at org.springframework.data.redis.cache.RedisCache.deserializeCacheValue(RedisCache.java:260)
	at org.springframework.data.redis.cache.RedisCache.lookup(RedisCache.java:94)
	at org.springframework.cache.support.AbstractValueAdaptingCache.get(AbstractValueAdaptingCache.java:58)
	at org.springframework.cache.interceptor.AbstractCacheInvoker.doGet(AbstractCacheInvoker.java:73)
	at org.springframework.cache.interceptor.CacheAspectSupport.findInCaches(CacheAspectSupport.java:571)
	at org.springframework.cache.interceptor.CacheAspectSupport.findCachedItem(CacheAspectSupport.java:536)
	at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:402)
	at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:346)
	at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
	at com.mam.wind.service.WindFundService$$EnhancerBySpringCGLIB$$1b5ff5e7.selectFundAnnouncementPage(<generated>)
	at com.mam.wind.service.WindFundServiceTest.selectFundAnnouncement(WindFundServiceTest.java:84)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
	at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
	at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
	at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
	at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:221)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)


五、若有更多详细信息,请在下面提供。

      具体原因是:com.baomidou.mybatisplus.extension.plugins.pagination.Page#records 变量,默认值为: Collections.emptyList()  导致的:

/**
 * 查询数据列表
 */
protected List<T> records = Collections.emptyList();


修改方案:

本地重写 com.baomidou.mybatisplus.extension.plugins.pagination.Page   类,并且修改 records 变量为:

/**
 * 查询数据列表
 */
protected List<T> records = new ArrayList();

问题解决了。

(如果此问题不予解决,我就先本地重写这个类了。)

3条回答
  • 2022-02-25 17:40

    zhx1994 - BladeX技术支持:  你这个报错是序列化和反序列化不一致导致的啊。

    -----------------------------------------------------------------------------

    回复:不是的。


    @Cacheable 可选的序列化方式有三种:

    image.png

    默认使用的是第一种:

    org.springblade.core.redis.config.BladeRedisProperties.SerializerType#ProtoStuff


    image.png


    使用  ProtoStuff 序列化方式时,只要反序列化的是 com.baomidou.mybatisplus.extension.plugins.pagination.Page    这个类,就会报错.


    写个main方法,验证一下:

    public static void main(String[] args) {
       HashMap<String, Object> map = new HashMap<>();
       map.put("name", "zhangsan");
       map.put("age", 2);

       Query query = new Query();
       query.setSize(10);
       query.setCurrent(1);

       IPage<HashMap<String,Object>> page = Condition.getPage(query);
       List list = new ArrayList<>();
       list.add(map);
       page.setRecords(list);
       ProtoStuffSerializer protoStuffSerializer = new ProtoStuffSerializer();


       // 直接序列化、反序列化 list ,不报错.
       byte[] serialize1 = protoStuffSerializer.serialize(list);
       Object deserialize1 = protoStuffSerializer.deserialize(serialize1);
       System.out.println(JSONUtil.toJsonPrettyStr(deserialize1));




       byte[] serialize = protoStuffSerializer.serialize(page);
       // 反序列化 page 对象,则报错。。
       Object deserialize = protoStuffSerializer.deserialize(serialize);
       System.out.println(JSONUtil.toJsonPrettyStr(deserialize));
    }

    控制台输出:

    [
       {
           "name": "zhangsan",
           "age": 2
       }
    ]
    Exception in thread "main" java.lang.UnsupportedOperationException
    at java.util.AbstractList.add(AbstractList.java:148)
    at java.util.AbstractList.add(AbstractList.java:108)
    at io.protostuff.runtime.RuntimeRepeatedFieldFactory$5.setValue(RuntimeRepeatedFieldFactory.java:311)
    at io.protostuff.runtime.PolymorphicSchemaFactories$11$1.setValue(PolymorphicSchemaFactories.java:247)
    at io.protostuff.runtime.ObjectSchema.mergeFrom(ObjectSchema.java:350)
    at io.protostuff.ByteArrayInput.mergeObjectEncodedAsGroup(ByteArrayInput.java:518)
    at io.protostuff.ByteArrayInput.mergeObject(ByteArrayInput.java:490)
    at io.protostuff.runtime.RuntimeRepeatedFieldFactory$5.mergeFrom(RuntimeRepeatedFieldFactory.java:269)
    at io.protostuff.runtime.RuntimeSchema.mergeFrom(RuntimeSchema.java:466)
    at io.protostuff.runtime.ObjectSchema.readObjectFrom(ObjectSchema.java:693)
    at io.protostuff.runtime.ObjectSchema.mergeFrom(ObjectSchema.java:350)
    at io.protostuff.ByteArrayInput.mergeObjectEncodedAsGroup(ByteArrayInput.java:518)
    at io.protostuff.ByteArrayInput.mergeObject(ByteArrayInput.java:490)
    at io.protostuff.runtime.RuntimeUnsafeFieldFactory$15$1.mergeFrom(RuntimeUnsafeFieldFactory.java:1217)
    at io.protostuff.runtime.RuntimeSchema.mergeFrom(RuntimeSchema.java:466)
    at io.protostuff.IOUtil.mergeFrom(IOUtil.java:45)
    at io.protostuff.ProtostuffIOUtil.mergeFrom(ProtostuffIOUtil.java:104)
    at org.springblade.core.redis.serializer.ProtoStuffSerializer.deserialize(ProtoStuffSerializer.java:59)
    at com.mam.wind.service.IPageUseProtoStuffSerializerErrorTest.main(IPageUseProtoStuffSerializerErrorTest.java:55)

    报错截图:

    image.png




    所以,解决方案就是重写 Page 类。


    1、在业务代码中创建类:com.baomidou.mybatisplus.extension.plugins.pagination.Page (注意,包名也需要相同)

    2、复制原Page类的所有代码到你自己创建的Page类中。

    3、并且修改 records 变量为:   (其他代码都不变,只修改这一行)

    /** 
     * 查询数据列表 
     */
    protected List<T> records = new ArrayList();


    1 讨论(1)
  • 你这个报错是序列化和反序列化不一致导致的啊。

    0 讨论(1)
  • 2023-06-08 16:17

    这个大概是因为ipage里面的records是abstractList,你可以去看下他的add,默认就是直接抛异常

    0 讨论(0)
提交回复