Pollito Dev
November 25, 2024

Let's talk Java: Data persistence in Spring

Posted on November 25, 2024  •  4 minutes  • 810 words  • Other languages:  Español

Who does what

Hibernate

JPA

Spring Data JPA

So… Who does what?

Spring Boot with JPA

The spring-boot-starter-data-jpa dependency does a lot of heavy lifting for you, but there are a few other things you’ll need to consider to make your Spring Boot project with JPA and Hibernate work seamlessly.

  1. Database Dependency: You need a database driver (H2, MySQL, PostgreSQL, etc.). Spring Boot will automatically pick up the driver and configure Hibernate for the appropriate dialect based on your database.
  2. You need to configure your database connection and a few JPA properties in application.properties (or application.yml).
  3. Entity Classes.
  4. Repository Interfaces: You’ll need to create a repository interface that extends Spring Data’s interfaces, like JpaRepository.

Do You Need Anything Else?

What’s the deal with Eager/Lazy Loading?

What’s the Difference?

Aspect Lazy Loading Eager Loading
Definition Associated data is loaded only when it’s accessed. Associated data is loaded immediately with the parent entity.
Advantages Saves memory by not loading unnecessary data. Simplifies access to related data without worrying about session/transaction boundaries.
Disadvantages - Requires an active Hibernate session; accessing outside results in LazyInitializationException. - Can lead to N+1 problem if mismanaged. - Loads unnecessary data, potentially wasting memory and processing time. - Can result in large, complex queries that slow down performance.
Use Case Best for scenarios where related data is not always needed. Best for scenarios where related data is always required.
Hibernate Default LAZY: For @OneToMany and @ManyToMany. EAGER: For @ManyToOne and @OneToOne.

Who’s responsible for Lazy/Eager Loading?

Common issues and how to handle them

LazyInitializationException

@Transactional
public List<Post> getUserPosts(Long userId) {
    User user = userRepository.findById(userId).orElseThrow();
    return user.getPosts(); // Access within transaction
}

N+1 Problem

@Query("SELECT u FROM User u JOIN FETCH u.posts WHERE u.id = :id")
Optional<User> findUserWithPosts(@Param("id") Long id);
@EntityGraph(attributePaths = {"posts"})
Optional<User> findById(Long id);

Overfetching with Eager Loading

Best Practices

  1. Default to Lazy: Use FetchType.LAZY unless you’re absolutely sure the data is always needed.
  2. Transactional Scope: Ensure lazy-loaded data is accessed within an active transaction.
  3. Optimize Queries: Use JOIN FETCH or EntityGraph for specific use cases that require associated data.
  4. Profile and Monitor: Use tools like Hibernate’s SQL logging or JPA metamodel to monitor what queries are being executed.
  5. Avoid Fetching Large Collections: For @OneToMany or similar relationships, paginate the results when possible.
Hey, check me out!

You can find me here