У меня есть следующие две сущности:
@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?