繁体   English   中英

如何使用 SqlResultSetMapping 将 JPA NativeQuery 的结果集映射到 POJO

[英]How to map the result set of a JPA NativeQuery to a POJO using SqlResultSetMapping

我正在尝试使用@SqlResultSetMapping 和@ConstructorResult 将本机查询的结果映射到POJO。 这是我的代码:

@SqlResultSetMapping(name="foo",
    classes = {
        @ConstructorResult(
                targetClass = Bar.class,
                columns = {
                    @ColumnResult(name = "barId", type = Long.class),
                    @ColumnResult(name = "barName", type = String.class),
                    @ColumnResult(name = "barTotal", type = Long.class)
                })
    })

public class Bar {

private Long barId;
private String barName;
private Long barTotal;

...

然后在我的 DAO 中:

Query query = em.createNativeQueryBar(QUERY, "foo");
... set some parameters ...
List<Bar> list = (List<Bar>) query.getResultList();

我读过这个功能只在 JPA 2.1 中受支持,但这就是我正在使用的。 这是我的依赖:

        <dependency>
            <groupId>org.hibernate.javax.persistence</groupId>
            <artifactId>hibernate-jpa-2.1-api</artifactId>
            <version>1.0.0.Final</version>
        </dependency>

我找到了一些资源,包括这个: @ConstructorResult mapping in jpa 2.1 但我仍然没有任何运气。

我错过了什么? 为什么找不到SqlResultSetMapping?

javax.persistence.PersistenceException: org.hibernate.MappingException: Unknown SqlResultSetMapping [foo]

@SqlResultSetMapping注解不应放在 POJO 上 把它放在(任何) @Entity类。 “未知的 SqlResultSetMapping [foo]”告诉您,JPA 提供程序没有看到名称为“foo”的任何映射。 请参阅我的另一个答案以获取正确的示例

简短的工作示例:

  • DTO POJO 类

    @lombok.Getter @lombok.AllArgsConstructor public class StatementDto { private String authorName; private Date createTime; }
  • 存储库 bean:

     @Repository public class StatementNativeRepository { @PersistenceContext private EntityManager em; static final String STATEMENT_SQLMAP = "Statement-SQL-Mapping"; public List<StatementDto> findPipelinedStatements() { Query query = em.createNativeQuery( "select author_name, create_time from TABLE(SomePipelinedFun('xxx'))", STATEMENT_SQLMAP); return query.getResultList(); } @SqlResultSetMapping(name= STATEMENT_SQLMAP, classes = { @ConstructorResult(targetClass = StatementDto.class, columns = { @ColumnResult(name="author_name",type = String.class), @ColumnResult(name="create_time",type = Date.class) } ) }) @Entity class SQLMappingCfgEntity{@Id int id;} // <- workaround }

我可以这样做:

Session session = em().unwrap(Session.class);
SQLQuery q = session.createSQLQuery("YOUR SQL HERE");
q.setResultTransformer( Transformers.aliasToBean( MyNotMappedPojoClassHere.class) );
List<MyNotMappedPojoClassHere> postList = q.list();

@Entity添加到 DTO POJO 的问题在于,它将在您的数据库中创建一个您不需要的表。 必须将@Id和瞬态关键字添加到必要的字段也是一件麻烦事。 一个简单的解决方案是将您的@SqlResultSetMapping移动到抽象类。

@MappedSuperclass
@SqlResultSetMapping(name="foo",
    classes = {
        @ConstructorResult(
                targetClass = Bar.class,
                columns = {
                    @ColumnResult(name = "barId", type = Long.class),
                    @ColumnResult(name = "barName", type = String.class),
                    @ColumnResult(name = "barTotal", type = Long.class)
                })
    })

public abstract class sqlMappingCode {}

不要忘记添加@MappedSuperclass 这将确保 Hibernate 自动连接您的映射。

更新:此功能在 Hibernate 5.4.30 中被破坏。最终,创建了 Jira 票证HHH-14572

更新 2:看起来此功能在 5.4.30.Final 之后永久中断。 新的解决方案,您需要添加一个带有表名的@Entity ,然后创建两个类:

public class CustomSchemaFilterProvider implements SchemaFilterProvider {
// implement code, use CustomSchemaFilter below
}

然后创建一个单例自定义过滤器

public class CustomSchemaFilter implements SchemaFilter {
// implement code, check for table names you don't want in your schema
}

在您的 JPA 配置文件集中:

jpaProp.put(Environment.HBM2DDL_FILTER_PROVIDER, "com.your.package.CustomSchemaFilterProvider");
// OR
hibernate.hbm2ddl.schema_filter_provider=com.your.package.CustomSchemaFilterProvider

这将确保您的新@Entity在映射期间被休眠忽略

用休眠 5.6.9.Final 测试

领域模型

假设我们的数据库中有以下postpost_comment表:

JPA SqlResultSetMapping post 和 post_comment 表

JPA SqlResultSetMapping

SqlResultSetMapping JPA 注释如下所示:

@Repeatable(SqlResultSetMappings.class)
@Target({TYPE}) 
@Retention(RUNTIME)
public @interface SqlResultSetMapping { 

