转载  JPA dto字段映射填充--【报错】JPA 报错 Exception: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type

分类:spring,java 2022-05-16 00:08    466人阅读   

关键字: JPA复杂查询,JPA返回自定义实体,JPA返回自定义DTO,JPA联表查询,JPA原生SQL查询,JPA踩坑

注意看错误日志ConverterNotFoundException: No converter found capable of converting from type

既然是converter没有找到那就注入对应的converter就完事儿了,

查出的原始类型为AbstractJpaQueryT u p l e C o n v e r t e r TupleConverterTupleConverterTupleBackedMap,是一个Map,也就是Map转实体呗,对应的实体注入一个Map转dto的converter就行了

继续看错误日志, at org.springframework.data.repository.query.ResultProcessor.processResult(ResultProcessor.java:156) ~[spring-data-commons-2.2.5.RELEASE.jar:2.2.5.RELEASE]

在这个ResultProcessor有所发现,用到的ConversionService是通过DefaultConversionService.getSharedInstance()获取的,如下图:

那么把我们需要注入的converter注入到DefaultConversionService.getSharedInstance()应该就能实现自动转换为自定义Dto了

 

实现方案:

1.添加自定义注解@JpaDto

JpaDto.java

package com.lmt.zeus.jpa.annotation;
/**
 * @description 自定义注解表示,加在类上表示是一个JpaDto类
 * @author bazhandao
 * @date 2020/3/26 16:39
 * @since JDK1.8
 */

import org.springframework.stereotype.Component;



import java.lang.annotation.*;

@Documented
@Component
@Target(value = {ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface JpaDto {}

2.添加ZeusJpaConfiguration类实现注入加有@JpaDto注解的类对应的converter功能

ZeusJpaConfiguration.java

package com.lmt.zeus.jpa.config;



import com.lmt.zeus.jpa.annotation.JpaDto;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.BeanUtils;

import org.springframework.beans.FatalBeanException;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.ApplicationContext;

import org.springframework.context.annotation.Configuration;

import org.springframework.core.convert.support.DefaultConversionService;

import org.springframework.core.convert.support.GenericConversionService;



import javax.annotation.PostConstruct;

import java.beans.PropertyDescriptor;

import java.lang.reflect.Method;

import java.lang.reflect.Modifier;

import java.util.Map;



/**

 * @description

 *

 * @author bazhandao

 * @date 2020/3/26 17:51

 * @since JDK1.8

 */

@Slf4j

@Configuration

public class ZeusJpaConfiguration {



    @Autowired

    private ApplicationContext applicationContext;
    /**
     * 初始化注入@JpaDto对应的Converter
     */
    @PostConstruct
    public void init() {
        Map<String, Object> map = applicationContext.getBeansWithAnnotation(JpaDto.class);
        for (Object o : map.values()) {
            Class c = o.getClass();
            log.info("Jpa添加Converter,class={}", c.getName());
            GenericConversionService genericConversionService = ((GenericConversionService) DefaultConversionService.getSharedInstance());
            genericConversionService.addConverter(Map.class, c, m -> {
                try {
                    Object obj = c.newInstance();
                    // 这里可以扩展,注入的converter,实现sql查寻出的结果为数据库中带下划线的字段,通过程序转为驼峰命名再设置到实体中
                    // 也可以做类型转换判断,这里未做类型判断,直接copy到dto中,类型不匹配的时候可能会出错
                    return copyMapToObj(m, obj);
                } catch (Exception e) {
                    throw new FatalBeanException("Jpa结果转换出错,class=" + c.getName(), e);
                }
            });
        }
    }

    /**
     * 将map中的值copy到bean中对应的字段上

     * @author bazhandao
     * @date 2020-03-26
     * @param map
     * @param target
     * @return
     */

    private Object copyMapToObj(Map<String, Object> map, Object target) {
        if(map == null || target == null || map.isEmpty()){
            return target;
        }

        Class<?> actualEditable = target.getClass();
        PropertyDescriptor[] targetPds = BeanUtils.getPropertyDescriptors(actualEditable);
        for (PropertyDescriptor targetPd : targetPds) {
            if(targetPd.getWriteMethod() == null) {
                continue;
            }

            try {
                String key = targetPd.getName();
                Object value = map.get(key);
                if (value == null) {
                    continue;
                }
                Method writeMethod = targetPd.getWriteMethod();
                if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                    writeMethod.setAccessible(true);
                }
                writeMethod.invoke(target, value);
            } catch (Exception ex) {
                throw new FatalBeanException("Could not copy properties from source to target", ex);
            }
        }
        return target;
    }
}

3.实体类

package com.lmt.stock.data;

import com.lmt.zeus.jpa.annotation.JpaDto;
import lombok.Getter;
import lombok.Setter;

import java.math.BigDecimal;

/**
 * @description 小试牛刀
 *
 * @author bazhandao
 * @date 2020/3/26 13:53
 * @since JDK1.8
 */
@JpaDto  // 注意实体类要加上@JpaDto注解,将该类注入到容器
@Getter
@Setter
public class XxxDto {

    BigDecimal profitPercent;

    String accountCode;

}

4.接口实现

import com.lmt.stock.data.XxxDto;
import com.lmt.stock.data.entity.StockHisHq;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import java.util.List;

/**
 * @author bazhandao
 * @date 2020/3/23 11:08
 * @since JDK1.8
 */
public interface StockHisHqRepository extends JpaRepository<StockHisHq, Long> {
    
    // 这里可以用HQL查询,也可以用原生SQL查询
    // 查寻出的字段命名要规范
    // @Query(value = "select a.account_code as accountCode, b.profit_percent as profitPercent from trade_account a, trade_order b where a.account_code = b.account_code and a.account_code = ?1", nativeQuery = true)
    @Query(value = "select a.accountCode as accountCode, b.profitPercent as profitPercent from TradeAccount a, TradeOrder b where a.accountCode = b.accountCode and a.accountCode = ?1")
    List<XxxDto> findXxxDtoByAccountCode(String accountCode);

}

ps:该方案可以使用原生SQL也可以使用HQL,只要查寻出的结果字段名跟实体中的字段名保持一致即可,还可以对注入的converter做扩展,支持更多功能

这里使用了自定义注解将dto注入到容器,通过Spring Boot的Configuration功能实现注入该dto对应的converter功能

也可以不用自定义注解,通过自定义类扫描器或者通过配置加载到dto对应的class注入对应的converter,总之只要开拓思维玩法是多样的

服务器费用不足...

建筑工程机械设备租赁网站HTML模板 - Antek

时尚和轻量设计Bootstrap4管理系统模板 - Sunny

消息通知提示jQuery小部件

区块链数字货币管理系统网页模板 - Cryptio

jQuery弹出提示框组件

bootstrap框架web UI工具包后台模板 - MegaDin

动画效果教育行业SVG画图和图标

bootstrap风格后台界面管理系统模板 - Voler

物品租赁买卖业务平台HTML5模板 - Doremi

通用的医院医疗保健HTML5模板

很酷的元素周期表three.js动画

程序员向妹子表白专用代码

纯css太阳系动画html源码

时尚的社交网站前端界面HTML模板 - Cirkle

移动端友好的新闻博客类HTML5模板

CSS DIV中秋节网页代码

简约的Bootstrap5价格表小部件

墨汁跳跃闯关js小游戏源码

程序员向妹子表白专用代码

CSS3 Button按钮悬停效果集合

服务器费用不足...
 工具推荐 更多»