spring-boot - 我在查询 dsl jpa 实体将 dto 转换为 map 时遇到问题

标签 spring-boot hibernate spring-data-jpa querydsl

注意到:对不起。我不会说英语,所以我使用翻译器。

我使用 spring boot 和 spring data jpa 和 querydsl

我有 2 个实体和 2 个用于 api 服务的 dto

让我先向您展示实体和 dto

//package and imports...

@Entity
@Getter
@Setter(AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Event {

    @Id
    @GeneratedValue
    private Long eventId;
    private String requestBy;
    private String content;
    private String difficulty;
    private String partyType;
    private LocalDate eventDate;
    private LocalTime eventTime;
    private String comment;

    @OneToMany(mappedBy = "event")
    private List<Member> members = new ArrayList<>();

    private Event(Event.Builder builder) {
        this.requestBy = builder.requestBy;
        this.content = builder.content;
        this.difficulty = builder.difficulty;
        this.partyType = builder.partyType;
        this.eventDate = builder.eventDate;
        this.eventTime = builder.eventTime;
        this.comment = builder.comment;
    }

    public void addMember(Member member) {
        this.members.add(member);
        member.joinEvent(this);
    }

    public void addAllMember(Member... members) {
        Arrays.stream(members).forEach(this::addMember);
    }

    @NoArgsConstructor
    public static class Builder {
        //... something builder codes
    }
}
//package and imports...

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class Member {

    @Id
    @GeneratedValue
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    private Event event;

    private String name;
    private String position;

    public Member(String name, String position) {
        this.name = name;
        this.position = position;
    }

    void joinEvent(Event event) {
        this.event = event;
    }

}
//package and imports...

@Getter
@Setter
@NoArgsConstructor
public class EventDto {

    private Long eventId;
    private String requestBy;
    private String content;
    private String difficulty;
    private String partyType;
    private LocalDate eventDate;
    private LocalTime eventTime;
    private String comment;
    private List<MemberDto> members = new ArrayList<>();

    @QueryProjection
    public EventDto(Long eventId, String requestBy, String content, String difficulty, String partyType, LocalDate eventDate, LocalTime eventTime, String comment, List<MemberDto> members) {
        this.eventId = eventId;
        this.requestBy = requestBy;
        this.content = content;
        this.difficulty = difficulty;
        this.partyType = partyType;
        this.eventDate = eventDate;
        this.eventTime = eventTime;
        this.comment = comment;
        this.members = members;
    }

    public static EventDto convertEventToDto(Event event) {
        EventDto dto = new EventDto();

        dto.setEventId(event.getEventId());
        dto.setRequestBy(event.getRequestBy());
        dto.setContent(event.getContent());
        dto.setDifficulty(event.getDifficulty());
        dto.setPartyType(event.getPartyType());
        dto.setEventDate(event.getEventDate());
        dto.setEventTime(event.getEventTime());
        dto.setComment(event.getComment());
        event.getMembers().stream()
                .map(member -> new MemberDto(member.getId(), member.getName(), member.getPosition()))
                .forEach(memberDto -> dto.members.add(memberDto));

        return dto;
    }
}
//package and imports...

@Getter
@Setter
@NoArgsConstructor
public class MemberDto {

    private Long memberId;
    private String name;
    private String position;

    @QueryProjection
    public MemberDto(Long memberId, String name, String position) {
        this.memberId = memberId;
        this.name = name;
        this.position = position;
    }
}

非常简单的实体和 DTO。 事件有成员/每个成员有一个事件。一对多/多对一 一般查询没有问题。

所以我尝试 querydsl 转换,但失败了

我想转换为 Map 所以我在下面进行了查询

public Map<LocalDate, List<EventDto>> findAllDtoByEventDateBetween(LocalDate start, LocalDate end) {
    Map<LocalDate, List<EventDto>> transform = queryFactory
            .from(event)
            .join(event.members, member)
            .where(event.eventDate.between(start, end))
            .transform(
                    groupBy(event.eventDate).as(
                            list(new QEventDto(
                                    event.eventId,
                                    event.requestBy,
                                    event.content,
                                    event.difficulty,
                                    event.partyType,
                                    event.eventDate,
                                    event.eventTime,
                                    event.comment,
                                    list(
                                            new QMemberDto(
                                                    member.id.as("memberId"),
                                                    member.name.as("name"),
                                                    member.position.as("position")
                                            )
                                    )
                            ))
                    )
            );

    return transform;
}

该代码抛出IllegalArgumentException:参数类型不匹配

所以我将其更改为使用投影

public Map<LocalDate, List<EventDto>> findAllDtoByEventDateBetween(LocalDate start, LocalDate end) {
    Map<LocalDate, List<EventDto>> transform = queryFactory
            .from(event)
            .join(event.members, member)
            .where(event.eventDate.between(start, end))
            .transform(
                    groupBy(event.eventDate).as(
                            list(Projections.fields(
                                    EventDto.class,
                                    event.eventId,
                                    event.requestBy,
                                    event.content,
                                    event.difficulty,
                                    event.partyType,
                                    event.eventDate,
                                    event.eventTime,
                                    event.comment,
                                    list(
                                            Projections.fields(
                                                    MemberDto.class,
                                                    member.id.as("memberId"),
                                                    member.name.as("name"),
                                                    member.position.as("position")
                                            )
                                    )
                            ))
                    )
            );

    return transform;
}

它抛出org.springframework.dao.InvalidDataAccessApiUsageException:不支持的表达式new MemberDto(member1.id作为memberId,member1.name作为名称,member1.position作为位置);嵌套异常是 java.lang.IllegalArgumentException: 不支持的表达式 new MemberDto(member1.id as memberId, member1.name as name, member1.position asposition)

如果我使用 groupBy().list() 而不是 groupBy().as(list()) ,它可以完美地制作列表,但我想制作 Map 而无需在外部使用其他循环或流

我可以让 Map 仅使用 QueryDsl 吗?或者必须使用其他逻辑?

感谢阅读我的问题。再次抱歉,我的英语很糟糕。

最佳答案

Querdysl 工厂表达式(投影)不能与组表达式一起使用。对于使用 JPA 的高级 DTO 投影,请查看 Blaze-Persistence Entity Views(也有 Querydsl 集成)的示例。

关于spring-boot - 我在查询 dsl jpa 实体将 dto 转换为 map 时遇到问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74419837/

相关文章:

java - 如何为ProxyFactoryBean设置多个目标并根据Config调用其中一个目标方法?

java - 在数据库中为用户和消息之间的 OneToMany 关系创建 NULL 值

hibernate - 测试 Hibernate JPA 时出错

Spring Data Rest @EmbeddedId 无法从 Post Request 构造

java - 无法访问 Spring H2 控制台

java - Spring-Boot-Jersey 设置 CORS

java - JPA 的 commit() 方法是否使实体分离?

java - 将 Java Arraylist 传递给 JpaRepository 的 native 查询

spring - 使用 Spring JPA 规范相对于直接查询有哪些优势

java - 处理异常时保存不起作用