Optimizing Performance with Spring Data JPA
Last Updated :
16 Jan, 2024
Spring Data JPA is a popular dependency that you can use in your Spring Boot application for Java Persistence API functionalities. It is an extension of CrudRepository, providing better efficiency in your development process. In simpler words, having Spring Boot Data JPA in your project means you don’t have to define a lot of methods, for example, CRUD (Create-Read-Update-Delete) methods, save methods, and more. This saves a lot of time that can be invested into better implementing the business logic in the code. For a more detailed introduction to this dependency, refer to What is Spring Data JPA.
Below are some approaches, which will help us to optimize the performance.
1. Lazy loading
Lazy loading essentially means loading some and not all of the results at a time, which saves time by not retrieving the possibly unnecessary data from the database.
@Entity
public class Author {
@OneToMany(mappedBy = "author", fetch = FetchType.LAZY)
private List<Book> books;
// other fields and methods
}
2. Pagination
As explained in previous point, this dependency enables lazy loading. Hence it does a good job at pagination too, as user after scrolling through the few points on the page can desire to see more, which is only when the pagination algorithm shows more points, not more.
public interface BookRepository extends JpaRepository<Book, Long> {
Page<Book> findAll(Pageable pageable);
}
Java
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
@Service
public class BookService {
private final BookRepository bookRepository;
public BookService(BookRepository bookRepository) {
this .bookRepository = bookRepository;
}
public Page<Book> getBooks( int page, int size) {
PageRequest pageable = PageRequest.of(page, size);
return bookRepository.findAll(pageable);
}
}
|
3. Caching
Similar to how caching speeds up searches on our browsers, caching in Spring Data JPA fastens data retrieval from databases too using queries.
@SpringBootApplication
@EnableCaching
public class MyApplication {
//...
}
Write below line in application.properties file to enable caching:
spring.cache.type=ehcache
4. N+1 data retrievals
​An infamous problem is when, a particular entity is called, multiple related classes are too invoked. This not only increases the code execution time, but also is a brute force approach. This dependency ensures the entity calls are as timesaving, optimized and smooth as possible. A solution is to load the data in batches.
@Entity
public class Author {
// ... other fields
@OneToMany(mappedBy = "author", fetch = FetchType.LAZY)
@BatchSize(size = 10)
private List<Book> books;
}
Example of the numerous features offered by JpaRepository and CrudRepository
An object of any class extending JpaRepository offers more functions than its CrudRepository counterpart, with some of the functions listed below:
1. count(Example<S> example)
Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BookService {
private final BookRepository bookRepository;
@Autowired
public BookService(BookRepository bookRepository) {
this .bookRepository = bookRepository;
}
public long countBooks() {
return bookRepository.count();
}
}
|
2. exists(Example<S> example)
Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.stereotype.Service;
@Service
public class BookService {
private final BookRepository bookRepository;
@Autowired
public BookService(BookRepository bookRepository) {
this .bookRepository = bookRepository;
}
public boolean doesBookExist(String title, String author) {
Book exampleBook = new Book();
exampleBook.setTitle(title);
exampleBook.setAuthor(author);
Example<Book> example = Example.of(exampleBook);
return bookRepository.exists(example);
}
}
|
3. flush(), return type: void
Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class BookService {
private final BookRepository bookRepository;
@Autowired
public BookService(BookRepository bookRepository) {
this .bookRepository = bookRepository;
}
@Transactional
public void saveAndFlushBook(Book book) {
bookRepository.save(book);
bookRepository.flush();
}
}
|
4. deleteInBatch(), return type: void
Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class BookService {
private final BookRepository bookRepository;
@Autowired
public BookService(BookRepository bookRepository) {
this .bookRepository = bookRepository;
}
@Transactional
public void deleteBooksInBatch(List<Book> books) {
bookRepository.deleteInBatch(books);
}
}
|
5. deleteAllInBatch(), return type: void
Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class BookService {
private final BookRepository bookRepository;
@Autowired
public BookService(BookRepository bookRepository) {
this .bookRepository = bookRepository;
}
@Transactional
public void deleteAllBooksInBatch() {
bookRepository.deleteAllInBatch();
}
}
|
6. deleteAllByIdInBatch(Iterable<String> ids), return type: void
Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class BookService {
private final BookRepository bookRepository;
@Autowired
public BookService(BookRepository bookRepository) {
this .bookRepository = bookRepository;
}
@Transactional
public void deleteBooksByIdInBatch(List<String> bookIds) {
bookRepository.deleteAllByIdInBatch(bookIds);
}
}
|
7. saveAllAndFlush(Iterable<S> entities), return type: List<S>
Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class BookService {
private final BookRepository bookRepository;
@Autowired
public BookService(BookRepository bookRepository) {
this .bookRepository = bookRepository;
}
@Transactional
public List<Book> saveAllAndFlushBooks(List<Book> books) {
List<Book> savedBooks = bookRepository.saveAll(books);
bookRepository.flush();
return savedBooks;
}
}
|
8. saveAndFlush(S entity), return type: S
Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class BookService {
private final BookRepository bookRepository;
@Autowired
public BookService(BookRepository bookRepository) {
this .bookRepository = bookRepository;
}
@Transactional
public Book saveAndFlushBook(Book book) {
return bookRepository.saveAndFlush(book);
}
}
|
These functions help to considerably optimize the results and efficiency of the code, with more functionalities being inbuilt into the system thereby putting lesser pressure on the programmer.
Conclusion
Spring Data JPA is an immensely beneficial dependency which positively impacts every project it’s used in through streamlined optimization. No wonder, 9 out of 10 Spring Boot developers today imperatively add this dependency right during the creation of a fresh project. This strengthens resonance with the core idea and advantage of Spring Boot – “Minimal configuration, more business logic development”, helping beginners to get started with the Spring Boot framework faster.
Share your thoughts in the comments
Please Login to comment...