@Query 어노테이션은 Spring Data JPA에서 지원하는 어노테이션으로 일반 SQL 및 JPQL쿼리를 작성할 수 있게해주는 어노테이션이다.
@Query어노테이션을 사용하는 이유는 Spring Data JPA에서 기본적인 CRUD기능은 지원해 주지만 업무에 따른 상세한 쿼리를 제공해주지 못한다는 한계가 있기 때문에 @Query어노테이션을 사용하여 직접쿼리를 작성함으로 써 그러한 문제를 해결 할 수 있다.
속성명 | 설명 |
---|---|
value | JPQL 혹은 SQL 쿼리를 작성한다. |
nativeQuery | true로 하면 SQL쿼리를 작성할 수 있다. |
countQuery | 페이징처리를 위한 카운트 쿼리를 별도로 작성할 수 있다. |
(해당 예시는 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클래스가 필요하다.)
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 어노테이션을 사용한다면 공통된 방법으로 사용한다.
select 뿐만 아니라 insert, update, delete를 사용할 경우에는 또다른 어노테이션을 추가로 붙여주어야 한다.
@Modifying
Spring Boot JPA에서 제공하는 어노테이션으로 insert, update, delete등 벌크 연산을 수행할 때 사용하는 어노테이션이다.
속성명 | 기본 값 | 설명 |
---|---|---|
flushAutomatically | false | 쿼리를 실행하기전에 영속성 컨텍스트의 변경사항을 DB에 flush한다. |
clearAutomatically | false | 쿼리를 실행한 후 영속성 컨텍스트를 clear한다. |
clearAutomatically속성을 true로 하지않으면 영속성 컨텍스트와 DB의 데이터 싱크가 맞지 않게되는 문제가 발생한다.
데이터를 insert하고 insert에 사용한 영속성 컨텍스트로 update를 시도한 후 select하면 DB상에서 데이터는 변경되었지만 값을 확인한다면 1차 캐시에 저장된 insert에 사용한 값이 출력이 되는 문제가 생긴다.
그렇기에 영속성 컨텍스트를 초기화 해주면 insert에 사용한 영속성 컨텍스트는 초기화 되고 update를 사용한 영속성 컨텍스트의 값을 출력해주기 때문에 데이터 동기화 문제를 해결해줄 수 있다.
@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);
}