网站建设投标书服务方案范本,电子商务网站硬件建设的核心,网站制作软件是什么,大庆网页制作公司价格jpa查询返回自定义对象、返回指定VO、POJO
jpa查询返回自定义对象、返回指定VO、POJO#xff0c;JPA查询前会做大量处理#xff0c;还有线程通知的操作。若并发大#xff0c;处理性能直线下降。但是jpa就因为做了大量处理#xff0c;对多数据库兼容极好#xff0c;操作方…jpa查询返回自定义对象、返回指定VO、POJO
jpa查询返回自定义对象、返回指定VO、POJOJPA查询前会做大量处理还有线程通知的操作。若并发大处理性能直线下降。但是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中查询结果使用数组的ListObject[] 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 JpaRepositoryFolderEntity, String {Query(select e from FolderEntity e)public ListFolderVo 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.* pbNOTE:/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 SetConvertiblePair getConvertibleTypes() {SetConvertiblePair 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 JpaRepositoryFolderEntity, String {Query(select e from FolderEntity e)public ListFolderVo get();// 一定要加上 as XXX 否则将无法解析到 AbstractJpaQuery.TupleConverter.TupleBackedMap 中的key值// 像下面的 e.createTime 将无法被解析到Query(select e.id as id,e.name as name,e.createTime from FolderEntity e)ListFolderVo getIdAndName();
}EntityToVoGenericConverter中修改如下 // 不必担心性能问题底层使用了cache存储处理Overridepublic SetConvertiblePair getConvertibleTypes() {SetConvertiblePair 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 {IdColumn(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;
}