    String name(); 

    EntityResult[] entities() default {};

    ConstructorResult[] classes() default {};

    ColumnResult[] columns() default {};
}

SqlResultSetMapping注解是可重复的,并在实体类级别应用。 除了采用 Hibernate 用来注册映射的唯一名称之外,还有三个映射选项:

  • EntityResult
  • ConstructorResult
  • ColumnResult

接下来,我们将了解这三个映射选项是如何工作的,以及您需要使用它们的用例。

JPA SqlResultSetMapping - EntityResult

EntityResult选项允许您将 JDBC ResultSet列映射到一个或多个 JPA 实体。

假设我们想要获取前 5 个Post实体以及与给定title模式匹配的所有关联PostComment实体。

正如我在本文中所解释的,我们可以使用DENSE_RANK SQL 窗口函数来了解如何过滤postpost_comment连接记录,如下面的 SQL 查询所示:

SELECT *
FROM (
  SELECT
    *,
    DENSE_RANK() OVER (
    ORDER BY
      "p.created_on",
      "p.id"
    ) rank
  FROM (
    SELECT
      p.id AS "p.id", p.created_on AS "p.created_on",
      p.title AS "p.title", pc.post_id AS "pc.post_id",
      pc.id as "pc.id", pc.created_on AS "pc.created_on",
      pc.review AS "pc.review"
    FROM post p
    LEFT JOIN post_comment pc ON p.id = pc.post_id
    WHERE p.title LIKE :titlePattern
    ORDER BY p.created_on
  ) p_pc
) p_pc_r
WHERE p_pc_r.rank <= :rank

但是,我们不想返回标量列值的列表。 我们想从这个查询中返回 JPA 实体,所以我们需要配置@SqlResultSetMapping注解的entities属性,像这样:

@NamedNativeQuery(
    name = "PostWithCommentByRank",
    query = """
        SELECT *
        FROM (
          SELECT
            *,
            DENSE_RANK() OVER (
            ORDER BY
              "p.created_on",
              "p.id"
            ) rank
          FROM (
            SELECT
              p.id AS "p.id", p.created_on AS "p.created_on",
              p.title AS "p.title", pc.post_id AS "pc.post_id",
              pc.id as "pc.id", pc.created_on AS "pc.created_on",
              pc.review AS "pc.review"
            FROM post p
            LEFT JOIN post_comment pc ON p.id = pc.post_id
            WHERE p.title LIKE :titlePattern
            ORDER BY p.created_on
          ) p_pc
        ) p_pc_r
        WHERE p_pc_r.rank <= :rank
        """,
    resultSetMapping = "PostWithCommentByRankMapping"
)
@SqlResultSetMapping(
    name = "PostWithCommentByRankMapping",
    entities = {
        @EntityResult(
            entityClass = Post.class,
            fields = {
                @FieldResult(name = "id", column = "p.id"),
                @FieldResult(name = "createdOn", column = "p.created_on"),
                @FieldResult(name = "title", column = "p.title"),
            }
        ),
        @EntityResult(
            entityClass = PostComment.class,
            fields = {
                @FieldResult(name = "id", column = "pc.id"),
                @FieldResult(name = "createdOn", column = "pc.created_on"),
                @FieldResult(name = "review", column = "pc.review"),
                @FieldResult(name = "post", column = "pc.post_id"),
            }
        )
    }
)

有了SqlResultSetMapping ,我们可以像这样获取PostPostComment实体:

List<Object[]> postAndCommentList = entityManager
    .createNamedQuery("PostWithCommentByRank")
    .setParameter("titlePattern", "High-Performance Java Persistence %")
    .setParameter("rank", POST_RESULT_COUNT)
    .getResultList();

而且,我们可以验证实体是否被正确获取:

assertEquals(
    POST_RESULT_COUNT * COMMENT_COUNT, 
    postAndCommentList.size()
);

for (int i = 0; i < COMMENT_COUNT; i++) {
    Post post = (Post) postAndCommentList.get(i)[0];
    PostComment comment = (PostComment) postAndCommentList.get(i)[1];

    assertTrue(entityManager.contains(post));
    assertTrue(entityManager.contains(comment));

    assertEquals(
        "High-Performance Java Persistence - Chapter 1",
        post.getTitle()
    );

    assertEquals(
        String.format(
            "Comment nr. %d - A must read!",
            i + 1
        ),
        comment.getReview()
    );
}

@EntityResult在通过 SQL 存储过程获取 JPA 实体时也很有用。 查看这篇文章了解更多详情。

JPA SqlResultSetMapping - 构造函数结果

假设我们要执行一个聚合查询,计算每个postpost_coment记录数并返回post title以用于报告目的。 我们可以使用下面的 SQL 查询来实现这个目标:

SELECT
  p.id AS "p.id",
  p.title AS "p.title",
  COUNT(pc.*) AS "comment_count"
FROM post_comment pc
LEFT JOIN post p ON p.id = pc.post_id
GROUP BY p.id, p.title
ORDER BY p.id

我们还希望将帖子标题和评论数封装在以下 DTO 中:

public class PostTitleWithCommentCount {

