一、该问题的重现步骤是什么?
当查询数据库时使用分页查询 并且使用缓存:
@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();
问题解决了。
(如果此问题不予解决,我就先本地重写这个类了。)
zhx1994 - BladeX技术支持: 你这个报错是序列化和反序列化不一致导致的啊。
-----------------------------------------------------------------------------
回复:不是的。
@Cacheable 可选的序列化方式有三种:
默认使用的是第一种:
org.springblade.core.redis.config.BladeRedisProperties.SerializerType#ProtoStuff
使用 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)
报错截图:
所以,解决方案就是重写 Page 类。
1、在业务代码中创建类:com.baomidou.mybatisplus.extension.plugins.pagination.Page (注意,包名也需要相同)
2、复制原Page类的所有代码到你自己创建的Page类中。
3、并且修改 records 变量为: (其他代码都不变,只修改这一行)
/** * 查询数据列表 */ protected List<T> records = new ArrayList();
你这个报错是序列化和反序列化不一致导致的啊。
这个大概是因为ipage里面的records是abstractList,你可以去看下他的add,默认就是直接抛异常
把返回类型改成String就可以了
扫一扫访问 Blade技术社区 移动端