编写自定义SpringData Repository的最佳方式是什么?原创
介绍
在本文中,我将向你展示编写自定义 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
:
测试时间
假设我们有两个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 提供程序提供的所有功能。