    private final String postTitle;
    
    private final int commentCount;

    public PostTitleWithCommentCount(
            String postTitle,
            int commentCount) {
        this.postTitle = postTitle;
        this.commentCount = commentCount;
    }

    public String getPostTitle() {
        return postTitle;
    }

    public int getCommentCount() {
        return commentCount;
    }
}

要将上述 SQL 查询的结果集映射到PostTitleWithCommentCount DTO,我们可以使用@SqlResultSetMapping注解的classes属性,如下所示:

@NamedNativeQuery(
    name = "PostTitleWithCommentCount",
    query = """
        SELECT
          p.id AS "p.id",
          p.title AS "p.title",
          COUNT(pc.*) AS "comment_count"
        FROM post_comment pc
        LEFT JOIN post p ON p.id = pc.post_id
        GROUP BY p.id, p.title
        ORDER BY p.id
        """,
    resultSetMapping = "PostTitleWithCommentCountMapping"
)
@SqlResultSetMapping(
    name = "PostTitleWithCommentCountMapping",
    classes = {
        @ConstructorResult(
            columns = {
                @ColumnResult(name = "p.title"),
                @ColumnResult(name = "comment_count", type = int.class)
            },
            targetClass = PostTitleWithCommentCount.class
        )
    }
)

ConstructorResult注释允许我们指示 Hibernate 使用哪个 DTO 类以及在实例化 DTO 对象时调用哪个构造函数。

请注意,我们使用了@ColumnResult注释的type属性来指定comment_count应该转换为 Java int 这是必需的,因为一些 JDBC 驱动程序使用LongBigInteger作为 SQL 聚合函数结果。

这是使用 JPA 调用PostTitleWithCommentCount命名本机查询的方法:

List<PostTitleWithCommentCount> postTitleAndCommentCountList = entityManager
    .createNamedQuery("PostTitleWithCommentCount")
    .setMaxResults(POST_RESULT_COUNT)
    .getResultList();

而且,我们可以看到返回的PostTitleWithCommentCount DTO 已正确获取:

assertEquals(POST_RESULT_COUNT, postTitleAndCommentCountList.size());

for (int i = 0; i < POST_RESULT_COUNT; i++) {
    PostTitleWithCommentCount postTitleWithCommentCount = 
        postTitleAndCommentCountList.get(i);

    assertEquals(
        String.format(
            "High-Performance Java Persistence - Chapter %d",
            i + 1
        ),
        postTitleWithCommentCount.getPostTitle()
    );

    assertEquals(COMMENT_COUNT, postTitleWithCommentCount.getCommentCount());
}

有关使用 JPA 和 Hibernate 获取 DTO 投影的最佳方法的更多详细信息,请查看这篇文章

JPA SqlResultSetMapping - 列结果

前面的示例展示了如何将 SQL 聚合结果集映射到 DTO。 但是,如果我们想要返回我们正在计算评论的 JPA 实体怎么办?

为了实现这个目标,我们可以使用entities属性来定义我们正在获取的Post实体,并使用@SqlResultSetMapping注解的classes属性来映射标量值,在我们的例子中是关联的post_comment记录的数量:

@NamedNativeQuery(
    name = "PostWithCommentCount",
    query = """
        SELECT
          p.id AS "p.id",
          p.title AS "p.title",
          p.created_on AS "p.created_on",
          COUNT(pc.*) AS "comment_count"
        FROM post_comment pc
        LEFT JOIN post p ON p.id = pc.post_id
        GROUP BY p.id, p.title
        ORDER BY p.id
        """,
    resultSetMapping = "PostWithCommentCountMapping"
)
@SqlResultSetMapping(
    name = "PostWithCommentCountMapping",
    entities = @EntityResult(
        entityClass = Post.class,
        fields = {
            @FieldResult(name = "id", column = "p.id"),
            @FieldResult(name = "createdOn", column = "p.created_on"),
            @FieldResult(name = "title", column = "p.title"),
        }
    ),
    columns = @ColumnResult(
        name = "comment_count",
        type = int.class
    )
)

执行PostWithCommentCount命名本机查询时:

List<Object[]> postWithCommentCountList = entityManager
    .createNamedQuery("PostWithCommentCount")
    .setMaxResults(POST_RESULT_COUNT)
    .getResultList();

我们将获得Post实体和commentCount标量列值:

assertEquals(POST_RESULT_COUNT, postWithCommentCountList.size());

for (int i = 0; i < POST_RESULT_COUNT; i++) {
    Post post = (Post) postWithCommentCountList.get(i)[0];
    int commentCount = (int) postWithCommentCountList.get(i)[1];

    assertTrue(entityManager.contains(post));

    assertEquals(i + 1, post.getId().intValue());
    assertEquals(
        String.format(
            "High-Performance Java Persistence - Chapter %d",
            i + 1
        ),
        post.getTitle()
    );

    assertEquals(COMMENT_COUNT, commentCount);
}
@Entity
@SqlResultSetMapping(name="ConnexionQueryBean",
   entities={
         @EntityResult(entityClass=com.collecteJ.business.bean.ConnexionQueryBean.class, fields={ 
        @FieldResult(name="utilisateurId", column="UTILISATEUR_ID"),
        @FieldResult(name="nom", column="NOM"),
        @FieldResult(name="prenom", column="PRENOM"),
        @FieldResult(name="nomConnexion", column="NOM_CONNEXION"),
        @FieldResult(name="codeAgence", column="CODE_AGENCE"),
        @FieldResult(name="codeBanque", column="CODE_BANQUE"),
        @FieldResult(name="codeDevise", column="CODE_DEVISE"),
        @FieldResult(name="codeCollecteur", column="CODE_COLLECTEUR")})
   })
public class ConnexionQueryBean implements Serializable {

@Id
private long utilisateurId;
private String codeCollecteur;
private String nom;
private String prenom;
private String nomConnexion; 
private String codeAgence;
private String codeBanque;
private String codeDevise;


public ConnexionQueryBean() {
}


public long getUtilisateurId() {
    return utilisateurId;
}

public void setUtilisateurId(long utilisateurId) {
    this.utilisateurId = utilisateurId;
}

public String getCodeCollecteur() {
    return codeCollecteur;
}

public void setCodeCollecteur(String codeCollecteur) {
    this.codeCollecteur = codeCollecteur;
}


public String getNom() {
    return nom;
}

public void setNom(String nom) {
    this.nom = nom;
}

public String getPrenom() {
    return prenom;
}

public void setPrenom(String prenom) {
    this.prenom = prenom;
}

public String getNomConnexion() {
    return nomConnexion;
}

public void setNomConnexion(String nomConnexion) {
    this.nomConnexion = nomConnexion;
}

public String getCodeAgence() {
    return codeAgence;
}

public void setCodeAgence(String codeAgence) {
    this.codeAgence = codeAgence;
}

public String getCodeBanque() {
    return codeBanque;
}

public void setCodeBanque(String codeBanque) {
    this.codeBanque = codeBanque;
}

public String getCodeDevise() {
    return codeDevise;
}

public void setCodeDevise(String codeDevise) {
    this.codeDevise = codeDevise;
}

@Override
public String toString() {
    return "ConnexionQueryBean{" + "utilisateurId=" + utilisateurId + ", codeCollecteur=" + codeCollecteur + ", nom=" + nom + ", prenom=" + prenom + ", nomConnexion=" + nomConnexion + ", codeAgence=" + codeAgence + ", codeBanque=" + codeBanque + ", codeDevise=" + codeDevise + '}';
}

这不是一个真正的实体,因为它不匹配任何数据库表。 但是@Entity@Id注释是 JPA 必须理解映射的。 如果您真的不想在该类中包含@Entity / @Id ,则可以删除@SqlResultSetMapping注释并将其放在 JPA 可以扫描的任何其他实体中。

您还应该确保您的@ComponentScan包含相应的包,如果您使用基于 java 的 spring 配置,您应该在META-INF目录下的persistence.xml/orm.xml中显式声明您的实体。

这是电话

String connexionQuery = "SELECT u.UTILISATEUR_ID, u.NOM, u.PRENOM, u.NOM_CONNEXION, a.CODE_AGENCE, a.CODE_BANQUE, a.CODE_DEVISE, c.CODE_COLLECTEUR FROM UTILISATEUR u, AGENCE a, COLLECTEUR c "
            + " WHERE (a.CODE_AGENCE = c.CODE_AGENCE AND u.UTILISATEUR_ID = c.UTILISATEUR_ID AND u.NOM_CONNEXION = '"+nomConnextion+"')";

