gpt4 book ai didi

java - 使用 JPQL 或 HQL 时如何将 @OneToMany 字段映射到 List

转载 作者:行者123 更新时间:2023-12-01 14:34:12 28 4
gpt4 key购买 nike

我有以下实体:

@Entity
public class CityExpert {
@Id
private long id;

@OneToOne
private User user;

@OneToMany(mappedBy = "cityExpert")
private List<CityExpertDocument> documents;

// Lots of other fields...
}

@Entity
public class CityExpertDocument {

@Id
private long id;

@ManyToOne
private CityExpert cityExpert;

// Lots of other fields...
}

@Entity
public class User {
@Id
private long id;

private String name;

private String email;

// Lots of other fields...
}

我有以下 HQL 查询,我在其中选择了 CityExpert 的一个子集小号:

"select " +
"e " +
"from " +
"CityExpert e " +
"where " +
"( (lower(e.user.name) like concat('%', lower(?1), '%') or e.user.name is null) or ?1 = '' ) " +
"and " +
"( (lower(e.user.phone) like concat('%', lower(?2), '%') or e.user.phone is null) or ?2 = '' ) "

但是,由于CityExpert的字段太多了, 我不想选择所有字段。因此,我将查询更改如下:

"select " +
"e.user.name, " +
"e.user.email, " +
"e.documents " +
"from " +
"CityExpert e " +
"where " +
"( (lower(e.user.name) like concat('%', lower(?1), '%') or e.user.name is null) or ?1 = '' ) " +
"and " +
"( (lower(e.user.phone) like concat('%', lower(?2), '%') or e.user.phone is null) or ?2 = '' ) "

但是,显然我们不能在这样的实体中选择一对多字段,因为我得到的是 MySQLSyntaxErrorException使用前面的查询(请参阅 this question )。因此,我已将查询更改为以下内容:

"select " +
"e.user.name, " +
"e.user.email, " +
"d " +
"from " +
"CityExpert e " +
"left join " +
"e.documents d" +
"where " +
"( (lower(e.user.name) like concat('%', lower(?1), '%') or e.user.name is null) or ?1 = '' ) " +
"and " +
"( (lower(e.user.phone) like concat('%', lower(?2), '%') or e.user.phone is null) or ?2 = '' ) "

然而,这次结果变成了 List<Object[]> , 而不是 List<CityExpert> .

我创建了以下 DTO:

public class CityExpertDTO {

private String name;
private String email;
private List<CityExpertDocument> documents;

}

但是,我不知道应该如何将 Hibernate 返回的结果映射到 List<CityExpertDTO> .我的意思是,我可以手动执行此操作,但肯定有 Hibernate 提供的自动化解决方案。

我正在使用 Spring Data JPA 并按如下方式使用 HQL:

public interface CityExpertRepository extends JpaRepository<CityExpert, Long> {

@Query(
"select " +
"e " +
"from " +
"CityExpert e " +
"where " +
"( (lower(e.user.name) like concat('%', lower(?1), '%') or e.user.name is null) or ?1 = '' ) " +
"and " +
"( (lower(e.user.phone) like concat('%', lower(?2), '%') or e.user.phone is null) or ?2 = '' ) "
)
Set<CityExpert> findUsingNameAndPhoneNumber(String name,
String phoneNumber);

}

如何将结果映射到 CityExpertDTO

最佳答案

表关系

假设我们有以下 postpost_comment表,通过 post_id 形成一对多关系post_comment 中的外键列表。

The post and post_comment tables used for the JPA DTO projection

SQL 投影

考虑到我们有一个只需要获取 id 的用例和 title来自 post 的列表,以及 idreview来自 post_comment 的列表,我们可以使用以下 JPQL 查询来获取所需的投影:

select p.id as p_id, 
p.title as p_title,
pc.id as pc_id,
pc.review as pc_review
from PostComment pc
join pc.post p
order by pc.id

运行上面的投影查询时,我们得到以下结果:

| p.id | p.title                           | pc.id | pc.review                             |
|------|-----------------------------------|-------|---------------------------------------|
| 1 | High-Performance Java Persistence | 1 | Best book on JPA and Hibernate! |
| 1 | High-Performance Java Persistence | 2 | A must-read for every Java developer! |
| 2 | Hypersistence Optimizer | 3 | It's like pair programming with Vlad! |

DTO 投影

但是,我们不想使用基于表格的 ResultSet或默认 List<Object[]> JPA 或 Hibernate 查询投影。我们想将上述查询结果集转换为 ListPostDTO对象,每个这样的对象都有一个 comments包含所有相关 PostCommentDTO 的集合对象:

