Optimizing Performance with Spring Data JPA

Last Updated : 26 Mar, 2026

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

Java
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

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();     //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

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) {    //saveAndFlush method used
        bookRepository.save(book);
        bookRepository.flush();
    }
}

4. deleteInBatch()

Deletes multiple records efficiently in a single operation

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);       //for delete books using batch operation
    }
}

5. deleteAllInBatch()

Deletes all records in batch

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();   //delete all books using batch operation
    }
}

6. deleteAllByIdInBatch(Iterable<String> ids)

Deletes records by IDs in batch

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);   //delete all books by Id
    }
}

7. saveAllAndFlush(Iterable<S> entities)

Saves multiple entities and flushes immediately

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);  //save all books by using saveAll
        bookRepository.flush();
        return savedBooks;
    }
}

8. saveAndFlush(S entity)

Saves and flushes a single entity instantly

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             //to improve database performance @Transactional annotation is used
    public Book saveAndFlushBook(Book book) {
        return bookRepository.saveAndFlush(book);
    }
}


Comment

Explore