Tuesday, 23 February 2021

Spring jpa: Fetch lazy associations

 Approach 1: Make an explicit call to lazy association in order to initialize it.  In general, most of the developers used to call the .size() method on the collection to load a lazy association. (Note: This lazy association invocation should be done when there is an active session, so you should keep this in @Transactional block).

@Transactional
public User findById(Integer id) {
    User user = userRepository.findById(id).get();
    // To load lazy association roles.
    user.getRoles().size();
    return user;
}

 

One problem with above approach is, it result in executing two different queries to the database.

 

Approach 2: Using FETCH JOIN clause.

@Query("SELECT u FROM User u JOIN FETCH u.roles WHERE u.id = :id")
public User findByIdAndFetchRolesEagerly(@Param("id") Integer id);

 

Find the below working application.

 

Step 1: Create new maven project ‘fetch_lazy_associations’.

 

Step 2: Update pom.xml with maven dependencies.

 

pom.xml

 

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.sample.app</groupId>
    <artifactId>fetch_lazy_associations</artifactId>
    <version>1</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jpa -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
        </dependency>


    </dependencies>
</project>

 

Step 3: Create new package com.sample.app.model and define User class.

 

User.java

package com.sample.app.model;

import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import com.fasterxml.jackson.annotation.JsonManagedReference;

@Entity
@Table(name = "USERS")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "USER_ID")
    private Integer id;

    @Column(name = "FIRST_NAME")
    private String firstName;

    @Column(name = "LAST_NAME")
    private String lastName;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "MY_USER_ID")
    @JsonManagedReference
    private Set<Role> roles;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Set<Role> getRoles() {
        return roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }

    @Override
    public String toString() {
        return "User [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", roles=" + roles + "]";
    }

}

 

Step 4: Define Role entity.

 

Role.java

package com.sample.app.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "roles")
public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ACCOUNT_ID")
    private Integer id;

    @Column(name = "ROLE_NAME")
    private String roleName;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    @Override
    public String toString() {
        return "Role [id=" + id + ", roleName=" + roleName + "]";
    }

}

 

Step 5: Create package ‘com.sample.app.repository’ and define UserRepository interface.

 

UserRepository.java

package com.sample.app.repository;

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;

import com.sample.app.model.User;

public interface UserRepository extends CrudRepository<User, Integer> {

    @Query("SELECT u FROM User u JOIN FETCH u.roles WHERE u.id = :id")
    public User findByIdAndFetchRolesEagerly(@Param("id") Integer id);

}

 

Step 6: Create package ‘com.sample.app.service’ and define UserService class.

 

UserService.java

 

package com.sample.app.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.sample.app.model.User;
import com.sample.app.repository.UserRepository;

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public User findById(Integer id) {
        User user = userRepository.findById(id).get();
        // To load lazy association roles.
        user.getRoles().size();
        return user;
    }
    
    public User findByIdAndFetchRolesEagerly(Integer id) {
        User user = userRepository.findByIdAndFetchRolesEagerly(id);
        return user;
    }

}

 

Step 7: Create application.properties file under src/main/resources folder.

 

application.properties

logging.level.root=WARN
logging.level.org.hibernate=ERROR

## H2 specific properties
spring.h2.console.enabled=true
spring.h2.console.path=/h2

spring.datasource.url=jdbc:h2:file:~/db/myOrg.db;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;

spring.datasource.username=krishna
spring.datasource.password=password123

spring.datasource.driverClassName=org.h2.Driver

## JPA specific properties
# Creates the schema, destroying previous data.
spring.jpa.hibernate.ddl-auto=create

spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

#spring.jpa.show-sql=true
#spring.jpa.properties.hibernate.format_sql=true

## Database connection pooling properties
# Number of ms to wait before throwing an exception if no connection is available.
spring.datasource.max-wait=10000

# Maximum number of active connections that can be allocated from this pool at the same time.
spring.datasource.tomcat.max-active=10
spring.datasource.tomcat.max-idle=5
spring.datasource.tomcat.min-idle=3

 

Step 8: Define App class in com.sample.app package.

 

App.java

package com.sample.app;

import java.util.HashSet;
import java.util.Set;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.transaction.annotation.Transactional;

import com.sample.app.model.Role;
import com.sample.app.model.User;
import com.sample.app.repository.UserRepository;
import com.sample.app.service.UserService;

@SpringBootApplication
public class App {

    public static void main(String args[]) {
        SpringApplication.run(App.class, args);
    }

    @Bean
    @Transactional
    public CommandLineRunner demo(UserRepository userRepository, UserService userService) {
        return (args) -> {
            User user = new User();

            user.setFirstName("Krishna");
            user.setLastName("Gurram");

            Role role1 = new Role();
            role1.setRoleName("Admin");

            Role role2 = new Role();
            role2.setRoleName("Service_user");

            Set<Role> roles = new HashSet<>();
            roles.add(role1);
            roles.add(role2);

            user.setRoles(roles);

            User savedUser = userRepository.save(user);
            
            savedUser = userService.findById(savedUser.getId());
            System.out.println(savedUser);
            
            savedUser = userService.findByIdAndFetchRolesEagerly(savedUser.getId());
            System.out.println(savedUser);
        };
    }

}

 

Run App.java, you will see below messages in console.

 

User [id=1, firstName=Krishna, lastName=Gurram, roles=[Role [id=1, roleName=Admin], Role [id=2, roleName=Service_user]]]
User [id=1, firstName=Krishna, lastName=Gurram, roles=[Role [id=2, roleName=Service_user], Role [id=1, roleName=Admin]]]

 

Project structure looks like below.

 


 


You can download complete working application from below location.

https://github.com/harikrishna553/springboot/tree/master/jpa/fetch_lazy_associations

 

Previous                                                    Next                                                    Home

No comments:

Post a Comment