• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    미해결

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

22.03.29 20:17 작성 조회수 310

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);
    }

}

답변 1

답변을 작성해보세요.

1

안녕하세요. 이호석님

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

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

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

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

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

감사합니다.