JPA 사용자 정의 원시 SQL 문의 쿼리 결과를 객체로 변환하는 방법

JPA를 선택하게 만든 것은
사양이고, Hibernate는 JPA 사양의 구현입니다. 관심이 있으시면 이 기사를 읽을 수 있습니다 .
Repository를 정의한 거의 모든 단일 테이블 비 집계 작업은 함수 이름 만 작성하면됩니다. 완료되고 정의 된 함수명에 따라 자동으로 쿼리가 완료됩니다.
MapStruct를 사용하면 다 대일, 쌍대 다, 일대 다 쿼리를 쉽게 완료 할 수 있습니다. 간단히 정의하고 주석을 추가하기 만하면됩니다. Spring MVC를 배울 때 많은 구성 파일을 작성하는 것보다 훨씬 편리합니다.
Hibernate는 우수한 성능, 3 단계 캐시, 더 많은 데이터베이스를 지원합니다 (좋은 데이터베이스 독립성). 위에서 언급했듯이 DAO 계층은 코드를 매우 절약 할 수 있습니다. 또한 Hibernate 프레임 워크는 비교적 성숙하고 데이터 일관성을 더 잘 유지합니다.
반대로 MyBatis는 더 가볍고 시작하기 쉬우 며 학습 임계 값이 낮으며 SQL 문을 직접 작성하기 쉽고 직접 SQL을 조정하기 쉽습니다. 주로 취향을 바꾸고 같은 문제를 찾고 싶어서 Hibernate를 선택합니다. 동일한 솔루션이 시야를 넓 힙니다.
Hibernate는 유연성이 약하고 데이터베이스 독립성이 우수합니다. 합리적으로 사용하면 특정 SQL 데이터베이스에 의존하지 않고 사용할 수 있습니다. 단점은 네이티브 SQL 문 지원에 친숙하지 않다는 점입니다.
SpringData Jpa 프레임 워크 사용자 지정 쿼리 문은 사용자 지정 엔티티 솔루션을 반환합니다.

 

SpringData Jpa 프레임 워크를 사용할 때는 일반적으로 비즈니스 요구 사항에 따라 복잡한 데이터베이스 쿼리를 수행하고 커스텀 엔티티 클래스를 반환해야합니다.이 프레임 워크에서는 현재 지속성을 위해 데이터베이스에 매핑 된 POJO 엔티티 만 반환하도록 지원합니다. @Query 주석을 사용하여 프레임 워크에서 사용자 지정 SQL 문을 실행할 수 있지만 반환 값은 여러 Object 배열의 List 컬렉션 인 List <Object []> 유형입니다.

아래에서는 사용자 지정 쿼리 문을 사용하여 SpringData Jpa 프레임 워크에서 사용자 지정 엔터티를 반환하는 솔루션을 소개합니다.

해결 방법 1 :

예를 들어 다음과 같은 관련 엔터티가 있습니다.
사용자 엔터티 :

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

직무

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;

 
  
}

지점 엔티티


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;


    }
}

변환 할 엔티티 Dto는 다음과 같습니다.

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

 

DAO 계층의 JPA 처리 인터페이스 클래스를 살펴 보겠습니다.

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

위의 처리 인터페이스 클래스의 주요 사항을 설명하겠습니다.

①nativeQuery = true, 속성 설정은이 메소드의 sql이 데이터베이스의 sql 문 형식으로 처리됨을 나타냅니다.

② 반환 값은 List <Object []>인데, Jpa는 질의 결과를 사용자 정의 엔티티에 자동으로 매핑 할 수 없다고했기 때문에 수정 된 객체를 사용하여 수신해야합니다.

마지막으로 List <Object []> 개체를 사용자 지정 엔터티로 변환하는 도구 클래스를 살펴 보겠습니다.
 

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

DAO 계층 메서드를 실행 한 후 해당 List <Object []> 개체를 가져오고 도구 클래스에서 정적 메서드 castEntity를 호출하여 데이터를 사용자 지정 엔터티로 변환합니다.

이 솔루션을 사용할 때 다음 요구 사항에주의하십시오.

① 사용자 정의 질의 문에서 질의 필드의 순서는 사용자 정의 엔터티의 생성 방법에서 속성의 순서와 일치해야합니다.

②이 솔루션은 특히 복잡한 질의 문을 해결할 때 매우 효율적입니다. 질의 문을 사용자 정의하고 데이터베이스와 한 번만 상호 작용하면되며 효율성이 상당히 높습니다. 그러나 절차의 규범 적 요구 사항은 상대적으로 높습니다.

③이 솔루션은 현재 프로젝트 데이터베이스의 데이터 테이블이 비즈니스 요구 사항에 따라 생성되고 JPA 프레임 워크를 사용하여 영구 엔터티를 생성하는 사이의 연관 관계를 따르지 않는 문제를 해결합니다 (즉, 비즈니스 요구 사항으로 인해 설정된 데이터베이스 테이블이 데이터베이스 구성 사양에 맞지 않음). 복잡한 쿼리에 대해 여러 테이블 연결을 수행해야 할 때 매우 실용적입니다.

특별 참고 사항 : 위의 예는 위의 테이블 연관이 위의 결과를 얻기 쉽고 JPA 프레임 워크를 사용하여 쉽게 구현 될 수 있기 때문에이 스키마를 보여주기위한 것입니다.

구체적인 변환 데모는 다음과 같습니다.

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

 

해결 방법 2 :

DAO 계층의 JPA 처리 인터페이스 클래스를 수정합니다.
 


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

참고 : 이번에는 EvaUserResp가 전체 경로를 작성하는 것이 가장 좋습니다. 프로그램이이 범주를 찾지 못할 수도 있습니다.

해결 방법 3 :

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

첫 번째 단계는 Ali의 fastJson을 소개하는 것입니다.

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

2 단계 : 리포지토리 계층은 List <Map <String, String >>을 사용하여 반환 된 데이터를받습니다.

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

세 번째 단계는 다음을 변환하는 것입니다.

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

더 많은 계획이 계속 업데이트됩니다 ...

추천

출처blog.csdn.net/LOVE_Me__/article/details/107452766