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


    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

  • 相关阅读:
    零拷贝报文捕获平台
    Table of Contents ---BCM
    bcm cmd
    Linux常用性能调优工具索引
    Vue params传值的坑
    安装了新的angular版本后无法运行老的angular版本项目
    后端返回的数据与前端console.log数据不一致问题
    门户页跳转页面 跳转指定的页面 接口会变成路由去显示 而不是显示组件
    配置git ssh 密钥
    grafana环境变量
  • 原文地址:https://www.cnblogs.com/softidea/p/11763632.html
Copyright © 2020-2023  润新知