우선 '좋아요' 기능을 구현할려고 했고, Heart 테이블에 @ManyToOne으로 User, Blog 테이블을 연관시켜놓은 상태입니다.
@NoArgsConstructor
@Data
@Entity
public class Heart {
@GeneratedValue(strategy = GenerationType.AUTO)
@Id
private Long Id;
@JoinColumn(name = "userId", nullable = false)
@ManyToOne()
private User user;
@JoinColumn(name="blogId", nullable = false)
@ManyToOne
private Blog blog;
public Heart(User user, Blog blog){
this.user=user;
this.blog=blog;
}
}
JPA활용하여 USER 테이블과, BLOG 테이블을 저장 및 find할 때는 문제가 없었는데,
좋아요 취소를 위해 delete 메소드를 추가했을 때 에러가 발생했습니다..
문제의 코드👇
public int addHeart(Long blogId, User user) {
Blog blog=blogRepository.findById(blogId).orElseThrow(
()->new NullPointerException("해당 아이디가 없습니다")
);
//이상 없음
List<Heart> heartList=heartRepository.findByUserAndBlog(user, blog);
//size가 0이상이면 해당 user가 그 blog에 이미 좋아요를 눌렀다는 의미
if(heartList.size()>0){
heartRepository.deleteByUserAndBlog(user, blog);
//@Formula로 가상 칼럼으로 좋아요 수 count한 것에서 -1
return blog.getHeartCount()-1;
}
Heart heart=new Heart(user, blog);
//좋아요 저장
heartRepository.save(heart);
//Formula로 좋아요 수 count한 것을 반환
//1을 더한 이유는 좋아요 클릭해서 1이 늘어난 것을 반영하기 위함
return blog.getHeartCount()+1;
}
500에러(서버 에러)가 발생했습니다..
콘솔창의 에러메시지를 보니..
javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'remove' call
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:295) ~[spring-orm-5.3.16.jar:5.3.16]
at com.sun.proxy.$Proxy122.remove(Unknown Source) ~[na:na]
at org.springframework.data.jpa.repository.query.JpaQueryExecution$DeleteExecution.doExecute(JpaQueryExecution.java:277) ~[spring-data-jpa-2.6.2.jar:2.6.2]
at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:90) ~[spring-data-jpa-2.6.2.jar:2.6.2]
at
...
...
라 나와있는데 가만히보니 TransactionRequiredException 이라고 친히 나와있긴합니다.
@Transactional 어노테이션은 하나의 트랜잭션 단위를 만들어서 db처리 시 하나라도 에러가 나면 전부 롤백시켜주는 기능으로 알고만 있었는데, 여기서는 왜 필요로 하는지 사실 아직 잘 모르겠습니다..
(다음번 포스팅은 transactional을 공부해보는 것으로!)
그래서 혹시나 하는 마음에 @Transactional 어노테이션을 메소드 위에 붙이니
Hibernate: select blog0_.id as id1_0_0_, blog0_.created_at as created_2_0_0_, blog0_.modified_at as modified3_0_0_, blog0_.contents as contents4_0_0_, blog0_.title as title5_0_0_, blog0_.writer as writer6_0_0_, (select count(1) from heart h where h.blog_id = blog0_.id) as formula1_0_ from blog blog0_ where blog0_.id=?
com.llave.mini_project.models.Blog@3e4a86d5
Hibernate: select heart0_.id as id1_1_, heart0_.blog_id as blog_id2_1_, heart0_.user_id as user_id3_1_ from heart heart0_ where heart0_.user_id=? and heart0_.blog_id=?
Hibernate: select user0_.id as id1_3_0_, user0_.created_at as created_2_3_0_, user0_.modified_at as modified3_3_0_, user0_.kakao_id as kakao_id4_3_0_, user0_.nickname as nickname5_3_0_, user0_.password as password6_3_0_ from user user0_ where user0_.id=?
Hibernate: select heart0_.id as id1_1_, heart0_.blog_id as blog_id2_1_, heart0_.user_id as user_id3_1_ from heart heart0_ where heart0_.user_id=? and heart0_.blog_id=?
Hibernate: delete from heart where id=?
에러 메시지 없이 맨 밑에 처럼 delete가 잘 된 것을 알수 있습니다.
다음번엔 트랜잭션에 대해 더 자세히 공부해보는 것으로..