springlazy-initializationpageable

Spring - get a page of entity with lazy elements loaded


I'm very new to Spring.

I'm trying to get a Page to return that to my front end. The AppUser has a lazy association with Role entity.

I'm getting a failed to lazily initialize a collection of role error.

Did a lot of research but I do not get the issue.

Let's see the code :

AppUser entity :

@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "app_user")
public class AppUser {
    @Id
    @GeneratedValue
    @Column(name = "user_id")
    private UUID userId;

    @Column(name = "user_first_name")
    private String userFirstName;

    @Column(name = "user_last_name")
    @NonNull
    private String userLastName;

    @Column(name = "matricule")
    private String matricule;

    @Column(name = "email_address")
    private String emailAddress;

    @Column(name = "password")
    private String password;

    @EqualsAndHashCode.Exclude
    @ManyToOne
    @JoinColumn(name = "grade_id")
    private Grade grade;

    @EqualsAndHashCode.Exclude
    @ManyToMany
    @JoinTable(name = "user_role_association", joinColumns = @JoinColumn(name = "user_id"), 
inverseJoinColumns = @JoinColumn(name = "application_role_id"))
    private List<AppRole> appRoles;

Controller method :

    @GetMapping(path = "user", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Page<AppUser>> getUserPage(@AuthenticationPrincipal UserDetail user,
                                                 @RequestParam("page") int page,
                                                 @RequestParam("size") int size,
                                                 @RequestParam(value = "sortDirection", 
required = false) String sortDirection,
                                                 @RequestParam(value = "sortField", required = 
false) String sortField) {
    log.info(String.format("User %s getting list of all users", user.getUsername()));
    try {
        final Page<AppUser> response = appUserService.getAll(page, size,sortDirection, 
sortField);
        return ResponseEntity.ok(response);
    } catch (Exception e) {
        log.error("Error fetching user page", e);
        throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Error fetching data");
    }
}

Service method :

    public Page<AppUser> getAll(int page,
                            int size,
                            String sortDirection,
                            String sortField) {
    Pageable pageable;
    Sort sort;
    // sorting & pagination
    if (sortField != null && !sortField.isEmpty()) {
        sort = Sort.by(sortField);
        if (sortDirection != null && sortDirection.equals("desc")) {
            sort = sort.descending();
        }
    } else {
        sort = Sort.by("matricule").descending();
    }
    pageable = PageRequest.of(page, size, sort);

    return appUserRepository.findAllAppUserWithAllProperties(pageable);
}

Repository

@Repository
public interface AppUserRepository extends JpaRepository<AppUser, UUID> {
Optional<AppUser> findByUserId(UUID uuid);
AppUser findByMatricule(String matricule);

@Query(value = "SELECT u FROM AppUser u LEFT JOIN FETCH u.appRoles WHERE u.matricule = 
:matricule")
AppUser findAppUserWithAppRolesByMatricule(String matricule);

@Query(value = "SELECT u FROM AppUser u LEFT JOIN u.appRoles")
Page<AppUser> findAllAppUserWithAllProperties(Pageable pageable);

}

Looking at the logs I can see the query runs :

SQL - select appuser0_.user_id as user_id1_0_, appuser0_.email_address as email_ad2_0_, 
appuser0_.grade_id as grade_id7_0_, appuser0_.matricule as matricul3_0_, appuser0_.password as 
password4_0_, appuser0_.user_first_name as user_fir5_0_, appuser0_.user_last_name as 
user_las6_0_ from rixheim.app_user appuser0_ left outer join rixheim.user_role_association 
approles1_ on appuser0_.user_id=approles1_.user_id left outer join rixheim.application_role 
approle2_ on approles1_.application_role_id=approle2_.application_role_id order by 
appuser0_.matricule desc limit ?

So I guess I'm not so far of a way to retrieve the full object but I'm really stuck now.

Appreciate if you can help on that matter.

Thanks.


Solution

  • finally found the way to do it.

    @EntityGraph(attributePaths = {"appRoles", "grade"})
    @Query(value = "FROM AppUser appuser")
    Page<AppUser> findAllAppUserWithAllProperties(Example example, Pageable 
    pageable);
    

    It sounds using EntityGraph will keep the lazy loading except when we specifically call that query.