The PostDTO and PostCommentDTO used for DTO projection

我们可以使用 Hibernate ResultTransformer , 如以下示例所示:

List<PostDTO> postDTOs = entityManager.createQuery("""
select p.id as p_id,
p.title as p_title,
pc.id as pc_id,
pc.review as pc_review
from PostComment pc
join pc.post p
order by pc.id
""")
.unwrap(org.hibernate.query.Query.class)
.setResultTransformer(new PostDTOResultTransformer())
.getResultList();

assertEquals(2, postDTOs.size());
assertEquals(2, postDTOs.get(0).getComments().size());
assertEquals(1, postDTOs.get(1).getComments().size());

PostDTOResultTransformer将定义 Object[] 之间的映射投影和 PostDTO包含 PostCommentDTO 的对象子 DTO 对象:

public class PostDTOResultTransformer 
implements ResultTransformer {

private Map<Long, PostDTO> postDTOMap = new LinkedHashMap<>();

@Override
public Object transformTuple(
Object[] tuple,
String[] aliases) {

Map<String, Integer> aliasToIndexMap = aliasToIndexMap(aliases);

Long postId = longValue(tuple[aliasToIndexMap.get(PostDTO.ID_ALIAS)]);

PostDTO postDTO = postDTOMap.computeIfAbsent(
postId,
id -> new PostDTO(tuple, aliasToIndexMap)
);

postDTO.getComments().add(
new PostCommentDTO(tuple, aliasToIndexMap)
);

return postDTO;
}

@Override
public List transformList(List collection) {
return new ArrayList<>(postDTOMap.values());
}
}

aliasToIndexMap只是一个小实用程序,它允许我们构建一个 Map将列别名与列值在 Object[] 中所在索引相关联的结构tuple数组:

public  Map<String, Integer> aliasToIndexMap(
String[] aliases) {

Map<String, Integer> aliasToIndexMap = new LinkedHashMap<>();

for (int i = 0; i < aliases.length; i++) {
aliasToIndexMap.put(aliases[i], i);
}

return aliasToIndexMap;
}

postDTOMap是我们要存储所有 PostDTO 的地方最终将由查询执行返回的实体。我们使用 postDTOMap 的原因就是父行在每个子记录的SQL查询结果集中都是重复的。

computeIfAbsent方法允许我们创建一个 PostDTO仅当不存在 PostDTO 时才对象引用已存储在 postDTOMap 中.

PostDTO类有一个可以设置 id 的构造函数和 title使用专用列别名的属性:

public class PostDTO {

public static final String ID_ALIAS = "p_id";

public static final String TITLE_ALIAS = "p_title";

private Long id;

private String title;

private List<PostCommentDTO> comments = new ArrayList<>();

public PostDTO(
Object[] tuples,
Map<String, Integer> aliasToIndexMap) {

this.id = longValue(tuples[aliasToIndexMap.get(ID_ALIAS)]);
this.title = stringValue(tuples[aliasToIndexMap.get(TITLE_ALIAS)]);
}

//Getters and setters omitted for brevity
}

PostCommentDTO以类似的方式构建:

public class PostCommentDTO {

public static final String ID_ALIAS = "pc_id";

public static final String REVIEW_ALIAS = "pc_review";

private Long id;

private String review;

public PostCommentDTO(
Object[] tuples,
Map<String, Integer> aliasToIndexMap) {
this.id = longValue(tuples[aliasToIndexMap.get(ID_ALIAS)]);
this.review = stringValue(tuples[aliasToIndexMap.get(REVIEW_ALIAS)]);
}

//Getters and setters omitted for brevity
}

就是这样!

使用 PostDTOResultTransformer ,SQL 结果集可以转换为分层 DTO 投影,使用起来非常方便,尤其是在需要将其编码为 JSON 响应时:

postDTOs = {ArrayList}, size = 2
0 = {PostDTO}
id = 1L
title = "High-Performance Java Persistence"
comments = {ArrayList}, size = 2
0 = {PostCommentDTO}
id = 1L
review = "Best book on JPA and Hibernate!"
1 = {PostCommentDTO}
id = 2L
review = "A must read for every Java developer!"
1 = {PostDTO}
id = 2L
title = "Hypersistence Optimizer"
comments = {ArrayList}, size = 1
0 = {PostCommentDTO}
id = 3L
review = "It's like pair programming with Vlad!"

关于java - 使用 JPQL 或 HQL 时如何将 @OneToMany 字段映射到 List<DTO>?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47965064/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com