Spring Data JPA for Abstraction of Queries
Last Updated on
Jul 28, 2023
Developers use boilerplate of code to execute even simple queries. Also pagination and auditing takes long lines of code and efforts. But when developer uses Spring Data JPA, they can execute query by creating methods with the use of method name convention. And Spring will provide the implementation of that method automatically.
Spring Data JPA is not actually a JPA provider like Hibernate, Jboss, EclipseLink and DataNucleus. It is a library or framework. And it adds one layer of abstraction on top of JPA provider. Using Spring in Java development, outcomes with a newer and elegant way of data access.
Feature Listing
- Can build repositories by extending any Spring Data Repository.
- Use any inbuilt CRUD methods provided by CRUDRepository.
- Create Methods using Method name convention or create method providing @Query annotated query.
- Pagination, Slicing, Sorting and Auditing support
- XML based entity mapping support
- Specification<T> and QueryDsl support
JPA Specifies support For:
- JPQL: It is a string-based query language.
- Criteria Query: It uses Criteria to execute JPQL query. It provides API support for re-factoring of classes.
- Native Query: To execute SQL query from JPA, set native query flag to true.
- It also provides support for RDBMS Stored Procedures.
Interfaces provided by Spring Data JPA:
- JPA specific repository interface is JpaRepository<T, ID extends Serializable> It uses combination of methods declared by CRUDRepository<T, Serializable>, PaginationAndSortingRepository<T, Serializable>, Repository<T, Serializable>.
- Not JPA specific repository interface is JpaSpecificationExecutor<T> It uses methods which are already defined in Specification<T> objects. Criteria API is used in Specification<T> method to retrieve entities from the database.
How to use Spring Data JPA:
- Create own repository interface and extend any repository interfaces provided by Spring Data.Ex:
-
public interface UserJPARepository extends JpaRepository<UserJPA, Long>{}
public interface UserJPARepository extends JpaRepository<UserJPA, Long>{}
– Here UserJPARepository is new interface that developer creates which extends JpaRepository and here UserJPA is the entity on which queries are going to perform.
-
- If needed, create any Custom method using @Query annotation or using Method name convention.Ex:
-
UserJPA findByEmail (String email);
UserJPA findByEmail (String email);
– Here, findByEmail method will execute “select * from table_name where email = ‘given_email_from_method_Patameter’ “
-
Page findAll(Pageable pageable);
Page findAll(Pageable pageable);
-
List findByBirthDateBetween(Date startDate, Date endDate);
List findByBirthDateBetween(Date startDate, Date endDate);
-
List findByAgeLessThan(int age);
List findByAgeLessThan(int age);
-
List findByAddress_PinCode(int pinCode);
List findByAddress_PinCode(int pinCode);
– Here, findByAddress_PinCode method will execute “SELECT p FROM UserJPA p LEFT JOIN p.address a WHERE a.pinCode= :pinCode“. It will fire a join query and pass parameter which is defined in joined table.
-
@Nullable List findByFirstNameNotLike(Nullable String firstName);
@Nullable List findByFirstNameNotLike(Nullable String firstName);
– It will return null in case of no result. Also it will accept null value for firstName.
-
Optional findOptioanlByEmail(String email);
Optional findOptioanlByEmail(String email);
– It will return Optional.empty() in case of no result and even throw an IllegalArgumentException if email value is null.
-
Slice findTop100ByLastName (String lastName, Pageable page);
Slice findTop100ByLastName (String lastName, Pageable page);
– Top and First in the query will return limited (here 100) Number of distinct records. Slice will generate limitation per page. For ex: If there are 10 records per page then for 100 records 10 pages getscreated.
-
@Query ("SELECT u FROM UserJPA u WHERE u.age < (SELECT count(n.addressId) FROM AddressJPA n)") public List findByInnerQuery();
@Query (“SELECT u FROM UserJPA u WHERE u.age < (SELECT count(n.addressId) FROM AddressJPA n)”) public List findByInnerQuery();
-
@Query ("select * from user_jpa", nativeQuery=true) public List findBySQLQuery();
@Query (“select * from user_jpa”, nativeQuery=true) public List findBySQLQuery();
– Here, using nativeQuery=true flag provides executing query with SQL syntax.
-
- To use this repository, inject the repository interface to another component.Ex:
-
@Autowired private UserRepository repo; // ingesting repo to another class
@Autowired private UserRepository repo; // ingesting repo to another class
-
repo.findAll(); // inbuilt query
repo.findAll(); // inbuilt query
-
repo.save(); // inbuilt query
repo.save(); // inbuilt query
-
repo. findAll(new PageRequest(0,10)); // custom query
repo. findAll(new PageRequest(0,10)); // custom query
– Here, 0 is number of page (1st page in array) and 10 is number of records per page
-
repo.findByAgeLessThan(50); // custom query
repo.findByAgeLessThan(50); // custom query
-
Spring Data JPA vs Hibernate Coding:
For CRUD operation using Spring Data JPA, developer has no need to create implementation of methods. Ex:
-
repo.findAll(); // inbuilt query
repo.findAll(); // inbuilt query
This will list out all the records from tables. Where as if developer using Hiberante, then for repo.findAll() method, they have to write some lines of code like:
-
Session session = this.sessionFactory.getCurrentSession(); List listUsers = session.createQuery("from UserJPA").list(); return listUsers;
Session session = this.sessionFactory.getCurrentSession(); List listUsers = session.createQuery(“from UserJPA”).list(); return listUsers;
Ex:
-
repo.findByAgeLessThan(int age); // custom query
repo.findByAgeLessThan(int age); // custom query
This will list out all the records from tables. Where as if developer using Hiberante, then for repo.findAll () method, they have to write some lines of code like:
-
Session session = this.sessionFactory.getCurrentSession(); Query query = session.createQuery("from UserJPA where age < :age"); query.setParameter("age", 50); List listUsers = query.list(); return listUsers;
Session session = this.sessionFactory.getCurrentSession(); Query query = session.createQuery(“from UserJPA where age < :age”); query.setParameter(“age”, 50); List listUsers = query.list(); return listUsers;
Transactionality:
- With transactions configured, you can configure a bean with @Transactional either at the method level or class level.
- Developer use @Transactional annotation to make sure that transaction is running.
- Reading methods like findAll() or findOne() are using @Transactional(readOnly=true) which triggers performance optimizations inside the persistence provider as well as on the database level.
-
@Transactional(timeout=10) public List findAll();
@Transactional(timeout=10) public List findAll();
This will cause the findAll() method to be executed with a timeout of 10 seconds.
-
@Transactional(isolation=Isolation.SERIALIZABLE)
@Transactional(isolation=Isolation.SERIALIZABLE)
It will protect against dirty, non-repeatable reads.
-
@Transactional(readOnly=true, propogation = Propogation.SUPPORTS)
@Transactional(readOnly=true, propogation = Propogation.SUPPORTS)
From non-transactional context, using this annotation read-only flag gets ignored and transaction won’t get created.
QueryDsl:
QueryDsl is a framework which creates SQL-like queries via its API. To use QueryDsl, extends QueryDslPredicateExecuter in repository. Ex:
-
Predicate pre = firstName.equalsIgnoreCase(“abc”).and(user.lastName.startsWithIgnoreCase(“bcd”)); repo.findAll(pre);
Predicate pre = firstName.equalsIgnoreCase(“abc”).and(user.lastName.startsWithIgnoreCase(“bcd”)); repo.findAll(pre);
Specification:
To extend specification, extend JpaSpecificationExecutor. Using this will allow you to use specification in methods. Specification method uses criteria query in the logic. Ex:
-
List findAll(Specification spec);
List findAll(Specification spec);
Ex:
-
Specification isAdmin(){ //predicate(){ //method_logic }} repo.findAll(isAdmin());
Specification isAdmin(){ //predicate(){ //method_logic }} repo.findAll(isAdmin());
– This method will find all the records that matches with such specifications.
Auditing:
Auditing means keeping track of who created or changed an entity And the point of time this changes happened. JPA provides @CreatedBy and @LastModifiedBy annotation to define who created and modified the entity. Also provides @CreatedDate and @LastModifiedDate to define time of occurrence. Ex:
-
class Customer { @CreatedBy private UserJPA user; @CreatedDate private DateTime createdDate; }
class Customer { @CreatedBy private UserJPA user; @CreatedDate private DateTime createdDate; }
Comments