๐Ÿšจ [Spring JPA] ๋กœ์ปฌ์€ ๋˜๋Š”๋ฐ ๋ฐฐํฌ์—์„œ๋งŒ LazyInitializationException์ด ๋œฌ๋‹ค?

yeahcoldยท2025๋…„ 5์›” 13์ผ

๋กœ์ปฌ์—์„  ์ž˜ ๋˜๋˜ ์ฝ”๋“œ๊ฐ€ ๋ฐฐํฌ ํ™˜๊ฒฝ์—์„œ๋งŒ ํ„ฐ์ง€๋Š” LazyInitializationException ์›์ธ๊ณผ ํ•ด๊ฒฐ ๊ณผ์ •์„ ์ •๋ฆฌํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.


์—๋Ÿฌ ์ƒํ™ฉ

์‚ฌ์šฉ์ž ๊ฒ€์ƒ‰ ํŽ˜์ด์ง€์—์„œ ์•„๋ž˜์™€ ๊ฐ™์€ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

failed to lazily initialize a collection of role: innocean.domain.auth.User.userRoles: could not initialize proxy - no Session
org.hibernate.LazyInitializationException

๋ฌธ์ œ ์ฝ”๋“œ

์ปจํŠธ๋กค๋Ÿฌ (์œ ์ € ๊ฒ€์ƒ‰ API)

@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 ๊ฐ์ฒด ์ ‘๊ทผ ์‹œ๋„
}

DTO ๋ณ€ํ™˜ ์ฝ”๋“œ

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ํ•ด์„œ ๋กœ๋”ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


UserRepository ์ˆ˜์ •

์—ฌ๊ธฐ์„œ 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())  // ๐Ÿ’ก ์ด์ œ ์•ˆ์ „ํ•˜๊ฒŒ ์ ‘๊ทผ ๊ฐ€๋Šฅ

UserWithrolesDTO.java (์ฐธ๊ณ ์šฉ)

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๋กœ ๋ฏธ๋ฆฌ ๋กœ๋”ฉํ•˜๊ธธ.
์•ˆ ๊ทธ๋Ÿฌ๋ฉด ๋กœ์ปฌ์—์„  ๋˜๊ณ , ๋ฐฐํฌ์—์„  ์•ˆ ๋˜๋Š” ์˜๋ฌธ์˜ ๋ฒ„๊ทธ๋ฅผ ๋งŒ๋‚˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค ^_^
์ €๋„ ์•Œ๊ณ  ์‹ถ์ง€ ์•Š์•˜์–ด์š”

profile
Software Engineer

0๊ฐœ์˜ ๋Œ“๊ธ€