inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

실전! 스프링 데이터 JPA

@EntityGraph

안녕하세요 개인 프로젝트중 궁금한게 생겨서 질문드립니다.

478

칼칼한칼리

작성한 질문수 3

0

안녕하세요 선생님 스프링부트를 이용한 프로젝트를 진행중에 궁금한 것이 생겨서 꼭 알고 싶은 마음에 질문하게 되었습니다.

 

프로젝트 구성은 이렇습니다. Entity는 User, Board, Comment를 만들었고, Spring Data Jpa를 활용하여 각각의 repository인 UserRepository, BoardRepository, CommentRepository도 만들었고, service부분인 CommentServiceImpl을 구성하는 중에 문제가 발생하였습니다.

@Service
public class CommentServiceImpl implements CommentService{

   ...
    @Override
    public Long create(User user, Long board_id, String content) {
        Board board = boardRepository.findById(board_id).get();

        //user.getClass() = class com.graduation.parrot.domain.User  //proxy가 아님을 확인
        System.out.println("user.getClass() = " + user.getClass());

        //user.getBoards() = [Board(id=2, title=title1, content=at), ... //정상실행
        System.out.println("user.getBoards() = " + user.getBoards());

        //LazyInitializationException 발생
        System.out.println("user.getComments() = " + user.getComments());

        return commentRepository.save(new Comment(content, user, board)).getId();
    }

에러메시지 : org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.graduation.parrot.domain.User.comments, could not initialize proxy - no Session

 

원래는 System.out.print...이 부분이 없었지만 이해를 돕기위하여 추가하였습니다. (return 부분의 new Comment를 생성할때 생성자에서 user.getComments()가 실행됩니다.)

테이블의 연관관계 매핑은 [user : board - 1대다], [user : comment - 1대다], [board : comment - 1대다] 이렇게 구성하였습니다. 

제가 생각할때 user.getBoards()와 user.getComments()가 구조상 차이가 없어보이는데 스프링은 그렇게 생각하지 않는지 user.getComments()를 실행할 때만 에러를 발생시킵니다..

문제상황 자체를 해결하는 방법은 찾았으나(user를 바로 사용하지 않고 userRepository.findById() 이용) 왜 user.getComments()만 문제가 발생하는지 꼭 알고 싶습니다.

+@Transactional를 달아도 에러가 발생합니다

 

아래는 Entity들의 자바코드입니다. 

User

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString(of = {"id", "name", "login_id", "password"})
public class User extends BaseTimeEntity{

    @Id
    @GeneratedValue
    @Column(name = "user_id")
    private Long id;

    @Column(nullable = false, length = 100, unique = true)
    private String login_id;

    @JsonIgnore
    @Column(nullable = false, length = 100)
    private String password;

    @Column(length = 15, unique = true)
    private String name;

    @Column(length = 100)
    private String email;

    @Enumerated(EnumType.STRING)
    private Role role = Role.ROLE_USER;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    private List<Board> boards = new ArrayList<>();

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    private List<Comment> comments = new ArrayList<>();

    @Builder
    public User(String login_id, String password, String name, String email) {
        this.login_id = login_id;
        this.password = password;
        this.name = name;
        this.email = email;
    }
}

Board

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@ToString(of = {"id", "title", "content"})
public class Board extends BaseTimeEntity{

    @Id
    @GeneratedValue
    @Column(name = "board_id")
    private Long id;

    @Column(nullable = false, length = 30)
    private String title;

    @Column(length = 1000)
    private String content;

    private String author;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", updatable = false)
    private User user;

    @OneToMany(mappedBy = "board", cascade = CascadeType.ALL)
    private List<Comment> comments = new ArrayList<>();

    @Builder
    public Board(String title, String content, User user) {
        this.title = title;
        this.content = content;
        this.author = user.getName();
        setUser(user);
    }

    public void setUser(User user) {
        this.user = user;
        user.getBoards().add(this);
    }

Comment

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@ToString(of = {"id", "content"})
public class Comment extends BaseTimeEntity{

    @Id @GeneratedValue
    @Column(name = "comment_id")
    private Long id;

    @Column(nullable = false)
    private String content;
    private String author;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "board_id")
    private Board board;

    @Builder
    public Comment(String content, User user, Board board) {
        this.content = content;
        this.author = user.getName();
        setUser(user);
        setBoard(board);
        this.board = board;
    }

    public void setUser(User user) {
        this.user = user;
        user.getComments().add(this); //System.out.print를 지우고 정상실행시 이 부분에서 지연로딩 에러가 발생합니다.
    }

    private void setBoard(Board board) {
        this.board = board;
        board.getComments().add(this);
    }

}

java JPA spring spring-boot

답변 1

1

김영한

안녕하세요. 이호석님

모든 과정을 트랜잭션 안에서 처리해야 합니다.

현재 파라미터를 보면 create(User user)라는 부분을 확인할 수 있는데요.

User가 트랜잭션 밖에서 조회되고, 넘어온 것입니다.

이 문제를 해결하려면 user 자체를 트랜잭션 안에서 조회하셔야 합니다.

트랜잭션이 있는 서비스에서 user를 조회하시면 될꺼에요.

감사합니다.

changeTeam 메서드 질문

0

35

1

existsByUserIdAndProjectId vs existsByUserAndProject 중 어떤 방식이 적절할까요?

0

88

1

existsByUserIdAndProjectId vs existsByUserAndProject 중 어떤 방식이 적절할까요?

0

129

3

MemberRepository 구현체

0

73

1

pdf 표현 질문드립니다.

0

78

1

로그가 남지 않는 문제.

0

100

1

테스트 라이브러리가 강의는 junit4가 맞나요??

0

80

2

pdf 파일과 차이점이 있는 것같은데 문제 없나요?

0

79

2

@PrePersist, @PreUpdate 호출 시점 질문드립니다.

0

104

2

Sort 인터페이스는 잘 사용 안하나요?

0

61

1

스캔대상 질문드립니다.

0

50

1

하이버네이트6에서의 최적화에 이은 질문

0

92

1

save() vs saveAndFlush DB 통신 횟수

0

61

1

순수 JPA 리포지토리 코드 수정부분

0

93

2

bulk연산 후 flush하는 이유를 모르겠어요

0

164

3

bulk insert 질문입니다.

0

191

2

교만했던 것 같아요.

0

152

1

RESTful 강의는 안하시는 건지 궁금합니다.

0

154

2

동적 테이블에 대한 질문

0

90

1

영속성 전이와 연관관계

0

142

2

강의 10:25 질문

0

80

1

단건 update 질문

0

104

2

엔티티 와 도메인의 경계

0

132

1

UsernameOnlyDto 타입 type mismatch 오류

0

123

1