Spring Data JPA simplifies database interactions in Spring Boot applications by providing a repository-based approach for data persistence. However, as applications grow, optimizing performance becomes essential to ensure scalability, faster response times, and efficient resource utilization.
- Reduces unnecessary database calls
- Provides efficient data fetching techniques
- Helps avoid common pitfalls like N+1 problem
Strategies used for Performance Optimization in Spring Data JPA
These approaches, will help us to optimize the performance of our Application.
1. Lazy loading
- Lazy loading ensures that related data is fetched only when required, reducing unnecessary database queries.
- Improves performance by loading data on demand instead of fetching everything at onnce.
@Entity
public class Author {
@OneToMany(mappedBy = "author", fetch = FetchType.LAZY)
private List<Book> books;
// other fields and methods
}
2. Pagination
- Pagination helps in fetching data in smaller chunks instead of loading the entire dataset at once.
- Prevents memory overload and improves response time.
public interface BookRepository extends JpaRepository<Book, Long> {
Page<Book> findAll(Pageable pageable);
}
3. Caching
Caching stores frequently accessed data to reduce repeated database queries.
@SpringBootApplication
@EnableCaching
public class MyApplication {
//...
}
Write below line in application.properties file to enable caching
spring.cache.type=ehcache
4. Batch Processing
Batch processing reduces multiple database calls by executing operations in bulk.
@Modifying
@Query("UPDATE Customer c SET c.status = :status WHERE c.id IN :ids")
void updateCustomerStatus(@Param("status") String status, @Param("ids") List<Long> ids);
5. Avoiding N+1 Select Problem
- The N+1 problem occurs when multiple queries are executed unnecessarily for related data.
- Use JOIN FETCH to fetch related data in a single query.
@Query("SELECT c FROM Customer c JOIN FETCH c.orders WHERE c.id = :id")
Customer findCustomerWithOrders(@Param("id") Long id);
Enable Actuator for Monitoring
To monitor application performance, enable actuator endpoints by adding the following property
management.endpoints.web.exposure.include=*
Some Useful JpaRepository Methods used for Performance Optimization
Classes extending JpaRepository provide several advanced methods beyond CrudRepository, which help to improve performance and reduce manual coding effort.
1. count(Example<S> example)
Returns the number of matching records
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BookService { //BookService class
private final BookRepository bookRepository;
@Autowired //Autowiring BookRepository
public BookService(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
public long countBooks() {
return bookRepository.count();
}
}
2. exists(Example<S> example)
Checks if a record exists
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(); //new object created
exampleBook.setTitle(title); //calling the method parameter
exampleBook.setAuthor(author);
Example<Book> example = Example.of(exampleBook);
return bookRepository.exists(example);
}
}
3. flush()
Synchronizes persistence context with the database
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) { //saveAndFlush method used
bookRepository.save(book);
bookRepository.flush();
}
}
4. deleteInBatch()
Deletes multiple records efficiently in a single operation
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); //for delete books using batch operation
}
}
5. deleteAllInBatch()
Deletes all records in batch
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(); //delete all books using batch operation
}
}
6. deleteAllByIdInBatch(Iterable<String> ids)
Deletes records by IDs in batch
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); //delete all books by Id
}
}
7. saveAllAndFlush(Iterable<S> entities)
Saves multiple entities and flushes immediately
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); //save all books by using saveAll
bookRepository.flush();
return savedBooks;
}
}
8. saveAndFlush(S entity)
Saves and flushes a single entity instantly
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 //to improve database performance @Transactional annotation is used
public Book saveAndFlushBook(Book book) {
return bookRepository.saveAndFlush(book);
}
}