jpa查询返回自定义对象、返回指定VO、POJO
jpa查询返回自定义对象、返回指定VO、POJO
jpa查询返回自定义对象、返回指定VO、POJO,JPA查询前会做大量处理,还有线程通知的操作。若并发大,处理性能直线下降。但是jpa就因为做了大量处理,对多数据库兼容极好,操作方便。
有时候你想查询某个表,不想要某个字段内容太长时;或要返回非entity的对象时,需要自定义。
这时候你就会先百度、google一下,找到如下方案:
- 1、使用
session.createQuery
自定义返回Map结果,撸编写一大串代码,jpa就是为了简化代码编写,背道而驰了。 - 2、在查询的sql中直接使用新建构造对象的:
select new top.lingkang.lalanote.vo.FolderVo(e.id,e.name) from FolderEntity e
不优雅,一点也不优雅,你还要维护返回值的构造函数 - 3、
JpaRepository
中查询结果使用数组的:List<Object[]> query();
,后期维护可能存在变动、也不优雅 - 4、
JpaRepository
中查询返回结果是一个接口类的,查询pojo、vo写一堆接口层层转换也不优雅
基于以上种种,我立马分析源码看看怎么配置优雅地返回自定义结果,于是有了这篇文章,包含了我对java的一些理解。花了好几个小时,帮我点点赞吧!
转自:https://lingkang.top/archives/jpa-cha-xun-fan-hui-zi-ding-yi-dui-xiang
1、查询返回某个VO
public interface FolderRepository extends JpaRepository<FolderEntity, String> {@Query("select e from FolderEntity e")public List<FolderVo> get();
}
你要查询某个VO时,这样写会报错:类型转换失败
No converter found capable of converting from type [top.lingkang.lalanote.entity.FolderEntity] to type [top.lingkang.lalanote.vo.FolderVo]
它抛出异常的地方是这里GenericConversionService.handleConverterNotFound
,上一级调用是convert
通过异常栈发现处理返回结果转换的类是ResultProcessor.processResult
再往上也没啥看头了,我打个断点看ResultProcessor.ProjectingConverter.convert
发现private final ConversionService conversionService;
是DefaultConversionService
默认转换服务,DefaultConversionService
默认的转换服务是spring-core
所有。
jpa的结果处理:ResultProcessor.ChainingConverter.and
如下
return intermediate == null || targetType.isInstance(intermediate) ? intermediate: converter.convert(intermediate);
2、源码分析
我们的返回结果是FolderVo
,不是表映射实体类FolderEntity
所以targetType.isInstance(intermediate)
结果是false
,它进入了spring的默认结果转换:DefaultConversionService
,其中DefaultConversionService
是继承GenericConversionService
的。其中的转换处理方法是:
@Nullableprotected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);GenericConverter converter = this.converterCache.get(key);if (converter != null) {return (converter != NO_MATCH ? converter : null);}// 默认的转换中找不到 FolderEntity 结果转 FolderVoconverter = this.converters.find(sourceType, targetType);if (converter == null) {converter = getDefaultConverter(sourceType, targetType);}if (converter != null) {this.converterCache.put(key, converter);return converter;}this.converterCache.put(key, NO_MATCH);return null;}
我断点找了一两个小时发现jpa的ResultProcessor
是不开放配置的,初始化时已经是固定了,底层执行类也是私有的,无法进行直接配置。
那么只能从默认的转换服务下手,即配置GenericConversionService.converters
,在converters
添加上我们需要的类转换:FolderEntity
→FolderVo
,而且它有对应的添加方法:
private final Converters converters = new Converters();public void addConverter(Converter<?, ?> converter)
看了一遍也没能发现配置GenericConversionService
的入口,上面提到过,ResultProcessor
数据转换调用的是DefaultConversionService
查看了源码发现给我们开放了这个实例:
/*** Return a shared default {@code ConversionService} instance,* lazily building it once needed.* <p><b>NOTE:</b> We highly recommend constructing individual* {@code ConversionService} instances for customization purposes.* This accessor is only meant as a fallback for code paths which* need simple type coercion but cannot access a longer-lived* {@code ConversionService} instance any other way.* @return the shared {@code ConversionService} instance (never {@code null})* @since 4.3.5*/public static ConversionService getSharedInstance() {DefaultConversionService cs = sharedInstance;if (cs == null) {synchronized (DefaultConversionService.class) {cs = sharedInstance;if (cs == null) {cs = new DefaultConversionService();sharedInstance = cs;}}}return cs;}
注释中说明了这是一个公共共享的转换服务,我们可以直接拿到操作它,往里面添加我们的转换器:
FolderEntity
→FolderVo
3、定义结果解析,entity转pojo、vo等
首先定义一个转换器:
import cn.hutool.core.bean.BeanUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.GenericConverter;
import top.lingkang.lalanote.entity.FolderEntity;
import top.lingkang.lalanote.vo.FolderVo;import java.util.HashSet;
import java.util.Set;/*** @author lingkang* Created by 2023/8/12*/
@Slf4j
public class EntityToVoGenericConverter implements GenericConverter {// 不必担心性能问题,底层使用了cache存储处理@Overridepublic Set<ConvertiblePair> getConvertibleTypes() {Set<ConvertiblePair> convertiblePairs = new HashSet<>();// 其他转换类可以直接在此添加(这样写是定向)// convertiblePairs.add(new ConvertiblePair(FolderEntity.class, FolderVo.class));// 或者写成这样,这样会匹配所有对象进行转换(推荐)不必担心性能问题,底层使用了cache存储处理convertiblePairs.add(new ConvertiblePair(Object.class, Object.class));return convertiblePairs;}@Overridepublic Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {// System.out.println(source);try {// 直接创建结果对象Object instance = targetType.getType().getDeclaredConstructor().newInstance();// hutool-core中的bean复制:FolderEntity 复制属性到 FolderVoBeanUtil.copyProperties(source, instance);// 返回结果: FolderVoreturn instance;} catch (Exception e) {log.error("无法解析的映射!", e);throw new RuntimeException(e);}}
}
添加一个springboot启动后追加初始化设置
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.stereotype.Component;/*** @author lingkang* Created by 2023/8/12*/
@Component
@Order(1) //如果多个自定义的 ApplicationRunner ,用来标明执行的顺序
public class StartRunAfterInit implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) throws Exception {DefaultConversionService sharedInstance = (DefaultConversionService) DefaultConversionService.getSharedInstance();// 加入我们的解析 FolderEntity → FolderVosharedInstance.addConverter(new EntityToVoGenericConverter());}
}
再执行一次查询,能根据解析返回结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yEEerySS-1691861436064)(/upload/2023/08/image-1691854158520.png)]
断点也能找到我定义的处理类: EntityToVoGenericConverter
4、只查询某几个字段
有时候,你不可能把所有的字段都查询出来,只查询其中的两个、或两个以上。(查一个字段可以直接类型返回)
总不能分两次查询、使用数组接收、map接收?这样不优雅,按照上面的配置,我们可以这样:
public interface FolderRepository extends JpaRepository<FolderEntity, String> {@Query("select e from FolderEntity e")public List<FolderVo> get();// 一定要加上 as XXX 否则将无法解析到 AbstractJpaQuery.TupleConverter.TupleBackedMap 中的key值// 像下面的 e.createTime 将无法被解析到@Query("select e.id as id,e.name as name,e.createTime from FolderEntity e")List<FolderVo> getIdAndName();
}
EntityToVoGenericConverter
中修改如下
// 不必担心性能问题,底层使用了cache存储处理@Overridepublic Set<ConvertiblePair> getConvertibleTypes() {Set<ConvertiblePair> convertiblePairs = new HashSet<>();// 其他转换类可以直接在此添加(这样写是定向)// convertiblePairs.add(new ConvertiblePair(FolderEntity.class, FolderVo.class));// 或者写成这样,这样会匹配所有对象进行转换(推荐)convertiblePairs.add(new ConvertiblePair(Object.class, Object.class));// 用于解析 JpaQueryFactory.TupleConverter.TupleBackedMap(这样写是定向)// select e.id as id,e.name as name,e.createTime from FolderEntity econvertiblePairs.add(new ConvertiblePair(Map.class, Object.class));return convertiblePairs;}
执行调用
@GetMapping("/t2")@ResponseBodypublic Object t2() {return folderRepository.getIdAndName();}
返回结果:
可以看到,e.createTime
未曾 as createTime
导致无法映射到返回的实体类
5、实体类展示
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;/*** @author lingkang* created by 2023/7/27*/
@Data
@Entity
@Table(name = "n_folder")
public class FolderEntity extends BaseEntity {@Id@Column(name = "id")private String id;@Column(name = "parent_id")private String parentId;@Column(name = "name")private String name;@Column(name = "attr")private String attr;@Column(name = "type")private Integer type;
}
import lombok.Data;import java.io.Serializable;
import java.util.Date;/*** @author lingkang* created by 2023/8/3*/
@Data
public class FolderVo implements Serializable {private String id;private String parentId;private String name;private String attr;private Integer type;private Date createTime;private Date updateTime;
}
相关文章:

jpa查询返回自定义对象、返回指定VO、POJO
jpa查询返回自定义对象、返回指定VO、POJO jpa查询返回自定义对象、返回指定VO、POJO,JPA查询前会做大量处理,还有线程通知的操作。若并发大,处理性能直线下降。但是jpa就因为做了大量处理,对多数据库兼容极好,操作方…...

抖音小程序开发,收银台支付回调通知
大家好,我是小悟 关于抖音小程序收银台支付,可阅读【抖音小程序开发,唤起收银台,包括抖音支付、支付宝支付、微信支付】。 做支付功能最重要的一步就是异步回调通知,所谓回调通知就是唤起收银台支付,支付…...
selenium 爬虫
selenium 可以动态爬取网页数据,就像真实用户操作浏览器一样,从终端用户的角度测试应用程序,WebDriver通过原生浏览器支持或者浏览器扩展直接控制浏览器 webdriver下载 因为selenuim对浏览器的版本存在兼容问题,顾需要针对指定浏…...
亚商投资顾问 早餐FM/0815生成式人工智能服务管理
01/亚商投资顾问 早间导读 商务部等9部门:促进农村大宗商品消费更新换代中央财办等九部门印发指导意见推动农村流通高质量发展《生成式人工智能服务管理暂行办法》今起施行 02/亚商投资顾问 新闻早餐 // 热点聚焦 // 商务部等9部门印发《县域商业三年行动计划&…...

