The best way to write a custom Spring Data Repository
source link: https://vladmihalcea.com/custom-spring-data-repository/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
If you are trading Stocks and Crypto using Revolut, then you are going to love RevoGain!
Introduction
In this article, I’m going to show you the best way to write a custom Spring Data Repository.
While the default JpaRepository
methods, as well as the query methods, are very convenient in many situations, there might be times when you need custom Repository methods that can take advantage of any JPA provider-specific functionality.
When to use a custom Spring Data Repository
Let’s assume we want to fetch a one-to-many DTO projection, as I explained in this article.
Our JPA query looks like this:
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();
Notice that we are unwrapping the JPA Query
to a Hibernate org.hibernate.query.Query
in order to provide a custom ResultTransformer
that can build a hierarchical parent-child DTO aggregate from the default table-based Object[]
projection.
We can’t just use a regular Spring Data Repository query method or a @Query
annotation because we also have to pass our own Hibernate-specific ResultTransformer
.
Therefore, we need to write a custom Repository that can provide us access to the underlying JPA EntityManager
so that we can write our query using the Hibernate-specific API.
How to write a custom Spring Data Repository
First, we need to define an interface that provides the method signatures of our custom Repository methods.
public
interface
CustomPostRepository {
List<PostDTO> findPostDTOWithComments();
}
Second, we need to provide an implementation of the CustomPostRepository
interface:
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();
}
}
Third, we need to make the default Spring Data JPA PostRepository
extend our CustomPostRepository
interface:
@Repository
public
interface
PostRepository
extends
JpaRepository<Post, Long>, CustomPostRepository {
}
A picture is worth 100 words, so here’s a diagram that shows you how the custom Spring Data Repository is associated to the standard JpaRepository
one:
Testing time
Assuming we have two Post
entities, the first one having two PostComment
child entities, and the second Post
having a single PostComment
child:
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!"
)
)
);
When calling the findPostDTOWithComments
method, we are going to get the expected PostDTO
hierarchical projection:
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());
Cool, right?
If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.
Conclusion
While it’s very common to see standard Spring Data repositories that extend the JpaRepository
interface, a custom Repository can allow you to take advantage of all the features provided by JPA or the underlying JPA provider.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK