繁体   English   中英

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

[英]Mapping JPA or Hibernate projection query to DTO (Data Transfer Object)

在我的 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();
}

所以,为了读取数据,我必须使用循环(带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);

}

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

您有很多选项可以将投影映射到 DTO 结果集:

使用元组和 JPQL 的 DTO 投影

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

使用构造函数表达式和 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();

您还可以从 JPA 构造函数表达式中省略 DTO 包名称,并通过其简单的 Java 类名称(例如PostDTO )引用 DTO。

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

使用元组和本机 SQL 查询的 DTO 投影

这个可以从 Hibernate 5.2.11 获得,所以还有一个升级的理由。

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

使用 ConstructorResult 的 DTO 投影

如果我们使用前面介绍的相同PostDTO类类型,我们必须提供以下@SqlResultSetMapping

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

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

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

使用 ResultTransformer 和 JPQL 的 DTO 投影

这一次,您的 DTO 需要具有您需要 Hibernate 从底层 JDBC ResultSet填充的属性的设置器。

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

使用 ResultTransformer 和 Native SQL 查询的 DTO 投影

List postDTOs = entityManager.createNativeQuery("""
    select
           p.id as \"id\",
           p.title as \"title\"
    from Post p
    where p.created_on > :fromTimestamp
    """)
.setParameter( "fromTimestamp", Timestamp.from(
    LocalDateTime.of( 2016, 1, 1, 0, 0, 0 ).toInstant( ZoneOffset.UTC ) ))
.unwrap( org.hibernate.query.NativeQuery.class )
.setResultTransformer( Transformers.aliasToBean( PostDTO.class ) )
.getResultList();

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

以下是如何使用投影根据街道名称将地址组合在一起的完整示例。

Criteria criteria = getCurrentSession().createCriteria(Address.class);
// adding condition
criteria.add(Restrictions.eq("zip", "12345"));
// adding projection
criteria.setProjection(Projections.projectionList()
.add(Projections.groupProperty("streetName"), "streetName")
.add(Projections.count("apartment"), "count"));
// set transformer
criteria.setResultTransformer(new AliasToBeanResultTransformer(SomeDTO.class));

List<SomeDTO> someDTOs = criteria.list();

someDTOs 列表将包含按 streetName 列出的结果组数。 每个 SomeDTO 对象都包含街道名称和该街道的公寓数量。

一些DTO.java

public class SomeDTO{

private String streetName;
private Long count;

public void setStreetName(String streetName){
    this.streetName=streetName;
}
public String getStreetName(){
    return this.streetName;
}
public Long getCount() {
    return count;
}
public void setCount(Long count) {
    this.count = count;
}
}

这正是创建Blaze-Persistence 实体视图的用例!

你的 DTO 看起来像

@EntityView(Category.class)
interface DQCategoryDTO  {
  String getTitle();
  @Mapping("SIZE(sentences)")
  int getCount();
}

如果您使用 Spring Data,则可以在类似的存储库中使用它

interface CategoryRepository extends Repository<Category, Long> {
  List<DQCategoryDTO> findAll(Pageable pageable);
}

暂无
暂无

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

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