C语言题目的多种解法分享 2之字符串左旋和补充题
前言 有的时候,这个系列专栏中的解法之间并无优劣,只是给大家提供不同的解题思路 我决定将代码实现的过程写成注释,方便大家直接找到对应的函数,只有需要补充说明的知识才会单拿出来强调 这个系列的文章会更的比较慢࿰…...

科技云报道:算力之战,英伟达再度释放AI“炸弹”
科技云报道原创。 近日,在计算机图形学顶会SIGGRAPH 2023现场,英伟达再度释放深夜“炸弹”,大模型专用芯片迎来升级版本。 英伟达在会上发布了新一代GH200 Grace Hopper平台,该平台依托于搭载全球首款搭载HBM3e处理器的新型Grac…...

油电同价、标配8155,奇瑞猛攻10-15万中型SUV市场
8月8日,奇瑞瑞虎8冠军家族在北京国家奥林匹克体育中心正式上市,推出了瑞虎8 PRO冠军版、瑞虎8新能源冠军版两款新车,燃油混动双线同步发力。 其中,瑞虎8 PRO冠军版共推7款车型,官方指导价12.69万元-16.39万元…...

【leetcode】【图解】617. 合并二叉树
题目 难度:简单 给你两棵二叉树: root1 和 root2 。 想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是…...
基于java的汽车改装方案网站设计与实现
摘要 本文主要讲述了基于SpringBootMySql开发技术开发的汽车改装方案网站的设计与实现。这里的汽车改装方案网站是通过一个平台使所有的汽车爱好者们可以不用出门就可以体验到专业的汽车改装方案设计服务。现实生活中如果需要进行汽车改装的方案设计,往往要跑很多次…...

