Long bookResult = queryFactory
.update(book)
.set(book.bookStatus, 1)
.where(book.bookNo.eq(bookNo))
.execute();
javax.persistence.TransactionRequiredException: Executing an update/delete query
책 대출 기능 개발 중, 대출하려는 도서의 대출 상태값을 수정하기 위해 querydsl로 update 구문을 실행하였으나 해당 Exception이 발생하면서 정지...
update, delete구문을 실행할 땐 transaction 내에서 실행을 해야 하기 때문에 해당 메서드에 @Transactional 어노테이션을 붙여 해결하였다.
@Override
@Transactional
public int updateBookStatus(String username, int bookNo){
System.out.println("start update");
Long bookResult = queryFactory
.update(book)
.set(book.bookStatus, 1)
.where(book.bookNo.eq(bookNo))
.execute();
System.out.println(bookResult);
if(bookResult==0) {
return 0; //수정된 행 없음
}
🤷♀️ UPDATE, DELETE 구문을 트랜잭션 내에서 실행해야 하는 이유
일반적으로 트랜잭션 내의 DB 작업은 오토커밋이 비활성화되어 원자성을 지킬 수 있다.
여기서 원자성이란 더이상 쪼개질 수 없는 성질을 뜻하는데, 해당 트랜잭션과 관련된 작업을 모두 실행하던지, 혹은 아예 실행하지 않도록 보장하는 특성이다. => 실패한 작업이 있더라도 다른 작업들이 영향을 받지 않도록 한다.
한 메서드에 여러개의 UPDATE 구문을 작성해두었다고 치자.
1번째, 2번째의 구문까지는 잘 실행하여 UPDATE를 완료했는데 3번째 구문에서 Exception이 발생했을 경우 원자성이 보장되지 않는다면 1, 2구문까지의 업데이트만이 DB에 반영되고, 오토커밋 처리되어 DB에 문제가 발생할 수 있게 된다.
나의 코드를 예로 들자면 책의 대출 상태값 수정을 성공하면 대출이 되었다고 가정하고 해당 도서를 대여한 사용자의 성별과 나이에 따라 알맞는 컬럼의 카운트를 올려 연령별 분포를 알 수 있도록 했다.
그런데 여기서 트랜잭션을 사용하지 않고 해당 구문이 실행됐다면, 대출 도중 문제가 발생했을 때, 상태값 변경은 성공했으나 연령별 분포 테이블의 변경은 실패할 수도 있는 것이다.
트랜잭션은 이러한 일을 방지하도록 원자성을 보장하고, 해당 요청이 실패했을 경우 다른 모든 작업을 취소하고 롤백하여 트랜잭션 시작 이전의 상태로 돌아간다. 짱이쥬?
@Transactional 어노테이션을 사용하지 않는다면 직접 트랜잭션을 관리하여 사용할 수도 있다구 한당