Skip to content

Commit 6ac3d15

Browse files
committedNov 17, 2021
Intercept the generated SQL for logging or altering
1 parent 139b38b commit 6ac3d15

File tree

11 files changed

+389
-0
lines changed

11 files changed

+389
-0
lines changed
 
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
**[How To Avoid `LazyInitializationException` Via `JOIN FETCH`](https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootJoinFetch)**
2+
3+
**See also:**
4+
- [LEFT JOIN FETCH](https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootLeftJoinFetch)
5+
- [JOIN VS. JOIN FETCH](https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootJoinVSJoinFetch)
6+
7+
**Description:** Typically, when we get a `LazyInitializationException` we tend to modify the association fetching type from `LAZY` to `EAGER`. That is very bad! This is a [code smell](https://vladmihalcea.com/eager-fetching-is-a-code-smell/). Best way to avoid this exception is to rely on `JOIN FETCH` (if you plan to modify the fetched entities) or `JOIN` + DTO (if the fetched data is only read). `JOIN FETCH` allows associations to be initialized along with their parent objects using a single `SELECT`. This is particularly useful for fetching associated collections.
8+
9+
This application is a `JOIN FETCH` example for avoiding `LazyInitializationException`.
10+
11+
**Key points:**
12+
- define two related entities (e.g., `Author` and `Book` in a `@OneToMany` lazy-bidirectional association)
13+
- write a JPQL `JOIN FETCH` to fetch an author including his books
14+
- write a JPQL `JOIN FETCH` (or `JOIN`) to fetch a book including its author
15+
16+
**Output example:**\
17+
![](https://github.com/AnghelLeonard/Hibernate-SpringBoot/blob/master/HibernateSpringBootJoinFetch/hibernate%20spring%20boot%20join%20fetch.png)
18+
19+
-----------------------------------------------------------------------------------------------------------------------
20+
<table>
21+
<tr><td><b>If you need a deep dive into the performance recipes exposed in this repository then I am sure that you will love my book "Spring Boot Persistence Best Practices"</b></td><td><b>If you need a hand of tips and illustrations of 100+ Java persistence performance issues then "Java Persistence Performance Illustrated Guide" is for you.</b></td></tr>
22+
<tr><td>
23+
<a href="https://www.apress.com/us/book/9781484256251"><p align="left"><img src="https://github.com/AnghelLeonard/Hibernate-SpringBoot/blob/master/Spring%20Boot%20Persistence%20Best%20Practices.jpg" height="500" width="450"/></p></a>
24+
</td><td>
25+
<a href="https://leanpub.com/java-persistence-performance-illustrated-guide"><p align="right"><img src="https://github.com/AnghelLeonard/Hibernate-SpringBoot/blob/master/Java%20Persistence%20Performance%20Illustrated%20Guide.jpg" height="500" width="450"/></p></a>
26+
</td></tr></table>
27+
28+
-----------------------------------------------------------------------------------------------------------------------
29+
+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<groupId>com.jpa</groupId>
7+
<artifactId>HibernateSpringBootInterceptSql</artifactId>
8+
<version>1.0</version>
9+
<packaging>jar</packaging>
10+
11+
<name>HibernateSpringBootInterceptSql</name>
12+
<description>JPA project for Spring Boot</description>
13+
14+
<parent>
15+
<groupId>org.springframework.boot</groupId>
16+
<artifactId>spring-boot-starter-parent</artifactId>
17+
<version>2.1.4.RELEASE</version>
18+
<relativePath/> <!-- lookup parent from repository -->
19+
</parent>
20+
21+
<properties>
22+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
23+
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
24+
<java.version>1.8</java.version>
25+
</properties>
26+
27+
<dependencies>
28+
<dependency>
29+
<groupId>org.springframework.boot</groupId>
30+
<artifactId>spring-boot-starter-data-jpa</artifactId>
31+
</dependency>
32+
<dependency>
33+
<groupId>org.springframework.boot</groupId>
34+
<artifactId>spring-boot-starter-jdbc</artifactId>
35+
</dependency>
36+
<dependency>
37+
<groupId>org.springframework.boot</groupId>
38+
<artifactId>spring-boot-starter-web</artifactId>
39+
</dependency>
40+
<dependency>
41+
<groupId>mysql</groupId>
42+
<artifactId>mysql-connector-java</artifactId>
43+
<scope>runtime</scope>
44+
</dependency>
45+
<dependency>
46+
<groupId>org.springframework.boot</groupId>
47+
<artifactId>spring-boot-starter-test</artifactId>
48+
<scope>test</scope>
49+
</dependency>
50+
</dependencies>
51+
52+
<build>
53+
<plugins>
54+
<plugin>
55+
<groupId>org.springframework.boot</groupId>
56+
<artifactId>spring-boot-maven-plugin</artifactId>
57+
</plugin>
58+
</plugins>
59+
</build>
60+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.bookstore;
2+
3+
import com.bookstore.entity.Author;
4+
import com.bookstore.entity.Book;
5+
import com.bookstore.service.BookstoreService;
6+
import org.springframework.boot.ApplicationRunner;
7+
import org.springframework.boot.SpringApplication;
8+
import org.springframework.boot.autoconfigure.SpringBootApplication;
9+
import org.springframework.context.annotation.Bean;
10+
11+
@SpringBootApplication
12+
public class MainApplication {
13+
14+
private final BookstoreService bookstoreService;
15+
16+
public MainApplication(BookstoreService bookstoreService) {
17+
this.bookstoreService = bookstoreService;
18+
}
19+
20+
public static void main(String[] args) {
21+
SpringApplication.run(MainApplication.class, args);
22+
}
23+
24+
@Bean
25+
public ApplicationRunner init() {
26+
return args -> {
27+
28+
Author author = bookstoreService.fetchAuthorWithBooksByName();
29+
System.out.println("\nAuthor name: " + author.getName() + " Books: " + author.getBooks() + "\n");
30+
31+
Book book = bookstoreService.fetchBookWithAuthorByIsbn();
32+
System.out.println("\nTitle: " + book.getTitle() + " author: " + book.getAuthor() + "\n");
33+
34+
};
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package com.bookstore.entity;
2+
3+
import java.io.Serializable;
4+
import java.util.ArrayList;
5+
import java.util.List;
6+
import javax.persistence.CascadeType;
7+
import javax.persistence.Entity;
8+
import javax.persistence.Id;
9+
import javax.persistence.OneToMany;
10+
11+
@Entity
12+
public class Author implements Serializable {
13+
14+
private static final long serialVersionUID = 1L;
15+
16+
@Id
17+
private Long id;
18+
19+
private String name;
20+
private String genre;
21+
private int age;
22+
23+
@OneToMany(cascade = CascadeType.ALL,
24+
mappedBy = "author", orphanRemoval = true)
25+
private List<Book> books = new ArrayList<>();
26+
27+
public void addBook(Book book) {
28+
this.books.add(book);
29+
book.setAuthor(this);
30+
}
31+
32+
public void removeBook(Book book) {
33+
book.setAuthor(null);
34+
this.books.remove(book);
35+
}
36+
37+
public Long getId() {
38+
return id;
39+
}
40+
41+
public void setId(Long id) {
42+
this.id = id;
43+
}
44+
45+
public String getName() {
46+
return name;
47+
}
48+
49+
public void setName(String name) {
50+
this.name = name;
51+
}
52+
53+
public String getGenre() {
54+
return genre;
55+
}
56+
57+
public void setGenre(String genre) {
58+
this.genre = genre;
59+
}
60+
61+
public int getAge() {
62+
return age;
63+
}
64+
65+
public void setAge(int age) {
66+
this.age = age;
67+
}
68+
69+
public List<Book> getBooks() {
70+
return books;
71+
}
72+
73+
public void setBooks(List<Book> books) {
74+
this.books = books;
75+
}
76+
77+
@Override
78+
public String toString() {
79+
return "Author{" + "id=" + id + ", name=" + name
80+
+ ", genre=" + genre + ", age=" + age + '}';
81+
}
82+
83+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package com.bookstore.entity;
2+
3+
import java.io.Serializable;
4+
import javax.persistence.Entity;
5+
import javax.persistence.FetchType;
6+
import javax.persistence.Id;
7+
import javax.persistence.JoinColumn;
8+
import javax.persistence.ManyToOne;
9+
10+
@Entity
11+
public class Book implements Serializable {
12+
13+
private static final long serialVersionUID = 1L;
14+
15+
@Id
16+
private Long id;
17+
18+
private String title;
19+
private String isbn;
20+
21+
@ManyToOne(fetch = FetchType.LAZY)
22+
@JoinColumn(name = "author_id")
23+
private Author author;
24+
25+
public Long getId() {
26+
return id;
27+
}
28+
29+
public void setId(Long id) {
30+
this.id = id;
31+
}
32+
33+
public String getTitle() {
34+
return title;
35+
}
36+
37+
public void setTitle(String title) {
38+
this.title = title;
39+
}
40+
41+
public String getIsbn() {
42+
return isbn;
43+
}
44+
45+
public void setIsbn(String isbn) {
46+
this.isbn = isbn;
47+
}
48+
49+
public Author getAuthor() {
50+
return author;
51+
}
52+
53+
public void setAuthor(Author author) {
54+
this.author = author;
55+
}
56+
57+
@Override
58+
public boolean equals(Object obj) {
59+
if (this == obj) {
60+
return true;
61+
}
62+
if (!(obj instanceof Book)) {
63+
return false;
64+
}
65+
return id != null && id.equals(((Book) obj).id);
66+
}
67+
68+
@Override
69+
public int hashCode() {
70+
return 2021;
71+
}
72+
73+
@Override
74+
public String toString() {
75+
return "Book{" + "id=" + id + ", title=" + title + ", isbn=" + isbn + '}';
76+
}
77+
78+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.bookstore.interceptor;
2+
3+
import org.hibernate.resource.jdbc.spi.StatementInspector;
4+
5+
public class SqlInspector implements StatementInspector {
6+
7+
@Override
8+
public String inspect(String sql) {
9+
10+
System.out.println("The generated SQL is: " + sql);
11+
12+
// you can modify the sql here or return null to return the same sql
13+
14+
return null;
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.bookstore.repository;
2+
3+
import com.bookstore.entity.Author;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
import org.springframework.data.jpa.repository.Query;
6+
import org.springframework.stereotype.Repository;
7+
import org.springframework.transaction.annotation.Transactional;
8+
9+
@Repository
10+
public interface AuthorRepository extends JpaRepository<Author, Long> {
11+
12+
@Transactional(readOnly = true)
13+
@Query(value = "SELECT a FROM Author a JOIN FETCH a.books WHERE a.name = ?1")
14+
Author fetchAuthorWithBooksByName(String name);
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.bookstore.repository;
2+
3+
import com.bookstore.entity.Book;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
import org.springframework.data.jpa.repository.Query;
6+
import org.springframework.stereotype.Repository;
7+
import org.springframework.transaction.annotation.Transactional;
8+
9+
@Repository
10+
public interface BookRepository extends JpaRepository<Book, Long> {
11+
12+
@Transactional(readOnly = true)
13+
@Query(value = "SELECT b FROM Book b JOIN FETCH b.author WHERE b.isbn = ?1")
14+
// or, via JOIN
15+
//@Query(value = "SELECT b, a FROM Book b JOIN b.author a WHERE b.isbn = ?1")
16+
Book fetchBookWithAuthorByIsbn(String isbn);
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.bookstore.service;
2+
3+
import com.bookstore.repository.AuthorRepository;
4+
import com.bookstore.repository.BookRepository;
5+
import com.bookstore.entity.Author;
6+
import com.bookstore.entity.Book;
7+
import org.springframework.stereotype.Service;
8+
9+
@Service
10+
public class BookstoreService {
11+
12+
private final AuthorRepository authorRepository;
13+
private final BookRepository bookRepository;
14+
15+
public BookstoreService(AuthorRepository authorRepository,
16+
BookRepository bookRepository) {
17+
this.authorRepository = authorRepository;
18+
this.bookRepository = bookRepository;
19+
}
20+
21+
public Author fetchAuthorWithBooksByName() {
22+
23+
return authorRepository.fetchAuthorWithBooksByName("Joana Nimar");
24+
}
25+
26+
public Book fetchBookWithAuthorByIsbn() {
27+
28+
return bookRepository.fetchBookWithAuthorByIsbn("002-JN");
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
spring.datasource.url=jdbc:mysql://localhost:3306/bookstoredb?createDatabaseIfNotExist=true
2+
spring.datasource.username=root
3+
spring.datasource.password=root
4+
5+
spring.jpa.hibernate.ddl-auto=create
6+
spring.jpa.show-sql=true
7+
8+
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
9+
10+
spring.jpa.open-in-view=false
11+
12+
spring.datasource.initialization-mode=always
13+
spring.datasource.platform=mysql
14+
15+
spring.jpa.properties.hibernate.session_factory.statement_inspector=com.bookstore.interceptor.SqlInspector

0 commit comments

Comments
 (0)
Failed to load comments.