Home > Software design >  Interface projections with nested associations
Interface projections with nested associations

Time:10-31

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.

  • Related