In general you are correct.
Repository is a design pattern similar to DAO (Data Access Object) in that its purpose is to abstract data access in a generic way from its model. The difference is that the Repository tries to represent the data as a collection of elements, even remembering a Collection
.
The Spring Data Jpa project makes it easy to implement the Repository pattern through AOP (Aspect Oriented Programming).
Using only one interface, Spring will dynamically "generate" the implementation of data access methods. Extending the JpaRepository
interface is optional, but the advantage is it already comes with several generic CRUD methods and you do not need to reset all of them.
At first it may be a bit strange to use this Spring project. Who does not know AOP and how Spring Data works will waste some time looking for the implementation of the interfaces.
Creating new methods simply through signatures is very easy. Here's an example:
@Repository
public interface ClienteRepository extends JpaRepository<Cliente, Long> {
Cliente findByNome(String nome);
Page<Cliente> findByCidadeAndEstado(Cidade cidade, Estado, estado, Pageable pageable);
}
At first we can ask: what does Spring do with it?
First the findBy
prefix in method names means that it will be a query method.
What comes next is as an expression that defines which attributes will be used as filters. In the first example, findByNome
, means, search for the nome
attribute using the value passed in the first parameter. In the second example, the generated query will use an expression with the AND
operator, considering the attributes cidade
and estado
.
In addition, the special parameter of type Pageable
says that the result will be paged. Note that the method return is a "customer page".
See the documentation on creating queries for more details.
Alternatively, you can specify a JPA or any native query through the @Query annotation. See documentation .
JPA query example:
public interface UserRepository extends JpaRepository<User, Long> {
@Query("select u from User u where u.firstname like %?1")
List<User> findByFirstnameEndsWith(String firstname);
}
Native query example:
public interface UserRepository extends JpaRepository<User, Long> {
@Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?0", nativeQuery = true)
User findByEmailAddress(String emailAddress);
}
Customizing Spring Data Jpa
There are basically two ways to further customize Spring Data JPA:
Creating a specific implementation
If for just one case you really need to do an implementation of methods, it is possible. For this, if you have an interface ClienteRepository
:
Create a ClienteRepositoryCustom
interface with the methods you want to implement.
Create a ClienteRepositoryImpl
class in the same package by implementing this interface.
Make interface ClienteRepository
extend interface ClienteRepositoryCustom
See the documentation .
Creating a "generic" interface
If you have methods that you want to have in all repositories but are not present in the JpaRepository
interface, you can create a generic interface with these methods and make your repositories extend that interface.
Replacing the JpaRepository implementation
Well, all the magic has a trick behind the scenes. There is, yes, an implementation of it all and you can extend it to fit your goals.
I even did this in a recent project to allow a dynamic search using a map, where each map entry is automatically added to the search clauses.
I believe that describing the entire process is outside the scope of the response, as well as lengthening the content a lot. So I'll leave the link to one of the articles I used to do the implementation is Customizing Spring Data JPA Repository .
On the one hand, creating your extended version of JpaRepository
is not very complicated, but I suggest you try to solve specific issues using the more specific approaches already mentioned. Think twice before creating or overwriting methods that affect all your classes.