Spring Data JPA整合REST客户端Feign时: 分页查询的反序列化报错的问题

摘要:
feign.codec.Decode例外feign.codec。DecodeException:exist):行:列:规范=newExampleSpecification<(示例);类别<
Type definition error: [simple type, class org.springframework.data.domain.Page]; 
nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: 
Cannot construct instance of `org.springframework.data.domain.Page` (no Creators, like default construct, exist): 
abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: (PushbackInputStream); line: 1, column: 48] (through reference chain: 
 com.core.domain.dto.ResultDTO["data"]->com.trade.manager.rest.search.domain.dto.PCShopSearchProductDTO["products"]), feign.codec.DecodeException
feign.codec.DecodeException: 
Type definition error: 
[simple type, class org.springframework.data.domain.Page]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: 
Cannot construct instance of `org.springframework.data.domain.Page` (no Creators, like default construct, exist): 
abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: (PushbackInputStream); line: 1, column: 48] (through reference chain: 
 com.core.domain.dto.ResultDTO["data"]->com.trade.manager.rest.search.domain.dto.PCShopSearchProductDTO["products"])
    at feign.SynchronousMethodHandler.decode(SynchronousMethodHandler.java:180) ~[feign-core-10.1.0.jar!/:na]
    at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:140) ~[feign-core-10.1.0.jar!/:na]
    at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:78) ~[feign-core-10.1.0.jar!/:na]
    at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:103) ~[feign-core-10.1.0.jar!/:na]
    at com.sun.proxy.$Proxy161.searchProductShopPC(Unknown Source) ~[na:na]

Spring Data JPA的Page接口, 定义了分页的基本操作, 用起来很是方便. Page接口在SimpleJpaRepository中用得比较广泛, 例如: org.springframework.data.jpa.repository.support.SimpleJpaRepository#findAll

@Override  
public <S extends T> Page<S> findAll(Example<S> example, Pageable pageable) {  
    ExampleSpecification<S> spec = new ExampleSpecification<S>(example);  
    Class<S> probeType = example.getProbeType();  
    TypedQuery<S> query = getQuery(new ExampleSpecification<S>(example), probeType, pageable);  
    return pageable == null ? new PageImpl<S>(query.getResultList()) : readPage(query, probeType, pageable, spec);  
}  

该查询返回一个Page实例, 具体的实现是org.springframework.data.domain.PageImpl.

问题就出在这个PageImpl对象, 正常情况下没有任何问题, 但是如果这个对象通过Feign中转时, 就会出现无法反序列化的错误.

究其原因, 是PageImpl没有无参构造, 其超类Chunk也没有无参构造; 导致反序列化失败.

解决的方法有两种, 一是自定义反序列化, 比较麻烦.
另一种办法就是自定义Page, 放弃Spring自带的PageImpl, 这就解决了反序列化的问题. 笔者使用的是后一种方法.
这是笔者自定义的Page实现, 与Spring无关, 可以在任何项目中通用:

import java.io.Serializable;  
import java.util.ArrayList;  
import java.util.Collections;  
import java.util.Iterator;  
import java.util.List;  
  
/** 
 * Page operations. 
 * 
 * @auther rickgong@iteye.com on 2017/3/17. 
 * @see org.springframework.data.domain.Page 
 */  
public class Page<T> implements Iterable<T>, Serializable {  
    private static final long serialVersionUID = -3720998571176536865L;  
    private List<T> content = new ArrayList<>();  
    private long total;  
    private int pageNo;  
    private int pageSize;  
  
    public Page() {  
    }  
  
    public Page(List<T> content, long total, int pageNo, int pageSize) {  
        this.content = content;  
        this.total = total;  
        this.pageNo = pageNo;  
        this.pageSize = pageSize;  
    }  
  
    /** 
     * Returns if there is a previous page. 
     * 
     * @return if there is a previous page. 
     */  
    public boolean hasPrevious() {  
        return getPageNo() > 0;  
    }  
  
    /** 
     * Returns if there is a next page. 
     * 
     * @return if there is a next page. 
     */  
    public boolean hasNext() {  
        return getPageNo() + 1 < getTotalPage();  
    }  
  
    /** 
     * Returns whether the current page is the first one. 
     * 
     * @return whether the current page is the first one. 
     */  
    public boolean isFirst() {  
        return !hasPrevious();  
    }  
  
