将JPA或Hibernate投影查询映射到DTO(数据传输对象)

Tha*_*ran 13 java spring hibernate jpa dozer

在我的DAO层中,我有一个像这样的Find函数

public List<?> findCategoryWithSentenceNumber(int offset, int maxRec) {
  Criteria crit = getSession().createCriteria(Category.class, "cate");
    crit.createAlias("cate.sentences", "sent");

    crit.setProjection(Projections.projectionList().
    add(Projections.property("title"), "title").
    add(Projections.count("sent.id"), "numberOfSentence").
    add(Projections.groupProperty("title"))
  );

  crit.setFirstResult(offset);
  crit.setMaxResults(maxRec);

  return crit.list();
}
Run Code Online (Sandbox Code Playgroud)

所以,为了读取数据,我必须使用一个循环(带Iterator)

List<?> result = categoryDAO.findCategoryWithSentenceNumber(0, 10);
// List<DQCategoryDTO> dtoList = new ArrayList<>(); 

for (Iterator<?> it = result.iterator(); it.hasNext(); ) {
  Object[] myResult = (Object[]) it.next();

  String  title = (String) myResult[0];
  Long count = (Long) myResult[1];


  assertEquals("test", title); 
  assertEquals(1, count.intValue()); 

  // dQCategoryDTO = new DQCategoryDTO();
  // dQCategoryDTO.setTitle(title);
  // dQCategoryDTO.setNumberOfSentence(count);
  // dtoList.add(dQCategoryDTO);

}
Run Code Online (Sandbox Code Playgroud)

我的问题是:是否有任何api,框架可以轻松地将List<?> resultin 转换为DTO对象列表(例如,DQCategoryDTO)而不使用任何循环,迭代器和调用setter/getter来填充值?

Vla*_*cea 13

正如我在本文中所解释的那样,您有很多选项可以将投影映射到DTO结果集:

DTO使用Tuple和JPQL进行投影

List<Tuple> postDTOs = entityManager.createQuery(
    "select " +
    "       p.id as id, " +
    "       p.title as title " +
    "from Post p " +
    "where p.createdOn > :fromTimestamp", Tuple.class)
.setParameter( "fromTimestamp", Timestamp.from(
    LocalDateTime.of( 2016, 1, 1, 0, 0, 0 )
        .toInstant( ZoneOffset.UTC ) ))
.getResultList();

assertFalse( postDTOs.isEmpty() );

Tuple postDTO = postDTOs.get( 0 );
assertEquals( 
    1L, 
    postDTO.get( "id" ) 
);
Run Code Online (Sandbox Code Playgroud)

使用Constructor Expressionand和JPQL进行DTO投影

List<PostDTO> postDTOs = entityManager.createQuery(
    "select new com.vladmihalcea.book.hpjp.hibernate.query.dto.projection.jpa.PostDTO(" +
    "    p.id, " +
    "    p.title " +
    ") " +
    "from Post p " +
    "where p.createdOn > :fromTimestamp", PostDTO.class)
.setParameter( "fromTimestamp", Timestamp.from(
    LocalDateTime.of( 2016, 1, 1, 0, 0, 0 )
        .toInstant( ZoneOffset.UTC ) ))
.getResultList();
Run Code Online (Sandbox Code Playgroud)

使用Tuple和本机SQL查询进行DTO投影

这个可以从Hibernate 5.2.11获得,因此还有一个升级的理由.

List<PostDTO> postDTOs = entityManager.createQuery(
  "select new PostDTO(" +
  "    p.id, " +
  "    p.title " +
  ") " +
  "from Post p " +
  "where p.createdOn > :fromTimestamp", PostDTO.class)
.setParameter( "fromTimestamp", Timestamp.from(
  LocalDateTime.of( 2016, 1, 1, 0, 0, 0 )
      .toInstant( ZoneOffset.UTC ) ))
.getResultList();
Run Code Online (Sandbox Code Playgroud)

使用ConstructorResult进行DTO投影

如果我们使用PostDTO之前介绍的相同类类型,我们必须提供以下内容PostDTO:

List<Tuple> postDTOs = entityManager.createNativeQuery(
    "SELECT " +
    "       p.id AS id, " +
    "       p.title AS title " +
    "FROM Post p " +
    "WHERE p.created_on > :fromTimestamp", Tuple.class)
.setParameter( "fromTimestamp", Timestamp.from(
    LocalDateTime.of( 2016, 1, 1, 0, 0, 0 )
        .toInstant( ZoneOffset.UTC ) ))
.getResultList();
Run Code Online (Sandbox Code Playgroud)

现在,名为native query的SQL投影执行如下:

@NamedNativeQuery(
    name = "PostDTO",
    query =
        "SELECT " +
        "       p.id AS id, " +
        "       p.title AS title " +
        "FROM Post p " +
        "WHERE p.created_on > :fromTimestamp",
    resultSetMapping = "PostDTO"
)
@SqlResultSetMapping(
    name = "PostDTO",
    classes = @ConstructorResult(
        targetClass = PostDTO.class,
        columns = {
            @ColumnResult(name = "id"),
            @ColumnResult(name = "title")
        }
    )
)
Run Code Online (Sandbox Code Playgroud)

使用ResultTransformer和JPQL进行DTO投影

这一次,你的DTO需要为你需要Hibernate的属性设置setter来从底层的JDBC中填充@SqlResultSetMapping.

DTO预测如下:

List<PostDTO> postDTOs = entityManager.createNamedQuery("PostDTO")
.setParameter( "fromTimestamp", Timestamp.from(
    LocalDateTime.of( 2016, 1, 1, 0, 0, 0 )
        .toInstant( ZoneOffset.UTC ) ))
.getResultList();
Run Code Online (Sandbox Code Playgroud)

使用ResultTransformer和Native SQL查询进行DTO投影

List<PostDTO> postDTOs = entityManager.createQuery(
    "select " +
    "       p.id as id, " +
    "       p.title as title " +
    "from Post p " +
    "where p.createdOn > :fromTimestamp")
.setParameter( "fromTimestamp", Timestamp.from(
    LocalDateTime.of( 2016, 1, 1, 0, 0, 0 ).toInstant( ZoneOffset.UTC ) ))
.unwrap( org.hibernate.query.Query.class )
.setResultTransformer( Transformers.aliasToBean( PostDTO.class ) )
.getResultList();
Run Code Online (Sandbox Code Playgroud)


Sha*_*dra 5

您可以使用ResultTransformer,它可以将别名转换为 bean (DTO) 属性。有关用法,您可以参考此处的 Hibernate 文档,第 13.1.5 节