Part 1. Repository
1.1. Introduce
Building and implementing the data access layer in applications sometimes causes a lot of trouble for the programmer due to its cumbersome and complex. Although there are now many libraries and frameworks supporting it, we still cannot avoid rewriting too much pre-written code in libraries, and our code will become more and more “messed” . To minimize the impact of the above problem, Spring provides us with a solution that is abstract reposity , they are designed based on interfaces, significantly reducing pre-written code and the impact of having to execute too much. layers in the data access layer.
1.2. Core concepts
The most basic and core part of the Spring Data abstract repository is an interface called Repository , which accepts an Entity class
corresponding to the java class representing a table in our database, and the data type of the ID
field. in that table. This interface provides several functions that revolve around the CRUD entity it manages, as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public interface Repository<T, ID extends Serializable> { T save(T entity); T findById(ID primaryKey); List<T> findAll(); Page<T> findAll(Pageable pageable); Long count(); void delete(T entity); boolean exists(ID primaryKey); // … more functionality omitted. } |
Spring Data also provides us with some interfaces with features suitable for each purpose, and they all inherit from the Repository
interface above. Below is an example of an abstract repository that allows the sort and paging of data retrieved from the database:
1 2 3 4 5 6 7 | public interface PagingAndSortingRepository<T, ID extends Serializable> extends Repository<T, ID> { List<T> findAll(Sort sort); Page<T> findAll(Pageable pageable); } |
For example of PagingAndSortingRepository
, to access the second page with the length of a page of 20 lines, we could do the following:
1 2 3 | PagingAndSortingRepository<User, Long> repository = // lấy ra bean của repository Page<User> users = repository.findAll(new PageRequest(1, 20); |
Note: Here are some Spring Data interfaces available to us in the tree diagram:
1.3. Query methods
To create an abstract repository with Spring Data will basically include the following 4 steps:
- Declare an interface that extends from one of the available interfaces depending on the purpose.
public interface PersonRepository extends JpaRepository<User, Long> { … }
- Declare the query methods in the interface.
List<Person> findByEmail(String email);
- Configuration tells Spring that this is a repository and initializes it. (Here I use Repository anotation, you can also configure with .xml file)123@Repositorypublic interface PersonRepository extends JpaRepository<User, Long> { … }
- Inject bean of repository and use.12345678910public class SomeClient {@Autowiredprivate PersonRepoyitory repository;public void doSomething() {List<Person> persons = repository.findByEmail(" <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a> ");}}
Above I have provided the basic steps to create a repository, let’s go ahead and explore the details of each of the steps mentioned above!
1.3.1. Define repository interfaces
In the first step when creating a repository, you will have to define an entity class
for that repository to recognize and work with. The Repository will receive the entity class
and kiểu dữ liệu của ID trong entity class
, then we will have the CRUD methods of the Repository
matching our entity class.
1.3.2. Defines query methods
1.3.2.1. Query lookup strategies
Next, we will discuss the definition of query methods
. In general, there will be two ways for the repository to be able to identify the query to the database you want through the method name
. The first is to get a query directly from the method name in the repository. The second way will use some additional options from the Spring Data Framework to create the query. You can choose one of two ways depending on the actual purpose, but Spring also offers some strategies to help you do that, it is called query-lookup-strategy
following 3 options. :
- CREATE – If this option is used then the Spring framework will attempt to automatically generate a query from the query method name.
- USE_DECLARED_QUERY – For this option the Spring framework tries to find a declared query, either by anotations or declared by other means. If the repository cannot find it, it will error at runtime.
- CREATE_IF_NOT_FOUND (default) – This is the default option and it combines CREATE and USE_DECLARED_QUERY. It looks for a pre-declared query and if no declared query is found, it generates a query based on the custom method name.
1.3.2.2. Query creation
Spring helps us to generate query methods from the name of the methods in the repository, starting with the prefixes representing the purpose of the query to be executed. For example findBy, find, readBy, read, getBy, ...
, and the rest will be modern for variables in the entity class, we can concatenate them with keywords like And, Or, ...
as follows:
1 2 3 4 5 | public interface PersonRepository extends JpaRepository<User, Long> { List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname); } |
In this example, Spring will parse the EmailAddressAndLastname
(called the attribute expression) and generate us the query with two properties, address
and lastname
. As you can see, And
has been used in this example to make our method name clearer. In addition, you can also use other keywords like: Between, LessThan, GreaterThan, Like
for attribute expression.
With the help of keywords, our method name becomes clearer and easier to understand, however in some cases we want an expression to still make that clear but cannot use keywords like As mentioned above, so how can Spring understand our purpose to generate queries with the best purpose? Let’s go to the next example to clarify how Spring analyzes property expressions.
Assuming I have an entity class in which a variable is address
with data type ZipCode
and I want to find all the records by the parameter zipCode passed, at this point I will create a method like this: List<Person> findByAddress(ZipCode zipCode);
. As far as the theory was concerned, this is a very clear attribute expression and in practice it also performs very well. However, to make it easier for the other members to look at it, I want to fix it as:
1 2 | List<Person> findByAddressZipCode(ZipCode zipCode); |
The problem here is that when I add ZipCode
to the method name, will Spring misunderstand that I am looking for the zipCode
property? The answer here will be no and the method I just edited will still work as well as the original method.
Spring’s algorithm will find the AddressZipCode
expression in the entity class, if it does, it will accept this expression as an attribute and create a query for it. On the contrary, Spring will separate our property expression from right to left and follow the Camel Case
style words (words with capital letters) and divide them into head and tail, then it will try to find these parts in the entity class (prefer the first one). This time it will be AddressZip, Code
. There is no right, the algorithm continues to Address, ZipCode
. Thus, Spring has found the Address
for the query and determined this will be the property to look for, then get the tail to further separate.
The new problem that will happen in this case is when in our entity also exists a variable named addressZip
, so the above method will not work as we expected. To solve this problem, we can separate the keywords with the _
sign, then Spring will separate exactly the way we want:
1 2 | List<Person> findByAddress_ZipCode(ZipCode zipCode); |
1.3.2.3. Query data
Spring provides us the @Repository @Repository
help annotate the class to become a repository, through which Spring can recognize and create a repository bean
for us when launching the application. Then we just need to inject
bean and use the following:
1 2 3 4 5 6 | @Repository public interface PersonRepository extends JpaRepository<User, Long> { List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname); } |
1 2 3 4 5 6 7 8 9 | public class SomeClass { @Autowire private PersonRepository personRepository; // sử dụng autowire để inject bean private void testRepository() { List<Person> persons = personRepository.findByEmailAddressAndLastname(" <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a> ", "luong"); } } |
Conclusion
So, through this article, I have gone through the concepts and how Spring Data generates query command from the method name for us, in the following article I will introduce to you another feature of Spring Data. We actively create queries that retrieve data from the database, which is the Spring Specification. See you in the following article!