Como converter os resultados da consulta de instruções SQL nativas customizadas JPA em objetos

O que me fez escolher JPA
JPA é uma especificação, Hibernate é uma implementação da especificação JPA, se você estiver interessado, você pode passar a ler este artigo. Depois de
definir o Repositório, quase todas as operações de não agregação de tabela única precisam apenas escrever um nome de função Isso é feito e a consulta é concluída automaticamente de acordo com o nome da função definida.
Usar MapStruct também pode completar facilmente consultas muitos para um, par para muitos e um para muitos. Você só precisa defini-lo brevemente e adicionar uma anotação para fazê-lo. É muito mais confortável do que escrever um grande número de arquivos de configuração ao aprender Spring MVC.
O Hibernate tem bom desempenho, cache de três níveis e suporta mais bancos de dados (boa independência do banco de dados). Como mencionado acima, a camada DAO é muito econômica. Além disso, o framework Hibernate é relativamente maduro e mantém a consistência dos dados melhor.
Em contraste, MyBatis é mais leve, fácil de começar, baixo limite de aprendizado, fácil de escrever instruções SQL por você mesmo e fácil de ajustar o SQL escrito por você.Eu escolho o Hibernate principalmente porque quero mudar meu gosto ocasionalmente e encontrar o mesmo problema. A mesma solução amplia o horizonte.
O Hibernate é fraco em flexibilidade e tem boa independência de banco de dados. Se for usado razoavelmente, pode ser usado sem depender de um banco de dados SQL específico. A desvantagem é que não é amigável ao suporte de instrução SQL nativa. A
instrução de consulta personalizada do framework Spring Data Jpa retorna uma solução de entidade personalizada

 

Ao usar a estrutura Spring Data Jpa, geralmente precisamos realizar consultas complexas de banco de dados com base nos requisitos de negócios e retornar nossas classes de entidade personalizadas. Nesta estrutura, atualmente só oferece suporte ao retorno de entidades POJO que são mapeadas para o banco de dados para persistência. Embora possamos usar a anotação @Query para executar nossa instrução sql customizada no framework, o valor de retorno é do tipo List <Object []>, que é uma coleção List de vários arrays Object.

A seguir, apresentamos a solução de usar instruções de consulta personalizadas para retornar entidades personalizadas sob a estrutura Spring Data Jpa.

Solução um:

Por exemplo, temos as seguintes entidades associadas: Entidade do
usuário:

@Entity
@Getter
@Setter
@Table(name="tab_user")
public class User extends BaseEntity implements Serializable {

    @Id
    @NotNull(groups = Update.class)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "user_id")
    @ApiModelProperty(value = "主键")
    private Long userId;

    @Column(name = "username",nullable = false)
    @NotBlank
    @ApiModelProperty(value = "姓名")
    private String username;
  

    @Column(name = "branch_id",nullable = false)
    @NotNull
    @ApiModelProperty(value = "机构id")
    private Long branchId;

    @Column(name = "job_id",nullable = false)
    @NotNull
    @ApiModelProperty(value = "岗位id")
    private Long jobId;


    @Override
    public int hashCode() {
        return Objects.hash(id, username);
    }
}

Entidade de trabalho

package com.sgcc.modules.system.domain;

import com.sgcc.base.BaseEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Objects;

/**
 *  @author: liman
 *  @Date: 2020/7/17 17:37
 *  @Description: 员工表
 */

@Entity
@Getter
@Setter
@Table(name="tab_job")
public class JobTab extends BaseEntity implements Serializable {

    @Id
    @Column(name = "job_id")
    @NotNull(groups = Update.class)
    @ApiModelProperty(value = "ID", hidden = true)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long jobId;

    @Column(name = "job_name", nullable = false)
    @NotBlank
    @ApiModelProperty(value = "岗位名称")
    private String jobName;

    @Column(name = "branch_id", nullable = false)
    @NotBlank
    @ApiModelProperty(value = "岗位机构")
    private String branchId;

    @Column(name = "job_type", nullable = false)
    @NotBlank
    @ApiModelProperty(value = "岗位类别")
    private String jobType;

 
  
}

Entidade filial


package com.sgcc.modules.system.domain;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.sgcc.base.BaseEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;

/**
 * @website https://el-admin.vip
 * @description /
 * @author liman
 * @date 2020-07-07
 **/
@Entity
@Getter
@Setter
@Table(name="tab_branch")
public class Branch extends BaseEntity implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    @ApiModelProperty(value = "id")
    private Long id;

    @Column(name = "branch_id",nullable = false)
    @NotNull
    @ApiModelProperty(value = "机构id")
    private Long branchId;

    @Column(name = "branch_name",nullable = false)
    @NotBlank
    @ApiModelProperty(value = "机构名称")
    private String branchName;

    @Column(name = "level",nullable = false)
    @NotNull
    @ApiModelProperty(value = "级别")
    private Integer level;

    @Column(name = "p_id",nullable = false)
    @NotNull
    @ApiModelProperty(value = "父级机构id")
    private Long pId;

    @Column(name = "dept_sort",nullable = false)
    @NotNull
    @ApiModelProperty(value = "部门排序级别")
    private Integer deptSort;


    }
}

A entidade Dto a ser convertida é a seguinte:

package com.sgcc.modules.system.domain.resp;

import lombok.Getter;
import lombok.Setter;

/**
 *  @author: liman
 *  @Date: 2020/7/17 16:42
 *  @Description: 测评对象返回类
 */

@Getter
@Setter

public class EvaUserResp {
    /** 所在部门*/
    private String branchName;
    /** 测评对象*/
    private String userName;
    /** 职位*/
    private String jobName;

    /** 职位类别*/
    private String jobType;

    public EvaUserResp() {
    }

    public EvaUserResp(String branchName, String userName, String jobName, String jobType) {
        this.branchName = branchName;
        this.userName = userName;
        this.jobName = jobName;
        this.jobType = jobType;
    }
}

 

Vamos dar uma olhada na classe de interface de processamento JPA da camada DAO

public interface BookInfoRepository extends JpaRepository<BookInfo, BigDecimal> {
 
        @Query(value = "SELECT b.branch_name as branchName," +
            "a.username as userName," +
            "c.job_name as jobName," +
            "c.job_type as jobType FROM tab_user a " +
            "INNER JOIN tab_branch b ON b.branch_id = a.branch_id " +
            "INNER JOIN tab_job c on c.job_id = a.job_id " +
            "WHERE  user_id = :userId ", nativeQuery = true)
    List<Object[]>findByUserId(@Param("userId") Long userId);
}

Vamos explicar os principais pontos da classe de interface de processamento acima:

①nativeQuery = true, a configuração do atributo indica que o sql neste método é tratado no formato de instrução sql do banco de dados.

②O valor de retorno é List <Object []>. Como dissemos que Jpa não pode completar automaticamente o mapeamento dos resultados da consulta para entidades personalizadas, precisamos usar o objeto modificado para receber.

Finalmente, vamos dar uma olhada na classe de ferramenta que converte o objeto List <Object []> em nossa entidade personalizada:
 

public class EntityUtils {
    private static Logger logger = LoggerFactory.getLogger(EntityUtils.class);
 
    /**
     * 将数组数据转换为实体类
     * 此处数组元素的顺序必须与实体类构造函数中的属性顺序一致
     *
     * @param list           数组对象集合
     * @param clazz          实体类
     * @param <T>            实体类
     * @param model          实例化的实体类
     * @return 实体类集合
     */
    public static <T> List<T> castEntity(List<Object[]> list, Class<T> clazz, Object model) {
        List<T> returnList = new ArrayList<T>();
        if (list.isEmpty()) {
            return returnList;
        }
        //获取每个数组集合的元素个数
        Object[] co = list.get(0);
 
        //获取当前实体类的属性名、属性值、属性类别
        List<Map> attributeInfoList = getFiledsInfo(model);
        //创建属性类别数组
        Class[] c2 = new Class[attributeInfoList.size()];
        //如果数组集合元素个数与实体类属性个数不一致则发生错误
        if (attributeInfoList.size() != co.length) {
            return returnList;
        }
        //确定构造方法
        for (int i = 0; i < attributeInfoList.size(); i++) {
            c2[i] = (Class) attributeInfoList.get(i).get("type");
        }
        try {
            for (Object[] o : list) {
                Constructor<T> constructor = clazz.getConstructor(c2);
                returnList.add(constructor.newInstance(o));
            }
        } catch (Exception ex) {
            logger.error("实体数据转化为实体类发生异常:异常信息:{}", ex.getMessage());
            return returnList;
        }
        return returnList;
    }
 
    /**
     * 根据属性名获取属性值
     *
     * @param fieldName 属性名
     * @param modle     实体类
     * @return 属性值
     */
    private static Object getFieldValueByName(String fieldName, Object modle) {
        try {
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String getter = "get" + firstLetter + fieldName.substring(1);
            Method method = modle.getClass().getMethod(getter, new Class[]{});
            Object value = method.invoke(modle, new Object[]{});
            return value;
        } catch (Exception e) {
            return null;
        }
    }
 
