Как создать вложенную проекцию отображения один-ко-многим в весенние данные jpa - PullRequest
0 голосов
/ 11 марта 2020

У меня есть сопоставление один ко многим с таблицей post и post_comments, наше требование состоит в том, чтобы получить только несколько значений в обеих таблицах и отправить обратно вызывающей стороне как одно в сопоставление Many, как postDTO. Ниже приведен наш код.

Post Entity

@Entity(name = "Post")
@Getter
@Setter
public class Post {

    @Id
    private Long id;

    private String title;

    private LocalDateTime createdOn;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "post", orphanRemoval = true)
    private List<PostComment> comments = new ArrayList<>();

    public void addComment(PostComment comment) {
       this.comments.add(comment);
       comment.setPost(this);
    }

}

PostCommentEntity

@Getter
@Setter
public class PostComment {

    @Id
    private Long id;

    private String review;

    private LocalDateTime createdOn;

    public PostComment(String review) {
        this.review = review;
        this.createdOn = LocalDateTime.now();
    }

    @ManyToOne
    private Post post;

}

postDTO -> Требуемый формат ответа, который нам нужен.

@Getter
@Setter
@Builder
@ToString
public class PostDTO {

    String title;

    @Builder.Default
    List<PostCommentsDTO> comments;
}

PostCommentsDTO -> Вложенное значение проекции один ко многим.

@Data
@Builder
public class PostCommentsDTO {

    String review;

}

Как мы не могли Это не может быть достигнуто напрямую с использованием данных весны jpa. Достигается с использованием альтернативного сопоставления.

PostRepository Нам нужно получить только заголовок из таблицы публикаций и отзывы из таблицы посткомментариев, требуемые в качестве класса postDTO, поскольку мы не можем выполнить сопоставление в одном экземпляре, который я делегирую отображение в Java, как показано ниже, путем создания промежуточной проекции.

@Repository
public interface PostRepository extends JpaRepository<Post, Long> {

    @Query("SELECT p.title as title, c.review as review FROM Post p JOIN p.comments c where p.title = :title")
    List<PostCommentProjection> findByTitle(@Param("title") String title);
}

PostCommentProjection

public interface PostCommentProjection {

    String getTitle();

    String getReview();

}

И, наконец, в Java

    List<PostCommentProjection> postCommentProjections = this.postRepository.findByTitle("Post Title");

    final Function<Entry<String, List<PostComments>>, PostDTO> mapToPostDTO = entry -> PostDTO.builder()
            .title(entry.getKey()).comments(entry.getValue()).build();
    final Function<PostCommentProjection, String> titleClassifier = PostCommentProjection::getTitle;
    final Function<PostCommentProjection, PostComments> mapToPostComments = postCommentProjection -> PostComments
            .builder().review(postCommentProjection.getReview()).build();
    final Collector<PostCommentProjection, ?, List<PostComments>> downStreamCollector = Collectors
            .mapping(mapToPostComments, Collectors.toList());

    List<PostDTO> postDTOS = postCommentProjections.stream()
            .collect(groupingBy(titleClassifier, downStreamCollector)).entrySet().stream().map(mapToPostDTO)
            .collect(toUnmodifiableList());

Существует ли эффективный или автоматический c способ получения проекта POSTDTO непосредственно из репозитория?

1 Ответ

0 голосов
/ 11 марта 2020

Немного странно, что вы ищете комментарии по названию. Заголовок не уникален, может быть 10 разных постов с одинаковым заголовком, поэтому вы получите комментарии для всех 10 постов. Я предлагаю вам искать комментарии по идентификатору поста.

@Repository
public interface PostCommentRepository extends JpaRepository<PostComment, Long> {

    //not entirely sure if that is valid jpql, should work. 
    // Otherwise accept "Long postId", then sql: select review from post_comment where post_id = :postId
    //can also do it with projection
    @Query("select c.review from PostComment c where c.post = :post")
    List<String> findAllByPost(Post post);
}

... main() {
   ...
   List<String> reviews = commentRepo.findAllByPost(post);
   PostDTO dto = new PostDTO(post.getTitle(), reviews);
}

Кроме того, поскольку ваша сущность PostComment не так уж дорога, я также мог бы посчитать это решение не таким уж плохим:

... main() {
    ...
    List<String> reviews = post.getComments().stream().map(PostComment::getReview).collect(toList());
    PostDTO dto = new PostDTO(post.getTitle(), reviews);
}
...