JinhwanB / dividendpj

스프링 부트와 java를 사용한 배당금 프로젝트
0 stars 0 forks source link

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.jh.dividendpj.member.domain.Member.roles: could not initialize proxy - no Session #3

Closed JinhwanB closed 6 months ago

JinhwanB commented 6 months ago

토큰을 통한 인증 후 권한이 필요한 컨트롤러 호출 시 발생한 에러다.

// 현재 관리하고 있는 모든 회사 리스트 조회
    @GetMapping("/company")
    @PreAuthorize("hasRole('READ')")
    public ResponseEntity<GlobalApiResponse> getAllCompany(@PageableDefault(size = 10, sort = "name", direction = Sort.Direction.DESC) Pageable pageable) {
        Page<CompanyDto.Response> allCompany = companyService.getAllCompany(pageable);
        List<Page<CompanyDto.Response>> list = new ArrayList<>(List.of(allCompany));
        return ResponseEntity.ok(GlobalApiResponse.toGlobalApiResponse(list));
    }
@Entity
@Getter
@Setter // 테스트용
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Builder(toBuilder = true)
@ToString
public class Member implements UserDetails {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String username;

    @Column(nullable = false)
    private String password;

    @Column
    @ElementCollection
    private List<String> roles;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        roles.forEach(r -> {
            authorities.add(() -> r);
        });
        return authorities;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return false;
    }

    @Override
    public boolean isAccountNonLocked() {
        return false;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return false;
    }

    @Override
    public boolean isEnabled() {
        return false;
    }
}

이 멤버 엔티티의 role을 읽을 때 문제가 생기는 것 같다.

JinhwanB commented 6 months ago

위 컨트롤러가 호출되면 TokenProvider의 아래 메소드가 호출 되는것을 확인

public Authentication getAuthentication(String jwt) {
        UserDetails userDetails = memberService.loadUserByUsername(getUserName(jwt));
        return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
    }

위 에러에 대해서 구글링을 해보니 Transaction이 끝난 뒤에 엔티티를 조회하면서 생기는 오류라는 것에 힌트를 얻었다.

즉 위 메소드가 호출됐을 때 Transaction이 끝나면서 멤버의 role을 initialize하지 못하는 것이다.(role에 있는 @ElementCollection은 기본적으로 lazy로딩이다.)

따라서 Transaction이 유지될 수 있도록 해당 메소드에 @Transactional을 붙여주었다.

@Transactional
    public Authentication getAuthentication(String jwt) {
        UserDetails userDetails = memberService.loadUserByUsername(getUserName(jwt));
        return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
    }