0

以下 Spring Boot 服务方法LazyInitializationException在尝试将 a 添加Comment到 aPost时会引发 Hibernate post.addComment(comment)

    @Service
    public class CommentService {

        @Autowired
        private PostRepository postRepository;

        @Autowired
        private CommentRepository commentRepository;


//.....
        /**
         * Creates a new comment
         *
         * @param newCommentDto data of new comment
         * @return id of the created comment
         *
         * @throws IllegalArgumentException if there is no blog post for passed newCommentDto.postId
         */
        public Long addComment(NewCommentDto newCommentDto) {
            try {
                Post post = postRepository.findById(newCommentDto.getPostId()).get();

                Comment comment = new Comment();
                comment.setComment(newCommentDto.getContent());

                post.addComment(comment);
                comment = commentRepository.save(comment);
                return comment.getId();
            } catch (Exception e) {
                throw new IllegalArgumentException("There's no posts for given ID.");
            }

        }

实体映射如下:

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;

@Entity
public class Post {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    @Column(length = 4096)
    private String content;

    private LocalDateTime creationDate;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public LocalDateTime getCreationDate() {
        return creationDate;
    }

    public void setCreationDate(LocalDateTime creationDate) {
        this.creationDate = creationDate;
    }

    public Long getId() {
        return id;
    }

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


    public void addComment(Comment comment) {
        comments.add(comment);
        comment.setPost(this);
    }

    public List<Comment>getComments() {
        return this.comments;
    }



}

    import java.time.LocalDateTime;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;

    @Entity
    public class Comment {

        @Id
        @GeneratedValue
        private Long id;

        private String comment;

        private String author;

        private LocalDateTime creationDate;

        @ManyToOne(fetch = FetchType.EAGER, optional = false)
        @JoinColumn(name = "post_id", nullable = false)
        @OnDelete(action = OnDeleteAction.CASCADE)
        private Post post;

        public Post getPost() {
            return post;
        }

        public void setPost(Post post) {
            this.post = post;
        }

        public Long getId() {
            return id;
        }

        public void setId(Long id) {
            this.id = id;
        }

        public String getComment() {
            return comment;
        }

        public void setComment(String comment) {
            this.comment = comment;
        }

        public String getAuthor() {
            return author;
        }

        public void setAuthor(String author) {
            this.author = author;
        }

        public LocalDateTime getCreationDate() {
            return creationDate;
        }

        public void setCreationDate(LocalDateTime creationDate) {
            this.creationDate = creationDate;
        }

    }

的或的帖子引用的ManyToOne获取类型似乎没有什么区别。EAGERLAZYComment

我在这里做错了什么?如何解决这个错误?

随着@mckszcz 建议的更改,现在同一服务上的以下方法在尝试从帖子中获取评论时会引发一些反射异常:

/**
 * Returns a list of all comments for a blog post with passed id.
 *
 * @param postId id of the post
 * @return list of comments sorted by creation date descending - most recent first
 */
public List<CommentDto> getCommentsForPost(Long postId) {
    List<Comment> comments = postRepository.getOne(postId).getComments();
    List<CommentDto> result = new ArrayList<>();
    comments.forEach(comment -> {
        result.add(new CommentDto(comment.getId(), comment.getComment(), comment.getAuthor(), comment.getCreationDate()));
    });
    return result;
}

如方法的 javadoc 中所述,需要将其更改为返回属于帖子的所有评论的列表?

4

2 回答 2

1

由于拥有方是此处的注释,因此您应该将addComment方法更改为:

 public Long addComment(NewCommentDto newCommentDto) {
            try {
                Post post = postRepository.findById(newCommentDto.getPostId()).get();

                Comment comment = new Comment();
                comment.setComment(newCommentDto.getContent());

                comment.setPost(post);
                comment = commentRepository.save(comment);
                return comment.getId();
            } catch (Exception e) {
                throw new IllegalArgumentException("There's no posts for given ID.");
            }

        }
于 2020-01-23T14:43:34.970 回答
1

事实证明,除了重新排列addComment方法中的代码以消除(如@mckszcz 所指出的那样,LazyInitializationException由于错误的属性而由JPA 抛出):owning side

/**
     * Creates a new comment
     *
     * @param newCommentDto data of new comment
     * @return id of the created comment
     *
     * @throws IllegalArgumentException if there is no blog post for passed newCommentDto.postId
     */
    public Long addComment(NewCommentDto newCommentDto) {
        try {
            Post post = postRepository.findById(newCommentDto.getPostId()).get();

            Comment comment = new Comment();
            comment.setComment(newCommentDto.getContent());
            comment.setAuthor(newCommentDto.getAuthor());


            comment.setPost(post);
            //post.addComment(comment);
            comment = commentRepository.save(comment);
            return comment.getId();
        } catch (Exception e) {
            throw new IllegalArgumentException("There's no posts for given ID.");
        }

    }

为了解决服务方法中的反射InvocationTargetExceptiongetCommentsForPost(Long postId)还需要引入一个额外的查找器方法(允许通过包含父级的 ID 搜索多个子级)CommentRepository

@Repository
public interface CommentRepository extends JpaRepository<Comment, Long> {

    List<Comment> findByPostId(Long postId);

}

然后将该存储库周围的相应更改引入故障方法中:

/**
 * Returns a list of all comments for a blog post with passed id.
 *
 * @param postId id of the post
 * @return list of comments sorted by creation date descending - most recent first
 */
public List<CommentDto> getCommentsForPost(Long postId) {
    List<Comment> commentsForPost = commentRepository.findByPostId(postId);
    //List<Comment> comments = postRepository.getOne(postId).getComments();
    List<CommentDto> result = new ArrayList<>();
    commentsForPost.forEach(comment -> {
        result.add(new CommentDto(comment.getId(), comment.getComment(), comment.getAuthor(), comment.getCreationDate()));
    });
    return result;
}

这两项措施似乎已经解决了这些问题。

于 2020-01-23T20:44:05.923 回答