    ConnexionQueryBean ConnexionResults = (ConnexionQueryBean) defaultService.getEntityManager().createNativeQuery(connexionQuery,"ConnexionQueryBean").getSingleResult();
    System.out.println(ConnexionResults.toString());

我正在使用 Spring、JPA 2.1、Hibernate 5 和 Oracle,我认为 JPA 较低版本可能无法做到这一点,找到更多http://www.thoughts-on-java.org/result-set-mapping-complex-映射/

我有一个略有不同的答案,它只是从 Wildloop 的答案中得出的。
这是我的答案:

常量类:Constants.java

public class Constants {
    public final String TESTQUERYRESULT_MAPPING_NAME = "TestQueryResultMapping";
}

结果映射类:TestQueryResult.java

import lombok.Getter;
import lombok.Setter;

import javax.persistence.Entity;
import javax.persistence.EntityResult;
import javax.persistence.FieldResult;
import javax.persistence.Id;
import javax.persistence.SqlResultSetMapping;

@Getter
@Setter
@SqlResultSetMapping(
    //name = "TestQueryResultMapping"
    name = Constants.TESTQUERYRESULT_MAPPING_NAME
    ,entities = @EntityResult(
        entityClass = TestQueryResult.class
        ,fields = {
            @FieldResult(name = "rowId", column = "row_id")
            ,@FieldResult(name = "rowName", column = "row_name")
            ,@FieldResult(name = "value", column = "row_value")
        }
    )
)
@Entity
public class TestQueryResult {
    @Id
    private Integer rowId;

