使用ManyToOne的Spring Data JPA投影。

问题描述 投票:0回答:1

我有以下两个实体。

@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
}

我想在Spring Data JPA存储库中生成一个JPQL查询 给我提供作者和他们所有的评论。我的预测是

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> { 
}

这将通过Hibernate生成以下原始SQL查询(底层DB是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返回每条记录的方式(原始SQL看起来很好,应该是这样)。Hibernate或Spring Data JPA(或任何负责的)是否可以将所有注释合并到列表中,或者我是否需要编写Java代码,根据作者id合并行?

如果可能的话,我希望DB已经完成了所有的数据转换,并且有一个结构良好的Projection作为查询的输出。

我的演示测试是

@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中能实现吗?

java spring-data-jpa jpql
1个回答
1
投票

使用关系在 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
投票

你只需要查询作者就可以得到所有的评论。你需要在Author类中添加额外的List,然后,你可以使用JAP Reposity。

@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(),你不需要写任何sql。

© www.soinside.com 2019 - 2024. All rights reserved.