[JPA] @Query 어노테이션

LDB·2024년 11월 8일
0

JPA 기본

목록 보기
3/10
post-thumbnail

@Query

@Query 어노테이션은 Spring Data JPA에서 지원하는 어노테이션으로 일반 SQL 및 JPQL쿼리를 작성할 수 있게해주는 어노테이션이다.

@Query어노테이션을 사용하는 이유는 Spring Data JPA에서 기본적인 CRUD기능은 지원해 주지만 업무에 따른 상세한 쿼리를 제공해주지 못한다는 한계가 있기 때문에 @Query어노테이션을 사용하여 직접쿼리를 작성함으로 써 그러한 문제를 해결 할 수 있다.

@Query 어노테이션 속성

속성명설명
valueJPQL 혹은 SQL 쿼리를 작성한다.
nativeQuerytrue로 하면 SQL쿼리를 작성할 수 있다.
countQuery페이징처리를 위한 카운트 쿼리를 별도로 작성할 수 있다.

@Query 어노테이션 특징

  1. JPQL 또는 SQL을 직접 작성하여 쿼리를 정의할 수 있다.
  2. 복잡한 쿼리를 작성할 수 있다.
  3. 직접 작성한 쿼리를 최적화 할 수 있다.
  4. 정의한 쿼리를 다른 메서드에서 재사용할 수 있다.
  5. 쿼리의 가독성이 높다.

@Query 어노테이션 사용준비

(해당 예시는 Spring Boot로 작성했고 빌드도구는 gradle로 했다.)

build.gradle 파일

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    
    runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
    
    annotationProcessor 'org.projectlombok:lombok'
}

application.properties 파일

spring.datasource.driverClassName=org.mariadb.jdbc.Driver
spring.datasource.url=
spring.datasource.username=
spring.datasource.password=

entity 파일

@Entity
@Getter
@NoArgsConstructor
@Table(name="member")
public class Member{
	@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String name;
    private int age;
}

(JPArepository 및 JPA는 Entity로 데이터베이스 매핑을 하기때문에 Entity클래스가 필요하다.)


사용예시

select

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import com.spring.jpatest.entity.Member;

public interface memberRepository extends JpaRepository<Member, Integer> {
    
    // SQL 사용방식
    @Query(value="select * from member m where m.id=?1", nativeQuery=true)
    Member findMember1(int id);
    
    // SQL에 파라미터 지정 사용방식
    @Query(value="select * from member m where m.id=:id", nativeQuery=true)
    Member findMember2(@Param(value = "id")int id);
    
    // SQL에 객체 파라미터 사용방식
    @Query(value="select * from member m where m.id=:#{#memberInfo.id}", nativeQuery=true)
    Member findMember3(@Param(value = "memberInfo")MemberInfo info);
    
    // JPQL 사용방식
    @Query(value="select m from member m where m.id=?1")
    Member findMember4(int id);
    
    // JPQL에 파라미터 지정 사용방식
    @Query(value="select m from member m where m.id=:id")
    Member findMember5(@Param(value = "id")int id);
    
    // JPQL에 객체 파라미터 사용방식
    @Query(value="select m from member m where m.id=:#{#memberInfo.id}")
    Member findMember6(@Param(value = "memberInfo")MemberInfo info);
    
}

JpaRepository를 확장하는 과정에서 제너릭을 <Memeber, Integer>로 한 이유는 현재 JpaRepository의 규칙이라고도 할 수 있는데 <엔티티 클래스, 엔티티 클래스의 기본키 타입> 으로 선언하는게 규칙이다.

파라미터를 전달하는 방식은 SQL이나 JPQL이나 @Query 어노테이션을 사용한다면 공통된 방법으로 사용한다.

insert, update, delete

select 뿐만 아니라 insert, update, delete를 사용할 경우에는 또다른 어노테이션을 추가로 붙여주어야 한다.

  1. @Modifying

    • Spring Boot JPA에서 제공하는 어노테이션으로 insert, update, delete등 벌크 연산을 수행할 때 사용하는 어노테이션이다.

    • 속성명기본 값설명
      flushAutomaticallyfalse쿼리를 실행하기전에 영속성 컨텍스트의 변경사항을 DB에 flush한다.
      clearAutomaticallyfalse쿼리를 실행한 후 영속성 컨텍스트를 clear한다.
    • clearAutomatically속성을 true로 하지않으면 영속성 컨텍스트와 DB의 데이터 싱크가 맞지 않게되는 문제가 발생한다.

    • 데이터를 insert하고 insert에 사용한 영속성 컨텍스트로 update를 시도한 후 select하면 DB상에서 데이터는 변경되었지만 값을 확인한다면 1차 캐시에 저장된 insert에 사용한 값이 출력이 되는 문제가 생긴다.

    • 그렇기에 영속성 컨텍스트를 초기화 해주면 insert에 사용한 영속성 컨텍스트는 초기화 되고 update를 사용한 영속성 컨텍스트의 값을 출력해주기 때문에 데이터 동기화 문제를 해결해줄 수 있다.


  1. @Transactional

    • 그래서 보통 insert등의 작업을 한다면 Transactional 어노테이션을 함께 사용하는데 트랜잭션은 간단히 설명하자면 insert를 하고 select를 하는 작업의 과정을 하나의 트랜잭션이라고 한다.

    • 이를 통해 데이터베이스에 대한 변경 작업을 수행할 때 원자성(Atomicity), 일관성(Consistency), 독립성(Isolation), 지속성(Durability)을 보장할 수 있게 된다.


import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import com.spring.jpatest.dto.memberInfo;

public interface memberRepository extends JpaRepository<Member, Integer> {

	// JPQL 쿼리
	@Query(value = "update member m set m.name = :#{#paramMember.name} where m.id = :#{#paramMember.id}")
	@Modifying
	@Transactional
	public int updateJPQL(@Param(value = "paramMember") memberInfo member);
	
	// SQL 쿼리
	@Query(value = "update Member set name = :#{#paramMember.name} where id = :#{#paramMember.id}", nativeQuery = true)
	@Modifying
	@Transactional
	public int updateSQL(@Param(value = "paramMember") memberInfo member);
}

참고 사이트

https://sweet-rain-kim.tistory.com/196

https://louet.tistory.com/259

https://hstory0208.tistory.com/entry/JPA-Modifying%EC%9D%B4%EB%9E%80-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%A3%BC%EC%9D%98%ED%95%A0%EC%A0%90-%EB%B2%8C%ED%81%AC-%EC%97%B0%EC%82%B0

profile
가끔은 정신줄 놓고 멍 때리는 것도 필요하다.

0개의 댓글

관련 채용 정보