    private String rowName;

    private String value;

}

然后......在我的存储库实现代码中的某个地方:

public class TestQueryRepository {
    //... some code here to get the entity manager here

    public TestQueryResult getTopMost(Integer rowName) {
        //... some code here

        String queryString = "... some query string here" + rowName;
        TestQueryResult testQueryResult = null;

        //this.entityManager.createNativeQuery(queryString ,"TestQueryResultMapping").getResultList();
        List<TestQueryResult> results = this.entityManager.createNativeQuery(queryString ,Constants.TESTQUERYRESULT_MAPPING_NAME).getResultList();
        if (results != null && !results.isEmpty()) {
            testQueryResult = results.get(0);
        }

        return testQueryResult;
    }

}


...然后中提琴! 我得到了一些结果:D!

干杯,
阿塔尼斯·泽拉图

QLRM 可能是一个替代方案: http ://simasch.github.io/qlrm/

它与特定的 JPA 实现无关,也适用于 JDBC。

我最近整理了一个快速的概念证明,它对我自己和另一位开发人员都非常有效。

使用本机查询并使用 SqlResultSetMapping 的 Spring 引导端点,这里是代码片段。

import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.metrics.annotation.Timed;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;

@RestController
@RequestMapping(value = "/rest/yum-query", produces = APPLICATION_JSON_VALUE)
@Api(tags = {"notification"})
@Timed(extraTags = {"controller", "YumController"})
public class YumController {

