본문 바로가기
Spring

Spring Data JPA - Native SQL Query 작성하기

by 쌩욱 2021. 8. 20.

SNS 클론코딩 중 사용자들간의 구독 관계를 설정하기 위해 Subscribe 엔티티를 구현했습니다.

그러나 구독하는 아이디와 구독받는 아이디를 갖고 일일이 Subscribe 객체를 만들어 레퍼지토리에 저장하는 것보단 JPA Native SQL Query를 사용해 쿼리문으로 바로 디비에 저장하는 방법이 간편할 것입니다.


Native SQL Query ?

JPA는 Native SQL을 통해 SQL을 직접 사용할 수 있는 기능을 제공합니다.

- SQL을 개발자가 직접 정의할 수 있습니다.

- Native SQL 사용 시 엔티티를 조회하고, JPA가 지원하는 영속성 컨텍스트의 기능을 그대로 사용 가능합니다.

 

우선 Subscribe 테이블의 구조입니다.

Subscribe 테이블 스키마

 

그리고 JpaRepository를 상속한 SubscribeRepository입니다.

@Modifying : 네이티브 쿼리가 데이터베이스에 변경을 준다면 이 어노테이션을 사용합니다.

@Query(value="~", nativeQuery = true) : 네이티브 쿼리임을 의미합니다. value에는 SQL문을 정의하면 됩니다.

쿼리 내부의 콜론( : ) 의 의미 : 작성한 함수에서 받은 인자들을 바인딩해서 넣겠다는 문법입니다.

return type : 반환값이 int이면 성공 시 변경된 레코드의 개수, 실패 시 -1을 반환합니다.

public interface SubscribeRepository extends JpaRepository<Subscribe, Integer>{
	
	//native query 작성, 쿼리 내부의 :은 함수의 변수를 바인드해서 넣겠다는 문법
	@Modifying //데이터베이스에 변경을 주는 네이티브 쿼리는 이 어노테이션 필요 (INSERT, UPDATE, DELETE)
	@Query(value = "INSERT INTO subscribe(fromUserId, toUserId, createDate) VALUES(:fromUserId, :toUserId, now())", nativeQuery = true)
	void mSubscribe(int fromUserId, int toUserId);
	
	//return 타입이 int이면 : 성공하면 변경된 행 개수, 실패하면 -1 반환됨
	// -> 변경된 행의 개수만큼 숫자를 return함, ex) 행 10개 바꾸면 10 return, 0 return 하면 변경된게 없다는 뜻
	// void 해도 상관없음
	
	@Modifying
	@Query(value = "DELETE FROM subscribe WHERE fromUserId = :fromUserId AND toUserId = :toUserId", nativeQuery = true)
	void mUnSubscribe(int fromUserId, int toUserId);
}

 

그리고 컨트롤러에서 Post 요청을 받을 때 다음과 같이 현재 세션의 유저 아이디와 구독할 유저의 아이디를 넘겨주면

서비스에서 레퍼지토리의 앞서 작성한 네이티브 쿼리 함수를 실행하면 됩니다.

그러면 작성했던 쿼리문이 실행되어 데이터베이스 Subscribe 테이블에 레코드가 추가됩니다.

@PostMapping("/api/subscribe/{toUserId}")
	public ResponseEntity<?> subscribe(@AuthenticationPrincipal PrincipalDetails principalDetails, @PathVariable int toUserId){
		subscribeService.구독하기(principalDetails.getUser().getId(), toUserId);
		return new ResponseEntity<>(new CMRespDto<>(1, "구독하기 성공", null), HttpStatus.OK);
	}
@RequiredArgsConstructor
@Service
public class SubscribeService {
	private final SubscribeRepository subscribeRepository;
	
	@Transactional 
	public void 구독하기(int fromUserId, int toUserId) {
		try {
			subscribeRepository.mSubscribe(fromUserId, toUserId);
		}catch (Exception e) {
			throw new CustomApiException("이미 구독을 하였습니다.");
		}
	}
    ...
 }

 

 

전체 코드 : https://github.com/ysu96/photogram