JPA-проект Spring Data с ManyToOne - PullRequest
       99

JPA-проект Spring Data с ManyToOne

0 голосов
/ 30 апреля 2020

У меня есть следующие две сущности:

@Entity
public class Author {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String userName;
    // Getters/setters
}

@Entity
public class Comment {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "author_id")
    private Author author;

    private String comment;
    // Getters/setters
}

Я хочу сгенерировать запрос JPQL в репозитории Spring Data JPA, который дает мне авторов со всеми их комментариями. Мой прогноз -

public interface AuthorProjection {
    String getUserName();
    List<String> getComments();
}

, а мой запрос JPQL -

public interface AuthorRepository extends JpaRepository<Author, Long> {

    @Query(value= "SELECT a.id as id, \n" +
            "    a.userName as userName,\n" +
            "    c.comment as comments\n" +
            "from Author a \n" +
            "left join Comment c on c.author = a")
    List<AuthorProjection> authorsWithComments();
}

public interface CommentRepository extends JpaRepository<Comment, Long> { 
}

. При этом генерируется следующий необработанный запрос SQL через Hibernate (базовая база данных - PostgreSQL):

select author0_.id as col_0_0_, author0_.user_name as col_1_0_,comment1_.comment as col_2_0_ from author author0_ left outer join comment comment1_ on (comment1_.author_id=author0_.id)

Мои данные испытаний состоят из одного автора с двумя комментариями. К сожалению, когда я запускаю это, я всегда получаю двух авторов (на самом деле это один и тот же Автор) с одним комментарием каждый. Это, конечно, то, как DB возвращает каждую строку (raw SQL выглядит хорошо и как это должно быть). Возможно ли, чтобы Hibernate или Spring Data JPA (или кто-то ответственный) объединял все комментарии в список, или мне нужно написать Java код, который объединяет строки на основе идентификатора автора?

Если возможно, я бы хотел, чтобы БД выполняла все преобразования данных и имела хорошо структурированную проекцию в качестве вывода из запроса.

Мой тест для демонстрации:

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class JpatestApplicationTests {

    @Autowired
    private AuthorRepository authorRepository;

    @Autowired
    private CommentRepository commentRepository;

    @Test
    void contextLoads() {
        var author = new Author();
        author.setUserName("John Doe");
        var savedAuthor = authorRepository.save(author);

        var comment1 = new Comment();
        comment1.setComment("First comment").setAuthor(savedAuthor);
        commentRepository.save(comment1);

        var comment2 = new Comment();
        comment2.setComment("Second comment").setAuthor(savedAuthor);
        commentRepository.save(comment2);

        authorRepository.authorsWithComments().forEach(
                projection -> System.out.printf("%s, %s\n", projection.getUserName(), projection.getComments())
        );
    }
}

, который дает следующий вывод:

John Doe, [First comment]
John Doe, [Second comment]

И запустить непосредственно в PostgreSQL, он дает:

 col_0_0_ | col_1_0_ |    col_2_0_
----------+----------+----------------
        1 | John Doe | First comment
        1 | John Doe | Second comment
(2 rows)

Мой ожидаемый Java вывод:

John Doe, [First comment, Second comment]

Это возможно даже с JPA?

Ответы [ 2 ]

1 голос
/ 03 мая 2020

Используйте отношение в Author сущности, чтобы получить комментарии автора.

@OneToMany(mappedBy = "author")
private List<Comment> comments = new ArrayList<>();

Затем запросите этот путь

@Query(value= "SELECT a.userName as userName, a.comments as comments from Author a")
List<AuthorProjection> authorsWithComments();

И проекцию как

public interface AuthorProjection {
    String getUserName();
    List<Comment> getComments();
}
0 голосов
/ 03 мая 2020

Вы получаете все комментарии, просто запросив авторов. Вам нужно добавить дополнительный список в классе Author.

@OneToMany(mappedBy = "author", fetch=FetchType.LAZY)
private List<Comment> comments = new ArrayList<>();

Затем вы можете использовать JAP Reposity

@Repository
public interface AuthorRepository  extends CrudRepository<Author, Long> {

}

CrudRepository, уже реализовав множество полезных API, таких как findAll(), findById(), вам не нужно писать никаких sqls.

...