性能文章>编写自定义SpringData Repository的最佳方式是什么?>

编写自定义SpringData Repository的最佳方式是什么?原创

1年前
253658

介绍

在本文中,我将向你展示编写自定义 Spring Data Repository 的最佳方法。

虽然默认JpaRepository方法以及查询方法在许多情况下都非常方便,但有时你可能需要可以利用任何 JPA 提供程序特定功能的自定义 Repository 方法。

何时使用自定义 Spring Data Repository

假设我们想要获取一对多 DTO 投影,正如我在本文中所解释的那样。

我们的 JPA 查询如下所示:

List<PostDTO> postDTOs = entityManager.createQuery("""
    select p.id as p_id,
           p.title as p_title,
           pc.id as pc_id,
           pc.review as pc_review
    from PostComment pc
    join pc.post p
    order by pc.id
    """)
.unwrap(org.hibernate.query.Query.class)
.setResultTransformer(new PostDTOResultTransformer())
.getResultList();

请注意,我们正在将 JPA 解包Query到 Hibernate org.hibernate.query.Query,以便提供可以从默认的基于表的投影ResultTransformer构建分层父子 DTO 聚合的自定义。Object[]

我们不能只使用常规的 Spring Data Repository 查询方法或@Query注释,因为我们还必须传递我们自己的 Hibernate 特定的ResultTransformer.

因此,我们需要编写一个自定义存储库,它可以为我们提供对底层 JPA 的访问,EntityManager以便我们可以使用特定于 Hibernate 的 API 编写查询。

如何编写自定义 Spring Data Repository

首先,我们需要定义一个接口来提供我们自定义 Repository 方法的方法签名。

public interface CustomPostRepository {

    List<PostDTO> findPostDTOWithComments();
}

其次,我们需要提供CustomPostRepository接口的实现:

public class CustomPostRepositoryImpl implements CustomPostRepository {
     
    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public List<PostDTO> findPostDTOWithComments() {
        return entityManager.createNativeQuery("""
            SELECT p.id AS p_id,
                   p.title AS p_title,
                   pc.id AS pc_id,
                   pc.review AS pc_review
            FROM post p
            JOIN post_comment pc ON p.id = pc.post_id
            ORDER BY pc.id
            """)
        .unwrap(org.hibernate.query.Query.class)
        .setResultTransformer(new PostDTOResultTransformer())
        .getResultList();
    }
}

第三,我们需要让默认的 Spring Data JPAPostRepository扩展我们的CustomPostRepository接口:

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

一张图片胜过 100 个字,所以这里有一个图表向你展示自定义 Spring Data Repository 如何与标准 Spring Data Repository 相关联JpaRepository

自定义 Spring Data Repository 类关系

测试时间

假设我们有两个Post实体,第一个有两个PostComment子实体,第二个Post有一个子实体PostComment

entityManager.persist(
    new Post()
        .setId(1L)
        .setTitle("High-Performance Java Persistence")
        .addComment(
            new PostComment()
                .setId(1L)
                .setReview("Best book on JPA and Hibernate!")
        )
        .addComment(
            new PostComment()
                .setId(2L)
                .setReview("A must-read for every Java developer!")
        )
);
 
 
entityManager.persist(
    new Post()
        .setId(2L)
        .setTitle("Hypersistence Optimizer")
        .addComment(
            new PostComment()
                .setId(3L)
                .setReview("It's like pair programming with Vlad!")
        )
);

调用该findPostDTOWithComments方法时,我们将获得预期的PostDTO层次投影:

List<PostDTO> postDTOs = forumService.findPostDTOWithComments();
 
assertEquals(2, postDTOs.size());
assertEquals(2, postDTOs.get(0).getComments().size());
assertEquals(1, postDTOs.get(1).getComments().size());
 
PostDTO post1DTO = postDTOs.get(0);
 
assertEquals(1L, post1DTO.getId().longValue());
assertEquals(2, post1DTO.getComments().size());
assertEquals(1L, post1DTO.getComments().get(0).getId().longValue());
assertEquals(2L, post1DTO.getComments().get(1).getId().longValue());
 
PostDTO post2DTO = postDTOs.get(1);
 
assertEquals(2L, post2DTO.getId().longValue());
assertEquals(1, post2DTO.getComments().size());
assertEquals(3L, post2DTO.getComments().get(0).getId().longValue());

 

结论

虽然看到扩展JpaRepository接口的标准 Spring Data 存储库非常常见,但自定义存储库可以让你利用 JPA 或底层 JPA 提供程序提供的所有功能。

点赞收藏
VladMihalcea

My name is Vlad Mihalcea, and I’m a Java Champion. I wrote the High-Performance Java Persistence book, which became one of the best-selling Java books on Amazon.

请先登录,查看5条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步
8
5