    /** 
     * Returns whether the current  page is the last one. 
     * 
     * @return whether the current  page is the last one. 
     */  
    boolean isLast() {  
        return !hasNext();  
    }  
  
    /** 
     * Returns the total amount of elements of all pages. 
     * 
     * @return the total amount of elements of all pages. 
     */  
    public long getTotal() {  
        return total;  
    }  
  
    public void setTotal(long total) {  
        this.total = total;  
    }  
  
    /** 
     * Returns the number of total pages. 
     * 
     * @return the number of total pages 
     */  
    public int getTotalPage() {  
        return getPageSize() == 0 ? 1 : (int) Math.ceil((double) total / (double) getPageSize());  
    }  
  
    /** 
     * Returns the page content as unmodifiable {@link List}. 
     * 
     * @return Returns the page content as unmodifiable {@link List} 
     */  
    public List<T> getContent() {  
        return Collections.unmodifiableList(content);  
    }  
  
    public void setContent(List<T> content) {  
        this.content = content;  
    }  
  
    /** 
     * Returns whether the current page has content. 
     * 
     * @return whether the current page has content. 
     */  
    public boolean hasContent() {  
        return getContentSize() > 0;  
    }  
  
    /** 
     * Returns the number of elements on current page. 
     * 
     * @return the number of elements on current page. 
     */  
    public int getContentSize() {  
        return content.size();  
    }  
  
    /** 
     * Returns the number of items of each page. 
     * 
     * @return the number of items of each page 
     */  
    public int getPageSize() {  
        return pageSize;  
    }  
  
    public void setPageSize(int pageSize) {  
        this.pageSize = pageSize;  
    }  
  
    /** 
     * Returns the number of current page. (Zero-based numbering.) 
     * 
     * @return the number of current page. 
     */  
    public int getPageNo() {  
        return pageNo;  
    }  
  
    /** 
     * Set the number of current page. (Zero-based numbering.) 
     */  
    public void setPageNo(int pageNo) {  
        this.pageNo = pageNo;  
    }  
  
    @Override  
    public Iterator<T> iterator() {  
        return getContent().iterator();  
    }  
} 

https://www.iteye.com/blog/rickgong-2363789

免责声明:文章转载自《Spring Data JPA整合REST客户端Feign时: 分页查询的反序列化报错的问题》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Python3.x:pdf2htmlEX(解析pdf)安装和使用homestead 入坑安装下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

如何实现table表格中的button按钮有加载中的效果

一、如何实现table表格中的button按钮有加载中的效果 效果:  前端代码: <el-table-column label="送货单信息" align="center" width="110"> <template slot-scope="scope"> <el-button slo...

Win2012R2(英文版)新增用户设置流程

场景:新建用户,和其他用户进行VPS隔离, 处理步骤: 1.登录管理员Administrator,按 Win+R调出命令框,输入命令: lusrmgr.msc ;  2.选择New User  3.输入要创建的用户名及密码,密码尽量复杂一些,不然无法创建; 取消 User must change password at next logon 勾选 Us...

JavaScript常用方法(转载)

只能是写限定的东西 代码如下:   只能是中文 <input onkeyup="value=value.replace(/[ -~]/g,'')" onkeydown="if(event.keyCode==13)event.keyCode=9">   只能是英文和数字.屏蔽了输入法 <input onkeydown="if(event....

【GPU编解码】GPU硬编码 (转)

一、OpenCV中的硬编码 OpenCV2.4.6中,已实现利用GPU进行写视频,编码过程由cv::gpu::VideoWriter_GPU完成,其示例程序如下。 1 int main(int argc, const char* argv[]) 2 { 3 if (argc != 2) 4 { 5 st...

VS2017打开VS2010项目报 “找不到*.xaml”错误

VS2017打开VS2010项目报 “找不到*.xaml”错误。详细如下: 未处理System.IO.IOExceptionMessage: “System.IO.IOException”类型的未经处理的异常在 PresentationFramework.dll 中发生 其他信息: 找不到资源“window1.xaml”。 解决方法: 修改.csproj工...

WPF 实现已绑定集合项目的移除时动画过渡

信不信由你,这个场景貌似没有官方的完美解决方案。我认为这个要求一点都不过分,花了很长时间 bai google du,就是没找到好的方案。这是我花了一天时间才搞通的一个方案,跟大家分享,虽然仍然不太完美,但是希望对大家有用。 对完美的憧憬 一个已绑定到 ItemsControl 的集合,是不能通过 ItemsControl.Items 属性来访问的,如果你...