    private final YumService yumService;

    @Autowired
    public YumController(YumService yumService) {
        this.yumService = yumService;
    }

    @GetMapping(produces = APPLICATION_JSON_VALUE)
    public List<ApprovedApplicationsDTO> findApprovedApplications() {
        return yumService.findApprovedApplications();
    }

}

添加我的服务

import au.edu.qld.qcaa.sate.serviceaara.domain.repository.YumRepositoryCustom;
import au.edu.qld.qcaa.sate.serviceaara.dto.yum.ApprovedApplicationsDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Slf4j
@Service
public class YumService {

    private YumRepositoryCustom yumRepositoryCustom;

    @Autowired
    public YumService(YumRepositoryCustom yumRepositoryCustom) {
        this.yumRepositoryCustom = yumRepositoryCustom;
    }

    public List<ApprovedApplicationsDTO> findApprovedApplications() {
        return yumRepositoryCustom.findApprovedApplicationsNativeQuery();
    }

}

为本地查询设置一个新的查询结果类以映射到

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.ColumnResult;
import javax.persistence.ConstructorResult;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.SqlResultSetMapping;
import java.math.BigInteger;

@MappedSuperclass
@SqlResultSetMapping(name = "ApprovedApplicationQueryResultBean",
        classes = {
                @ConstructorResult(
                        targetClass = ApprovedApplicationQueryResultBean.class,
                        columns = {
                                @ColumnResult(name = "application_id", type = BigInteger.class),
                                @ColumnResult(name = "application_type", type = String.class),
                                @ColumnResult(name = "assessment_identifier", type = String.class),
                                @ColumnResult(name = "aara_code", type = String.class),
                                @ColumnResult(name = "aara_code_child", type = String.class),
                                @ColumnResult(name = "completion_year", type = Integer.class),
                                @ColumnResult(name = "reason", type = String.class),
                                @ColumnResult(name = "code", type = String.class),
                                @ColumnResult(name = "long_description", type = String.class),
                                @ColumnResult(name = "identified_condition", type = String.class),
                                @ColumnResult(name = "other", type = String.class),
                                @ColumnResult(name = "decision_code", type = String.class),
                        }
                )
        })
@NoArgsConstructor
@AllArgsConstructor
@Data
public class ApprovedApplicationQueryResultBean {

    @Id
    private BigInteger applicationId;

    private String applicationType;
    private String assessmentIdentifier;
    private String aaraCode;
    private String aaraCodeChild;
    private Integer completionYear;
    private String reason;
    private String code;
    private String longDescription;
    private String identifiedCondition;
    private String other;
    private String decisionCode;

}

创建一个新的存储库接口和实现类来调用查询

import au.edu.qld.qcaa.sate.serviceaara.dto.yum.ApprovedApplicationsDTO;

import java.util.List;

public interface YumRepositoryCustom {

    List<ApprovedApplicationsDTO> findApprovedApplicationsNativeQuery();

}

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Component
@Slf4j
public class YumRepositoryCustomImpl implements YumRepositoryCustom {

    @PersistenceContext
    private EntityManager em;

