转载 

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

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

关键字: 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,总之只要开拓思维玩法是多样的

点击广告,支持我们为你提供更好的服务

立体空间感3d几何体破碎

SEO和数字营销机构HTML模板 - SEOMY

残障人士服务网站HTML模板 - Medixare

CSS鼠标停靠图标变大

CSS DIV画的字母Q

canvas生日快乐动画特效

创意代理和初创公司HTML模板 - Wan

功能齐全的ReactJs管理模板 - Adminto

技能培训在线学习平台网站模板 - Collab

原子模型CSS3动画

市政府和行政机构HTML5模板 - Towngov

CSS 3D铅笔旋转效果

按钮的hover效果覆盖过渡

Tailwindcss高级管理后台模板框架 - T-Wind

环绕式按钮菜单

three.js立体感粒子动画

法律服务和律师事务所HTML5模板 - AttorCo

百分比加载进度SVG线条动画

单页形式个人主页HTML模板 - Wedo

React实现的电子商务管理后台模板 - Dashtar

点击广告,支持我们为你提供更好的服务
 工具推荐 更多»
点击广告,支持我们为你提供更好的服务