Cómo convertir los resultados de la consulta de declaraciones SQL nativas personalizadas de JPA en objetos

Lo que me hizo elegir JPA
JPA es una especificación, Hibernate es una implementación de la especificación JPA, si está interesado, puede pasar a leer este artículo. Después de
definir el Repositorio, casi todas las operaciones de no agregación de una sola tabla solo necesitan escribir un nombre de función Está hecho y la consulta se completa automáticamente de acuerdo con el nombre de función definido.
El uso de MapStruct también puede completar fácilmente consultas de varios a uno, emparejar a varios y de uno a varios. Solo necesita definirlo brevemente y agregar una anotación para hacerlo. Es mucho más cómodo que escribir una gran cantidad de archivos de configuración cuando aprende Spring MVC.
Hibernate tiene un buen rendimiento, caché de tres niveles y admite más bases de datos (buena independencia de la base de datos). Como se mencionó anteriormente, la capa DAO ahorra mucho código. Además, el marco de trabajo Hibernate es relativamente maduro y mantiene mejor la consistencia de los datos.
En contraste, MyBatis es más liviano, fácil de comenzar, bajo umbral de aprendizaje, fácil de escribir sentencias SQL por ti mismo y fácil de ajustar SQL escrito por ti mismo. Elijo Hibernate principalmente porque quiero cambiar mi gusto ocasionalmente y encuentro el mismo problema. La misma solución amplía el horizonte.
Hibernate tiene una flexibilidad débil y una buena independencia de la base de datos. Si se usa razonablemente, no puede depender de una base de datos SQL específica. La desventaja es que no es compatible con el soporte de la declaración SQL nativa. La
declaración de consulta personalizada del marco Spring Data Jpa devuelve una solución de entidad personalizada

 

Cuando usamos el marco Spring Data Jpa, generalmente necesitamos realizar consultas de base de datos complejas en función de los requisitos comerciales y devolver nuestras clases de entidad personalizadas. Bajo este marco, actualmente solo admite la devolución de entidades POJO que se asignan a la base de datos para su persistencia. Aunque podemos usar la anotación @Query para ejecutar nuestra declaración SQL personalizada en el marco, el valor de retorno es de tipo List <Object []>, que es una colección List de múltiples arreglos de Object.

A continuación, presentamos la solución de usar declaraciones de consulta personalizadas para devolver entidades personalizadas en el marco Spring Data Jpa.

Solución uno:

Por ejemplo, tenemos las siguientes entidades asociadas:
Entidad de usuario:

@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);
    }
}

Entidad de trabajo

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;

 
  
}

Entidad sucursal


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;


    }
}

La entidad Dto a convertir es la siguiente:

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;
    }
}

 

Veamos la clase de interfaz de procesamiento JPA de la capa 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);
}

Expliquemos los puntos principales en la clase de interfaz de procesamiento anterior:

①nativeQuery = true, la configuración del atributo indica que el sql en este método se trata en el formato de declaración sql de la base de datos.

②El valor de retorno es List <Object []>. Como dijimos que Jpa no puede completar automáticamente la asignación de los resultados de la consulta a las entidades personalizadas, necesitamos usar el objeto modificado para recibir.

Finalmente, veamos la clase de herramienta que convierte el objeto List <Object []> en nuestra entidad 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;
    }
}

Después de ejecutar el método de capa DAO, obtenga el objeto List <Object []> correspondiente y llame al método estático castEntity en la clase de herramienta para convertir los datos en una entidad personalizada.

Al utilizar esta solución, preste atención a los siguientes requisitos:

① El orden de los campos de consulta en la declaración de consulta personalizada debe ser coherente con el orden de los atributos en el método de construcción de la entidad personalizada.

② Esta solución es muy eficiente cuando se resuelven sentencias de consulta particularmente complejas, porque solo necesita personalizar la sentencia de consulta e interactuar con la base de datos una vez, y la eficiencia es considerable. Pero los requisitos normativos del procedimiento son relativamente altos.

③Esta solución resuelve el problema de que las tablas de datos en la base de datos del proyecto actual se crean según los requisitos comerciales y no se ajustan a la relación de asociación entre el uso del marco JPA para crear entidades persistentes (es decir, debido a los requisitos comerciales, las tablas de la base de datos establecida no se ajustan a las especificaciones de construcción de la base de datos), y Es muy práctico cuando necesita realizar múltiples asociaciones de tablas para consultas complejas.

Nota especial: El ejemplo anterior es solo para demostrar este esquema, porque la asociación de la tabla anterior es simple para obtener los resultados anteriores y se puede implementar fácilmente utilizando el marco JPA.

La demostración de conversión específica es la siguiente:

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

 

Solución dos:

Modifique la clase de interfaz de procesamiento JPA de la capa 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: Esta vez, EvaUserResp es mejor para escribir la ruta completa, es posible que el programa no pueda ubicar esta categoría.

Solución tres:

我要解决这样一条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 

El primer paso es presentar el fastJson de Ali

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

Paso 2: La capa de repositorio usa List <Map <String, String >> para recibir los datos devueltos

 @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);

El tercer paso es transformar:

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

Se siguen actualizando más planes ...

Supongo que te gusta

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