Spring data JPA只查询部分字段问题及解决

 更新时间:2024年08月12日 09:35:03   作者:北极象  
这篇文章主要介绍了Spring data JPA只查询部分字段问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

背景

在JPA查询中,有时只需要查部分字段,这时jpa repository查出的是map,无法映射到Entity类。

会提示错误:

org.springframework.core.convert.ConverterNotFoundException: 
No converter found capable of converting from type 
[org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type

网上搜索有多种解决方案。这里列举一下。

经过验证,本人采取了第一种方案,证明是可行的。

JPA 2.1以上的解决办法

实体中增加named query和result map

@SqlResultSetMapping(name = "EBookInfo",
        classes = @ConstructorResult(targetClass = EBookInfo.class,
                columns = {
                        @ColumnResult(name = "book_id", type = Long.class),
                        @ColumnResult(name = "book_name", type = String.class),
                        @ColumnResult(name = "file_type", type = String.class)
                }))
@NamedNativeQuery(name = "listExpressEbooks",
        query = "select book_id, book_name, file_type from ebook order by update_date desc",
        resultSetMapping = "EBookInfo")
@Entity
@Table(name = "ebook")
public class Ebook {

    private Long bookId;

    private Integer authorId;
    private String authorName;
    private Integer categoryId;
    private String bookName;
    private String subTitle;
    private String tags;
    private String isbn;
    private String edition;
    private Byte bookType;
    private Integer star;
    private Integer downloadCount;
    private Byte status;
    private String fileType;
    private String outline;
    private String introduction;
    private String preface;
    private String cover;
    private Float price;
    private String publisher;
    private String bgColor;
    private String foreColor;
    private String titleColor;
    private String coverBackgroundId;
    private String coverPictureId;
    private Integer coverTemplateId;
    private String coverPictureMode;
    private Integer pageMode;
    private Timestamp requestDate;
    private Timestamp publishDate;
    private Timestamp updateDate;

定义一个新的DTO对象

字段和查询的字段对应,需要提供构造函数:

@Data
public class EBookInfo {
    private Long bookId;
    private String bookName;    
    private String fileType;

    public EBookInfo(Long bookId, String bookName, String fileType) {
        this.bookId = bookId;
        this.bookName = bookName;
        this.fileType = fileType;
    }

}

repository中定义查询接口

    @Query(name = "listExpressEbooks", nativeQuery = true)
    public List<EBookInfo> listExpressEbooks();

其它方案

查询中构造新对象

public List<Blog> selectByYearMonth(String year, String month, int status) {
    String sql = String.format("select new Blog(blog.id, blog.title, blog.abs, blog.createtime) from Blog blog where blog.status = %d and YEAR(createtime) = %s and MONTH(createtime) = %s order by blog.createtime desc", status, year, month);

    //Query query = this.em.createNativeQuery(sql, "ExpressedResult");
    Query query = this.em.createQuery(sql);
    List results = query.getResultList();

    return results;
}

上述方法是之前我项目中代码库里的写法,Blog需要提供相应的构造函数。

自己写convertor

repository 返回 Tuple 对象,自己写代码手动转换为指定对象,repository层使用native查询。

这里要借助辅助类:

class NativeResultProcessUtils {

        /**
         * tuple转实体对象
         * @param source tuple对象
         * @param targetClass 目标实体class
         * @param <T> 目标实体类型
         * @return 目标实体
         */
        public static <T> T processResult(Tuple source,Class<T> targetClass) {
            Object instantiate = BeanUtils.instantiate(targetClass);
            convertTupleToBean(source,instantiate,null);
            return (T) instantiate;
        }

        /**
         *
         * tuple转实体对象
         * @param source tuple对象
         * @param targetClass 目标实体class
         * @param <T> 目标实体类型
         * @param ignoreProperties 要忽略的属性
         * @return 目标实体
         */
        public static <T> T processResult(Tuple source,Class<T> targetClass,String... ignoreProperties) {
            Object instantiate = BeanUtils.instantiate(targetClass);
            convertTupleToBean(source,instantiate,ignoreProperties);
            return (T) instantiate;
        }

        /**
         * 把tuple中属性名相同的值复制到实体中
         * @param source tuple对象
         * @param target 目标对象实例
         */
        public static void convertTupleToBean(Tuple source,Object target){
            convertTupleToBean(source,target,null);
        }

        /**
         * 把tuple中属性名相同的值复制到实体中
         * @param source tuple对象
         * @param target 目标对象实例
         * @param ignoreProperties 要忽略的属性
         */
        public static void convertTupleToBean(Tuple source,Object target, String... ignoreProperties){
            //目标class
            Class<?> actualEditable = target.getClass();
            //获取目标类的属性信息
            PropertyDescriptor[] targetPds = BeanUtils.getPropertyDescriptors(actualEditable);
            //忽略列表
            List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);

            //遍历属性节点信息
            for (PropertyDescriptor targetPd : targetPds) {
                //获取set方法
                Method writeMethod = targetPd.getWriteMethod();
                //判断字段是否可以set
                if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
                    //获取source节点对应的属性
                    String propertyName = targetPd.getName();
                    Object value = source.get(propertyName);
                    if(value!=null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], value.getClass())) {
                        try {
                            //判断target属性是否private
                            if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                writeMethod.setAccessible(true);
                            }
                            //写入target
                            writeMethod.invoke(target, value);
                        }
                        catch (Throwable ex) {
                            throw new FatalBeanException(
                                "Could not copy property '" + targetPd.getName() + "' from source to target", ex);
                        }
                    }
                }
            }
        }

    }

使用entityManager的Transformers.aliasToBean

未验证,Spring data jpa未必支持

使用entityManager的Transforms.ALIAS_TO_ENTITY_MAP

未验证

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Maven最佳实践之一个好的parent依赖基础

    Maven最佳实践之一个好的parent依赖基础

    今天小编就为大家分享一篇关于Maven最佳实践之一个好的parent依赖基础,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • SpringBoot之如何正确、安全的关闭服务

    SpringBoot之如何正确、安全的关闭服务

    这篇文章主要介绍了SpringBoot之如何正确、安全的关闭服务问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • Java基本数据类型与对应的包装类(动力节点java学院整理)

    Java基本数据类型与对应的包装类(动力节点java学院整理)

    Java是面向对象的编程语言,包装类的出现更好的体现这一思想,Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。 下面通过本文给大家详细介绍,感兴趣的朋友一起学习吧
    2017-04-04
  • Java详解使用线程池处理任务方法

    Java详解使用线程池处理任务方法

    java中经常需要用到多线程来处理,我们非常不建议单纯使用继承Thread或者实现Runnable接口的方式来创建线程,那样势必有创建及销毁线程耗费资源、线程上下文切换问题。同时创建过多的线程也可能引发资源耗尽的风险,这个时候引入线程池比较合理,方便线程任务的管理
    2022-05-05
  • spring cloud 配置阿里数据库连接池 druid的示例代码

    spring cloud 配置阿里数据库连接池 druid的示例代码

    这篇文章主要介绍了spring cloud 配置阿里数据库连接池 druid,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-03-03
  • spring集成okhttp3的步骤详解

    spring集成okhttp3的步骤详解

    okhttp是一个封装URL,比HttpClient更友好易用的工具,下面这篇文章主要给大家介绍了关于spring集成okhttp3的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或工作具有一定的参考学习价值,需要的朋友们下面来一起看看吧。
    2018-04-04
  • IntelliJ IDEA快速创建getter和setter方法

    IntelliJ IDEA快速创建getter和setter方法

    这篇文章主要介绍了IntelliJ IDEA快速创建getter和setter方法,本文通过图文实例相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-03-03
  • Mybatis Generator最完美配置文件详解(完整版)

    Mybatis Generator最完美配置文件详解(完整版)

    今天小编给大家整理了一篇关于Mybatis Generator最完美配置文件详解教程,非常不错具有参考借鉴价值,感兴趣的朋友一起学习吧
    2016-11-11
  • java处理图片背景颜色的方法

    java处理图片背景颜色的方法

    这篇文章主要为大家详细介绍了java处理图片背景颜色的方法,蓝底寸照批量转换为白底,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-04-04
  • 详解SpringBoot之集成Spring AOP

    详解SpringBoot之集成Spring AOP

    本篇文章主要介绍了详解SpringBoot之集成Spring AOP,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07

最新评论