Open In App

Optimizing Performance with Spring Data JPA

Last Updated : 16 Jan, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

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 {   //BookService class
    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);  //returns all books by using findAll method
    }
}


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 {     //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)

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(), 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) {    //saveAndFlush method used
        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);       //for delete books using batch operation
    }
}


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


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


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);  //save all books by using saveAll
        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             //to improve database performance @Transactional annotation is used
    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.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads