📜 ⬆️ ⬇️

Spring Data JPA

The article will describe the use of Spring Data.

Spring Data is an additional convenient mechanism for interacting with database entities, organizing them in a repository, extracting data, changing, in some cases, it will be enough to declare the interface and method in it, without implementation.

Content:
')
  1. Spring repository
  2. Query methods from method name
  3. Configuration and configuration
  4. Special parameter processing
  5. Custom Repository Implementations
  6. User Base Repository
  7. Query Methods - Query


1. Spring Repository


The basic concept in Spring Data is a repository. These are several interfaces that use JPA Entity to interact with. So for example the interface
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID>
provides basic operations for searching, saving, deleting data (CRUD operations)

 T save(T entity); Optional findById(ID primaryKey); void delete(T entity); 

and other operations.

There are other abstractions, such as PagingAndSortingRepository.

Those. If the list that the interface provides is sufficient for interaction with the entity, then you can directly extend the basic interface for your entity, supplement it with your own query methods and perform operations. Now I will briefly show the steps that are needed for the simplest case (without being distracted by the configuration, ORM, database).

1. Create an entity

 @Entity @Table(name = "EMPLOYEES") public class Employees { private Long employeeId; private String firstName; private String lastName; private String email; // . . . 

2. Inherit from one of the Spring Data interfaces, for example from CrudRepository

 @Repository public interface CustomizedEmployeesCrudRepository extends CrudRepository<Employees, Long> 

3. Use in the client (service) a new interface for data operations

 @Service public class EmployeesDataService { @Autowired private CustomizedEmployeesCrudRepository employeesCrudRepository; @Transactional public void testEmployeesCrudRepository() { Optional<Employees> employeesOptional = employeesCrudRepository.findById(127L); //.... } 

Here I used the findById ready method. Those. so easily and quickly, without implementation, we get a ready list of operations from CrudRepository:

  S save(S var1); Iterable<S> saveAll(Iterable<S> var1); Optional<T> findById(ID var1); boolean existsById(ID var1); Iterable<T> findAll(); Iterable<T> findAllById(Iterable<ID> var1); long count(); void deleteById(ID var1); void delete(T var1); void deleteAll(Iterable<? extends T> var1); void deleteAll(); 

It is clear that this list is most likely not enough to interact with the entity, and here you can expand your interface with additional query methods.

2. Query methods from method name


Requests to the entity can be built directly from the name of the method. For this, the mechanism of prefixes find ... By, read ... By, query ... By, count ... By, and get ... By is used, further from the method prefix begins the analysis of the rest. The introductory sentence may contain additional expressions, for example, Distinct. Further, the first By acts as a separator to indicate the beginning of the actual criteria. You can define conditions for properties of entities and combine them with And and Or. Examples

 @Repository public interface CustomizedEmployeesCrudRepository extends CrudRepository<Employees, Long> { //    firstName And LastName Optional<Employees> findByFirstNameAndLastName(String firstName, String lastName); //   5  FirstName       FirstName List<Employees> findFirst5ByFirstNameStartsWithOrderByFirstName(String firstNameStartsWith); 

The documentation defines the entire list, and the rules for writing the method. The result can be an entity T, Optional, List, Stream. In the development environment, such as Idea, there is a hint for writing query methods.

image
It is enough to define a method in the same way, without implementation, and Spring will prepare a request for the entity.

 @SpringBootTest public class DemoSpringDataApplicationTests { @Autowired private CustomizedEmployeesCrudRepository employeesCrudRepository; @Test @Transactional public void testFindByFirstNameAndLastName() { Optional<Employees> employeesOptional = employeesCrudRepository.findByFirstNameAndLastName("Alex", "Ivanov"); 

3. Configuration and configuration


The whole project is available on github.
github DemoSpringData

Here I will only touch on some features.

In context.xml, the transactionManager, dataSource, and entityManagerFactory beans are defined. It is important to indicate in it also

 <jpa:repositories base-package="com.example.demoSpringData.repositories"/> 

path where repositories are defined.

EntityManagerFactory is configured to work with the Hibernate ORM, and it, in turn, with the Oracle XE database, other options are possible here, in the context.xml you can see it all. The pom file has all the dependencies.

4. Special parameter processing


In the query methods, their parameters can use the special parameters Pageable, Sort, as well as the restrictions Top and First.

For example, this is how you can take the second page (index with -0), the size of three elements and sorting by firstName, previously specifying the Pageable parameter in the repository method, the criteria from the method name - “Search by FirstName starting from%„

 @Repository public interface CustomizedEmployeesCrudRepository extends CrudRepository<Employees, Long> { List<Employees> findByFirstNameStartsWith(String firstNameStartsWith, Pageable page); //.... } //   @Test @Transactional public void testFindByFirstNameStartsWithOrderByFirstNamePage() { List<Employees> list = employeesCrudRepository .findByFirstNameStartsWith("A", PageRequest.of(1,3, Sort.by("firstName"))); list.forEach(e -> System.out.println(e.getFirstName() + " " +e.getLastName())); } 

5. Custom implementations for the repository


Suppose that in the repository you need a method that cannot be described by the name of the method, then you can implement it using your interface and implementing class. In the example below, I’ll add to the repository a method for getting employees with maximum pay.

I declare the interface

 public interface CustomizedEmployees<T> { List<T> getEmployeesMaxSalary(); } 

I implement the interface. With the help of HQL (SQL) I get employees with maximum pay, other implementations are possible.

 public class CustomizedEmployeesImpl implements CustomizedEmployees { @PersistenceContext private EntityManager em; @Override public List getEmployeesMaxSalary() { return em.createQuery("from Employees where salary = (select max(salary) from Employees )", Employees.class) .getResultList(); } } 

As well as expanding the Crud Repository Employees also CustomizedEmployees.

 @Repository public interface CustomizedEmployeesCrudRepository extends CrudRepository<Employees, Long>, CustomizedEmployees<Employees> 

There is one important feature here. The class that implements the interface, must end (postfix) with Impl , or you must put your postfix in the configuration

 <repositories base-package="com.repository" repository-impl-postfix="MyPostfix" /> 

We check the work of this method through the repository.

 public class DemoSpringDataApplicationTests { @Autowired private CustomizedEmployeesCrudRepository employeesCrudRepository; @Test @Transactional public void testMaxSalaryEmployees() { List<Employees> employees = employeesCrudRepository.getEmployeesMaxSalary(); employees.stream() .forEach(e -> System.out.println(e.getFirstName() + " " + e.getLastName() + " " + e.getSalary())); } 

Another case when I need to change the behavior of an already existing method in the Spring interface, for example, delete in CrudRepository, I need, instead of deleting from the database, a sign of deletion be set. The technique is exactly the same. Below is an example:

 public interface CustomizedEmployees<T> { void delete(T entity); // ... } //  CustomizedEmployees public class CustomizedEmployeesImpl implements CustomizedEmployees { @PersistenceContext private EntityManager em; @Transactional @Override public void delete(Object entity) { Employees employees = (Employees) entity; employees.setDeleted(true); em.persist(employees); } 

Now if you call delete in the employeesCrudRepository, then the object will only be marked as deleted.

6. Custom Base Repository


In the previous example, I showed how to override delete in the Crud entity repository, but if you need to do this for all project entities, you can make your own interface for each one not so ... then in Spring data you can configure your base repository. For this:
An interface is declared and a method for overriding it (or common to all project entities) in it. Here I have introduced BaseEntity for all my entities (this is not necessary), for the convenience of calling common methods, its methods coincide with the methods of the entity.

 public interface BaseEntity { Boolean getDeleted(); void setDeleted(Boolean deleted); } //  Employees @Entity @Table(name = "EMPLOYEES") public class Employees implements BaseEntity { private Boolean deleted; @Override public Boolean getDeleted() { return deleted; } @Override public void setDeleted(Boolean deleted) { this.deleted = deleted; } //    @NoRepositoryBean public interface BaseRepository <T extends BaseEntity, ID extends Serializable> extends JpaRepository<T, ID> { void delete(T entity); } //    BaseRepository public class BaseRepositoryImpl <T extends BaseEntity, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements BaseRepository<T, ID> { private final EntityManager entityManager; public BaseRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) { super(entityInformation, entityManager); this.entityManager = entityManager; } @Transactional @Override public void delete(BaseEntity entity) { entity.setDeleted(true); entityManager.persist(entity); } } 

In the configuration, you must specify this base repository, it will be common to all project repositories.

  <jpa:repositories base-package="com.example.demoSpringData.repositories" base-class="com.example.demoSpringData.BaseRepositoryImpl"/> 

Now the Employees Repository (and others) need to be expanded from BaseRepository and already be used in the client.

 public interface EmployeesBaseRepository extends BaseRepository <Employees, Long> { // ... } 

I check the work of EmployeesBaseRepository

 public class DemoSpringDataApplicationTests { @Resource private EmployeesBaseRepository employeesBaseRepository; @Test @Transactional @Commit public void testBaseRepository() { Employees employees = new Employees(); employees.setLastName("Ivanov"); // Query by Example (QBE) Example<Employees> example = Example.of(employees); Optional<Employees> employeesOptional = employeesBaseRepository.findOne(example); employeesOptional.ifPresent(employeesBaseRepository::delete); } 

Now, as before, the object will be marked as deleted, and this will be done for all entities that extend the BaseRepository interface. In the example, the search method was used - Query by Example (QBE) , I will not describe it here, it is clear from the example what it does, simple and convenient.

7. Query Methods - Query


Earlier, I wrote that if you need a specific method or its implementation that cannot be described through the name of the method, then this can be done through some Customized Interface (CustomizedEmployees) and make the implementation of the calculation. And you can go the other way, by specifying the query (HQL or SQL), how to calculate this function.
For my example with getEmployeesMaxSalary, this implementation is even simpler. I will complicate it with the salary input parameter. Those. it is enough to declare a method and a calculation request in the interface.

 @Repository public interface CustomizedEmployeesCrudRepository extends CrudRepository<Employees, Long>, CustomizedEmployees<Employees> { @Query("select e from Employees e where e.salary > :salary") List<Employees> findEmployeesWithMoreThanSalary(@Param("salary") Long salary, Sort sort); // ... } 

Checking

 @Test @Transactional public void testFindEmployeesWithMoreThanSalary() { List<Employees> employees = employeesCrudRepository.findEmployeesWithMoreThanSalary(10000L, Sort.by("lastName")); 

I will only mention that requests can also be modifying, for this purpose, the @Modifying annotation is added to them .

 @Modifying @Query("update Employees e set e.firstName = ?1 where e.employeeId = ?2") int setFirstnameFor(String firstName, String employeeId); 

Another of the remarkable features of Query annotations is the substitution of the domain type of an entity into a query using the pattern # {# entityName} , through SpEL expressions.

So for example, in my hypothetical example, when I need to have the attribute “deleted” for all entities, I will do a basic interface with the method of getting a list of objects with the attribute “deleted” or “active”

 @NoRepositoryBean public interface ParentEntityRepository<T> extends Repository<T, Long> { @Query("select t from #{#entityName} t where t.deleted = ?1") List<T> findMarked(Boolean deleted); } 

Further all repositories for entities can be expanded from it. Interfaces that are not repositories, but are located in the "base-package" configuration folder, you must annotate @NoRepositoryBean.

Employees repository

 @Repository public interface EmployeesEntityRepository extends ParentEntityRepository <Employees> { } 

Now when the query is executed, the name of the T entity for the specific repository that will extend the ParentEntityRepository, in this case, Employees, will be substituted into the body of the query.

Check

 @SpringBootTest public class DemoSpringDataApplicationTests { @Autowired private EmployeesEntityRepository employeesEntityRepository; @Test @Transactional public void testEntityName() { List<Employees> employeesMarked = employeesEntityRepository.findMarked(true); // ... 

Materials
Spring Data JPA - Reference Documentation
Project on github .

Source: https://habr.com/ru/post/435114/


All Articles