Let's assume there are two entities:
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
@ManyToMany(mappedBy = "authors")
private Set<Book> books;
}
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String fileName;
private String fileType;
@Lob
private byte[] data;
@ManyToMany
@JoinTable(
name = "book_authors",
joinColumns = @JoinColumn(name = "book_id"),
inverseJoinColumns = @JoinColumn(name = "author_id"))
private Set<Author> authors;
}
The following DTO interface projection is used to query only the columns that are needed.
public interface AuthorView {
String getFirstName();
String getLastName();
Set<BookView> getBooks();
interface BookView {
String getTitle();
}
}
A simple findAllBy
query method is declared in the repository:
public interface AuthorRepository extends JpaRepository<Author, Long> {
@EntityGraph(attributePaths = "books")
List<AuthorView> findAllBy();
}
The method executes the following query:
select
author0_.id as id1_0_0_,
book2_.id as id1_1_1_,
author0_.first_name as first_na2_0_0_,
author0_.last_name as last_nam3_0_0_,
book2_.data as data2_1_1_,
book2_.file_name as file_nam3_1_1_,
book2_.file_type as file_typ4_1_1_,
book2_.title as title4_1_1_,
books1_.author_id as author_i2_2_0__,
books1_.book_id as book_id1_2_0__
from
author author0_
left outer join
book_authors books1_
on author0_.id=books1_.author_id
left outer join
book book2_
on books1_.book_id=book2_.id
Even though the projection doesn't contain data
, file_name
, and file_type
properties, they are fetched from the database which is causing performance issues, especially if the files are large.
The problem is that Spring Data JPA fetches the entire entities and uses them to perform a programmatic mapping, according to Thorben Janssen's blog.
Is there any solution to prevent fetching entire entities when using interface-based DTO projections except for writing massive queries?
CodePudding user response:
Recently, I found out Blaze Persistence - Entity View Module, which looks promising.
According to @Christian Beikov's answer, EntityView
projections can be used almost like Spring Data Projections with the Spring Data integration, and those projections fetch only the necessary properties. Additionally, there is no need to use @EntityGraph
, since the integration handles it ad-hoc by adapting the query generation.