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
No comments:
Post a Comment