DC电源模块减小输入电源与输出负载之间的能量损失
BOSHIDA DC电源模块减小输入电源与输出负载之间的能量损失 随着电子产品的普及,DC电源模块已成为现代电子设备中不可或缺的组成部分。DC电源模块可以将交流电转化为直流电,并根据需要,以适当的电压和电流提供给输出负载。然而,在输…...

Python自动化小技巧16——分类汇总写入excel不同sheet表
案例背景 上了两个月班的社畜博主最近终于有空来总结一下最近写的代码了。 因为上班都是文职工作,天天不是word就是excel就是PPT和pdf....这和什么机器学习还有数据科学不一样,任务更多的是处理实在的文字和表格等格式,按照领导要求来完成&…...

FlexRay汽车总线静电防护,如何设计保护方案图?
FlexRay是一种高速、实时、可靠、具备故障容错能力的总线技术,是继CAN和LIN总线之后的最新研发成果。FlexRay为线控应用(即线控驱动、线控转向、线控制动等)提供了容错和时间确定性性能要求。虽然FlexRay将解决当前高端和未来主流车载网络的挑…...

jpg图片太大怎么压缩?这样做轻松压缩图片
图片太大会给存储、分享带来麻烦,但其实现在压缩图片大小也不是什么难事,下面就给大家分享几个一直用的图片压缩方法,包含批量压缩、在线压缩、免费压缩等多种方式,大家按需自取哈~ 方法一:嗨格式压缩大师 这是一个可…...

B057-spring增强 依赖注入 AOP 代理模式 创建Bean
目录 AOP概念代理模式引出AOP实现方式xml方式实现注解方式实现 AOP 概念 事务管理:比如可以抽取try catch的重复代码 日志监控:比如业务逻辑前后打印关于当前订单数量的日志,了解业务做了什么 性能监控:比如业务前后打印时间&…...

小程序多图片组合
目录 子组件 index.js 子组件 index.wxml 子组件 index.wxss 父组件引用: 子组件:preview-image 子组件 index.js Component({properties: {previewData: {type: Array,default: [],observer: function (newVal, oldVal) {console.log(newVal, ol…...

YOLO v8目标跟踪详细解读(二)
上一篇,结合代码,我们详细的介绍了YOLOV8目标跟踪的Pipeline。大家应该对跟踪的流程有了大致的了解,下面我们将对跟踪中出现的卡尔曼滤波进行解读。 1.卡尔曼滤波器介绍 卡尔曼滤波(kalman Filtering)是一种利用线性…...

【广州华锐视点】AR电力职业技能培训系统让技能学习更“智慧”
随着科技的发展,教育方式也在不断地进步和创新。其中,增强现实(AR)技术的出现,为教育领域带来了全新的可能。AR电力职业技能培训系统就是这种创新教学方法的完美实践,它将虚拟与现实相结合,为学生提供了一个沉浸式的学…...
C#学习,反射
目录 C#学习 .NET的体系结构 二次编译 反射 什么是反射? 什么是Type? 什么是程序集? 反射API: 一,程序集 1, Load 2,LoadFrom 3,LoadFile 二,类型实例 1&a…...

代理模式概述
1.代理模式概述 学习内容 1)概述 为什么要有 “代理” ? 生活中就有很多例子,比如委托业务,黄牛(票贩子)等等代理就是被代理者没有能力或者不愿意去完成某件事情,需要找个人代替自己去完成这…...

最新AI系统ChatGPT网站程序源码+搭建教程/公众号/H5端/安装配置教程/完整知识库
1、前言 SparkAi系统是基于国外很火的ChatGPT进行开发的Ai智能问答系统。本期针对源码系统整体测试下来非常完美,可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。 那么如何搭建部署AI创作ChatGPT?小编这里写一个详细图文教程吧!…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...

【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...

Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
C++.OpenGL (14/64)多光源(Multiple Lights)
多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...