๋ก์ปฌ์์ ์ ๋๋ ์ฝ๋๊ฐ ๋ฐฐํฌ ํ๊ฒฝ์์๋ง ํฐ์ง๋
LazyInitializationException์์ธ๊ณผ ํด๊ฒฐ ๊ณผ์ ์ ์ ๋ฆฌํด๋ณด์์ต๋๋ค.
์ฌ์ฉ์ ๊ฒ์ ํ์ด์ง์์ ์๋์ ๊ฐ์ ์์ธ๊ฐ ๋ฐ์ํ์ต๋๋ค.

failed to lazily initialize a collection of role: innocean.domain.auth.User.userRoles: could not initialize proxy - no Session
org.hibernate.LazyInitializationException
@GetMapping("/search")
@PreAuthorize("hasAuthority('ADMIN_MANAGE')")
public String searchUsers(Model model, @RequestParam(required = false) String query, ...) {
Pageable pageable = PageRequest.of(page, size, sort);
List<String> queries = parseQueryList(query);
Page<UserWithRolesDTO> pagedUsers = userRoleService.searchUsers(queries, enabled, roleIds, pageable);
model.addAttribute("users", pagedUsers);
return "admin/user";
}
์ฌ๊ธฐ์ ์ ์ฝ๋๋ User -> Userrole -> Role ์ด๋ ๊ฒ ์ฐจ๋ก์ฐจ๋ก ์ด๋ํ๋ฉฐ user์ role ์ ๋ณด๋ฅผ ์ฐพ์ต๋๋ค. (๋ค:๋ค ๊ด๊ณ)
public Page<UserWithRolesDTO> searchUsers(List<String> queries, Boolean enabled, List<Long> roleIds, Pageable pageable) {
Specification<User> spec = Specification
.where(UserSpecifications.containsNameOrEmail(queries))
.and(UserSpecifications.hasEnabled(enabled))
.and(UserSpecifications.hasRoles(roleIds));
return userRepository.findAll(spec, pageable)
.map(UserWithRolesDTO::from); // โ Lazy ๊ฐ์ฒด ์ ๊ทผ ์๋
}
public static UserWithRolesDTO from(User user) {
List<String> roleNames = user.getUserRoles().stream()
.filter(UserRole::isEnabled)
.map(ur -> ur.getRole().getName()) // โ Lazy ๋ก๋ฉ ๋ฐ์ ์ง์
.toList();
return new UserWithRolesDTO(
user.getId(),
user.getUsername(),
user.getEmail(),
user.isEnabled(),
roleNames,
user.getCreatedAt()
);
}
๋ก์ปฌ์์๋ ํธ๋์ญ์
์ด ์ด์ ์๋ ์ํ์์ DTO๊ฐ ์์ฑ๋ผ์ Lazy ๋ก๋ฉ์ด ๊ฐ๋ฅํ์ง๋ง,
๋ฐฐํฌ ํ๊ฒฝ์์๋ ํธ๋์ญ์
์ด ๋จผ์ ์ข
๋ฃ๋ ๋ค DTO๋ฅผ ์์ฑํ๊ฒ ๋์ด Hibernate ์ธ์
์ด ๋ซํ๋ฒ๋ฆฝ๋๋ค. (๋ฐฐํฌ ์์๋ ํธ๋์ญ์
์ ์ง ๊ธฐ๊ฐ์ด ๋ ์งง์์ง๋๋ค ใ
.ใ
)
๊ฒฐ๊ณผ์ ์ผ๋ก JPA Lazy ๊ฐ์ฒด๋ฅผ ํธ๋์ญ์ ๋ฐ์์ ์ ๊ทผํ๋ฉด์ ์์ธ ๋ฐ์!
@EntityGraph ์ฌ์ฉSpring Data JPA์ @EntityGraph๋ฅผ ํ์ฉํ๋ฉด ํ์ํ ํ๋๋ฅผ ๋ฏธ๋ฆฌ fetch joinํด์ ๋ก๋ฉํ ์ ์์ต๋๋ค.
์ฌ๊ธฐ์ userRole๊ณผ role ์ ๋ณด๋ฅผ ๋ฏธ๋ฆฌ ๋ฐ์์ค๋ ๊ฑฐ์ฃ !
import org.springframework.data.jpa.repository.EntityGraph;
@EntityGraph(attributePaths = {"userRoles", "userRoles.role"})
Page<User> findAll(Specification<User> spec, Pageable pageable);
์ดํ์๋ DTO์์ Lazy ํ๋ ์ ๊ทผํด๋ ๋ฌธ์ ์์ด ์๋ํฉ๋๋ค.
user.getUserRoles().stream()
.map(ur -> ur.getRole().getName()) // ๐ก ์ด์ ์์ ํ๊ฒ ์ ๊ทผ ๊ฐ๋ฅ
public record UserWithRolesDTO(
Long id,
String username,
String email,
boolean enabled,
List<String> roles,
LocalDateTime createdAt
) {
public static UserWithRolesDTO from(User user) {
List<String> roleNames = user.getUserRoles().stream()
.filter(UserRole::isEnabled)
.map(ur -> ur.getRole().getName())
.toList();
return new UserWithRolesDTO(
user.getId(),
user.getUsername(),
user.getEmail(),
user.isEnabled(),
roleNames,
user.getCreatedAt()
);
}
}
โจ Lazy ํ๋๋ฅผ View์์ ์ฌ์ฉํ ๋ ๋ฐ๋์
fetch join์ด๋@EntityGraph๋ก ๋ฏธ๋ฆฌ ๋ก๋ฉํ๊ธธ.
์ ๊ทธ๋ฌ๋ฉด ๋ก์ปฌ์์ ๋๊ณ , ๋ฐฐํฌ์์ ์ ๋๋ ์๋ฌธ์ ๋ฒ๊ทธ๋ฅผ ๋ง๋๊ฒ ๋ฉ๋๋ค ^_^
์ ๋ ์๊ณ ์ถ์ง ์์์ด์