    /**
     * 获取属性类型(type),属性名(name),属性值(value)的map组成的list
     *
     * @param model 实体类
     * @return list集合
     */
    private static List<Map> getFiledsInfo(Object model) {
        Field[] fields = model.getClass().getDeclaredFields();
        List<Map> list = new ArrayList(fields.length);
        Map infoMap = null;
        for (int i = 0; i < fields.length; i++) {
            infoMap = new HashMap(3);
            infoMap.put("type", fields[i].getType());
            infoMap.put("name", fields[i].getName());
            infoMap.put("value", getFieldValueByName(fields[i].getName(), model));
            list.add(infoMap);
        }
        return list;
    }
}

Depois de executar o método da camada DAO, obtenha o objeto List <Object []> correspondente e chame o método estático castEntity na classe de ferramenta para converter os dados em uma entidade personalizada.

Ao usar esta solução, preste atenção aos seguintes requisitos:

①A ordem dos campos de consulta na instrução de consulta personalizada deve ser consistente com a ordem dos atributos no método de construção da entidade personalizada.

② Esta solução é muito eficiente ao resolver instruções de consulta particularmente complexas, porque ela só precisa personalizar a instrução de consulta e interagir com o banco de dados uma vez, e a eficiência é considerável. Mas os requisitos normativos do procedimento são relativamente altos.

③ Esta solução resolve o problema de que as tabelas de dados no banco de dados do projeto atual são criadas sob requisitos de negócios e não estão em conformidade com o relacionamento de associação entre o uso da estrutura JPA para criar entidades persistentes (ou seja, devido aos requisitos de negócios, as tabelas de banco de dados estabelecidas não estão em conformidade com as especificações de construção do banco de dados), e É muito prático quando você precisa realizar associações de várias tabelas para consultas complexas.

Nota especial: O exemplo acima é apenas para demonstrar esse esquema, porque a associação da tabela acima é simples de obter os resultados acima e pode ser facilmente implementada usando a estrutura JPA.

A demonstração de conversão específica é a seguinte:

 /**object对象转化EvaUserResp对象*/
 List<Object[]> evaUserResp = userRepository.findByUserId(x.getUserId());
 List<EvaUserResp> evaUserResps = EntityUtils.castEntity(evaUserResp, EvaUserResp.class, new EvaUserResp());

 

Solução dois:

Modifique a classe de interface de processamento JPA da camada DAO:
 


@Query(value = "SELECT new EvaUserResp(b.branch_name as branchName,a.username as userName,c.job_name as jobName,c.job_type as jobType ) FROM tab_user a INNER JOIN tab_branch b ON b.branch_id = a.branch_id INNER JOIN tab_job c on c.job_id = a.job_id WHERE user_id = 4 ", nativeQuery = true)
List<EvaUserResp> findByUserId(@Param("userId") Long userId);

Nota: Desta vez EvaUserResp é melhor escrever o caminho completo, o programa pode não ser capaz de localizar esta categoria

Solução três:

我要解决这样一条sql查询出来的结果:
SELECT b.branch_name as branchName," +
            "a.username as userName," +
            "c.job_name as jobName," +
            "c.job_type as jobType FROM tab_user a " +
            "INNER JOIN tab_branch b ON b.branch_id = a.branch_id " +
            "INNER JOIN tab_job c on c.job_id = a.job_id " +
            "WHERE  user_id = :userId 

O primeiro passo é apresentar o fastJson de Ali

       <dependency>
            <groupId> com.alibaba </groupId>
            <artifactId> fastjson </artifactId>
            <version> 1.2.56 </version>
        </dependency>

Etapa 2: A camada de repositório usa List <Map <String, String >> para receber os dados retornados

 @Query(value = "SELECT b.branch_name as branchName," +
            "a.username as userName," +
            "c.job_name as jobName," +
            "c.job_type as jobType FROM tab_user a " +
            "INNER JOIN tab_branch b ON b.branch_id = a.branch_id " +
            "INNER JOIN tab_job c on c.job_id = a.job_id " +
            "WHERE  user_id = :userId ", nativeQuery = true)
    List<Map<String,String>>  findByUserId(@Param("userId") Long userId);

A terceira etapa é transformar:

    List<Map<String,String>> evaUserResp = userRepository.findByUserId(x.getUserId());
    String irsStr = JSON.toJSONString(evaUserResp);
    List<EvaUserResp> evaUserResps = JSON.parseArray(irsStr,EvaUserResp.class);

Mais planos continuam sendo atualizados ...

Acho que você gosta

Origin blog.csdn.net/LOVE_Me__/article/details/107452766
Recomendado
Clasificación