My Comment
entity is self-joined that has a subComment
set.
@Entity
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private String id;
@OneToMany(mappedBy = "parentComment", cascade = CascadeType.ALL)
private Set<Comment> subComments = new HashSet<>();
@ManyToOne
@JoinColumn(referencedColumnName = "id")
private Comment parentComment;
Here in my addComment
method
public ResponseEntity<Comment> addComment(Comment comment) {
Comment currComment = commentRepository.save(comment);
if (currComment.getParentId() != null) {
Comment parent = commentRepository.findById(currComment.getParentId()).orElse(null);
if (parent != null) {
parent.addSubComment(currComment);
currComment.setParentId(parent.getId());
currComment.setParentComment(parent);
commentRepository.save(parent);
}
}
Comment responseComment = commentRepository.save(currComment);
return ResponseEntity.ok(responseComment);
}
When I tried to establish the reverse relationship (owning side),
comment.setParentComment(parent);
causes the error
comment.setParentComment(parent);
is causing an error: java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
Full Comment entity class
@OneToMany(mappedBy = "parentComment", cascade = CascadeType.ALL)
private Set<Comment> subComments = new HashSet<>();
@ManyToOne
@JoinColumn(referencedColumnName = "id")
private Comment parentComment;
private boolean isParent;
private String parentId;
public String getParentId() {
return parentId;
}
public void setParentId(String parentId) {
this.parentId = parentId;
}
public Set<Comment> getSubComments() {
return subComments;
}
public void setSubComments(Set<Comment> subComments) {
this.subComments = subComments;
}
public Comment addSubComment(Comment comment) {
this.subComments.add(comment);
return this;
}
public Comment getParentComment() {
return parentComment;
}
public void setParentComment(Comment parentComment) {
this.parentComment = parentComment;
}
public boolean getIsParent() {
return isParent;
}
public void setIsParent(boolean isParent) {
this.isParent = isParent;
}
@ManyToOne
@JoinColumn(referencedColumnName = "id")
@JsonBackReference
private Comment parentComment;
I added @JsonBackReference that solved the java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
error. The parent Comment
is also able to see the subComments
set.
You seem to be over complicating it.
1) You have the @JoinColumn(referencedColumnName = "id")
but that is redundant. That's what the foreign key references anyway so you don't need to be explicit about it. No worries, but don't write code you don't need.
2) If you have the parentId in the a new subComment you don't need to look it up and add it to the parent comment's list. The concept you're missing here is the "owning" entity concept. Look at the Javadoc for mappedBy
. Since the parentComment
field does the mapping it defines the owning entity. Granted, it's the same Entity, but the point is that it's the parentComment
field that controls the persistence. You don't need to add anything to the Set
of subComments
in order for the relationship to be persisted. You can do so if you want but JPA will ignore it. You only need to set the parentComment
field. E.g.
Edit: This example is with JPA instead of Spring Data, but it's the same under the hood.
Your entity needs only be:
@Entity
public class Comment {
@Id @GeneratedValue
private Integer id;
@OneToMany(mappedBy="parentComment")
private Set<Comment> subComments;
@ManyToOne
private Comment parentComment;
and you use it like so:
private void run() {
runWrite();
runRead();
Comment comment = new Comment();
comment.setId(1);
Comment subComment = new Comment();
subComment.setParentComment(comment);
runSaveSubComment(subComment);
runRead();
}
private void runWrite() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("persistence");
em = emf.createEntityManager();
tx = em.getTransaction();
try {
tx.begin();
Comment comment = new Comment();
Comment subComment = new Comment();
subComment.setParentComment(comment);
em.persist(comment);
em.persist(subComment);
tx.commit();
} finally {
emf.close();
}
}
private void runRead() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("persistence");
em = emf.createEntityManager();
try {
Comment comment = em.createQuery("select c from Comment c left join fetch c.subComments where c.id = :id", Comment.class).setParameter("id", 1).getSingleResult();
System.out.println(comment + Arrays.toString( comment.getSubComments().toArray()) );
} finally {
emf.close();
}
}
private void runSaveSubComment(Comment subComment) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("persistence");
em = emf.createEntityManager();
tx = em.getTransaction();
try {
tx.begin();
em.persist(subComment);
tx.commit();
} finally {
emf.close();
}
}
or if you want to do with Spring Data.
Comment comment = new Comment();
Comment sub1 = new Comment();
sub1.setParentComment(comment);
repo.save(comment);
repo.save(sub1);
Comment parentComment = repo.fetchSubCommentsById(1);
System.out.println(parentComment + Arrays.toString(parentComment.getSubComments().toArray()));
Comment sub2 = new Comment();
sub2.setParentComment(parentComment);
repo.save(sub2);
parentComment = repo.fetchSubCommentsById(1);
System.out.println(parentComment + Arrays.toString(parentComment.getSubComments().toArray()));
// or
Comment p = new Comment();
p.setId(1);
Comment sub3 = new Comment();
sub3.setParentComment(p);
repo.save(sub3);
parentComment = repo.fetchSubCommentsById(1);
System.out.println(parentComment + Arrays.toString(parentComment.getSubComments().toArray()));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With