    @Override
    public List<ApprovedApplicationsDTO> findApprovedApplicationsNativeQuery() {
        StringBuilder q = new StringBuilder();
        q.append("select distinct a4.application_id, a4.application_type, a3.assessment_identifier, ");
        q.append("a3.aara_code, ad.aara_code_child, a3.completion_year, c.reason, rd.code, rd.long_description, ic.identified_condition, ic.other, ad2.decision_code ");
        q.append("from category c ");
        q.append("left join application a4 on a4.application_id = c.application_id ");
        q.append("left join arrangement a3 on a3.application_id = a4.application_id ");
        q.append("left join arrangement_detail ad on a3.arrangement_id  = ad.arrangement_id ");
        q.append("left join identified_condition ic on ic.category_id = c.category_id ");
        q.append("left join reference_data rd on rd.code = c.reason ");
        q.append("left join arrangement_decision ad2 on ad2.arrangement_id  = a3.arrangement_id ");
        q.append("left JOIN (SELECT max(ad3.arrangement_decision_id) as theid ");
        q.append("FROM arrangement_decision ad3 ");
        q.append("GROUP BY ad3.arrangement_id) b on ad2.arrangement_decision_id = b.theid ");

        q.append("where a4.application_type = ?1 and a3.completion_year = ?2 ");

        q.append("and a4.is_active = true and a3.is_active = true and ic.is_active = true and c.is_active = true ");
        q.append("order by 1 ");

        Query query = em.createNativeQuery(q.toString(), "ApprovedApplicationQueryResultBean");
        query.setParameter(1, ApplicationConstants.APPLICATION_TYPE_AARA);
        query.setParameter(2, Calendar.getInstance().get(Calendar.YEAR));

        List<ApprovedApplicationQueryResultBean> entityResults = query.getResultList();
        return entityResults.stream().map(entity -> {
            return mapToDTO(entity);
        }).collect(Collectors.toList());
    }

    private ApprovedApplicationsDTO mapToDTO(ApprovedApplicationQueryResultBean entity) {
        return ApprovedApplicationsDTO.builder()
                .applicationId(entity.getApplicationId())
                .applicationType(entity.getApplicationType())
                .aaraCode(entity.getAaraCode())
                .aaraCodeChild(entity.getAaraCodeChild())
                .completionYear(entity.getCompletionYear())
                .reason(entity.getReason())
                .code(entity.getCode())
                .longDescription(entity.getLongDescription())
                .identifiedCondition(entity.getIdentifiedCondition())
                .other(entity.getOther())
                .decisionCode(entity.getDecisionCode())
                .build();
    }

}

添加我的端点响应 DTO

import lombok.Builder;
import lombok.Data;

import java.math.BigInteger;

@Data
@Builder
public class ApprovedApplicationsDTO {
    private BigInteger applicationId;
    private String applicationType;
    private String assessmentIdentifier;
    private String aaraCode;
    private String aaraCodeChild;
    private Integer completionYear;
    private String reason;
    private String code;
    private String longDescription;
    private String identifiedCondition;
    private String other;
    private String decisionCode;

}
Then hit my endpoint via postman or curl or which ever tool you wish to use:
http://localhost:10050/rest/yum-query

该解决方案独立于 JPA 实现。 一旦您将本机查询的结果收集为

List<Object[]> = em.createNativeQueryBar(QUERY, "foo").getResultList();

如果您需要将每个 List 元素映射到 Person 例如

Class Person { String name; Int age; }

其中列表元素 [0] 是名称,元素 [1] 是年龄。

您可以使用ObjectMapper将 Object[] 转换为 Person 。 您需要在类上添加 @JsonFormat 注释。

@JsonFormat(shape = JsonFormat.Shape.ARRAY) 
Class Person { String name; Int age; }

并将 Object[] 转换为 Person 为

ObjectMapper mapper = new ObjectMapper();
JSONArray jsonArray = new JSONArray(Arrays.asList(attributes)).toString();
Person person = mapper.readValue(jsonArray, Person.class);
@MappedSuperclass
@SqlResultSetMapping(name="foo",
classes = {
    @ConstructorResult(
            targetClass = Bar.class,
            columns = {
                @ColumnResult(name = "barId", type = Long.class),
                @ColumnResult(name = "barName", type = String.class),
                @ColumnResult(name = "barTotal", type = Long.class)
            })
})

你可以这样做,但这不是很推荐,因为它不是很舒服。 你可以像下面那样做。

Query query = em.createNativeQueryBar(QUERY);
... set some parameters ...
List<Object[]> result = query.getResultList();
List<Bar> list = result.stream().map(
        objects -> {
            Bar bar = new Bar();
            bar.setBarId(Long.parseLong(objects[0].toString()));
            bar.setBarName(objects[1].toString());
            bar.setBarTotal(Long.parseLong(objects[2].toString()));
            ...
            
            return bar;
        }
).collect(Collectors.toList());

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM