본문 바로가기
데이터 접근 기술/JPA

[JPA] JPQL 기본 문법과 SELECT 절

by oncerun 2021. 1. 31.
반응형

JPQL도 SQL과 비슷하게 SELECT, UPDATE, DELETE 문을 사용할 수 있다. 저장할 때에는 엔티티 매니저를 사용하기 때문에 INSERT문은 없다.

 

SELECT :: =
 SELECT_절
 FROM_절
 [WHERE_절]
 [GROUPBY_절]
 [HAVING_절]
 [ORDERBY_절]
 
 UPDATE :: = UPDATE_절 WHERE_절
 DELETE :: = DELETE_절 WHERE_절

 

SELECT 문

 

EX) SELECT m FROM Member AS m where m.username = 'hi'

 

  • 엔티티의 속성은 대소문자를 구분한다. 하지만 JPQL의  키워드는 대소문자를 구분하지 않는다.
  • JPQL에서 사용한 Member는 클래스명이 아니라 엔티티명이다. @Entity(name="Member")로 지정된 엔티티이며,엔티티명을 지정하지 않은 경우에는 클래스며을 기본 값으로 사용한다.
  • JPQL에서 AS를 활용한 별칭은 필수로 사용해야한다.

 

작성한 JPQL을 실행하려면 쿼리 객체를 만들어야 한다. 쿼리 객체는 TypeQuery와 Query가 있는데 반환할 타입을 명확하게 지정할 수 있다면 TypeQuery 객체를 사용하고, 타입이 불확실한 경우에는 Query객체를 사용한다.

em.createQuery() 메서드의 두 번째 인자에 반환할 타입을 지정하면 TypeQuery객체를 그렇지 않으면 Query객체를 반환한다.

 

TypedQuery<Member> query = em.createQuery("select m from Member m",Member.class);
List<Member> resultList = query.getResultList();

 

 

만약 조회 대상의 String타입과 Integer타입 두 개를 반환한다면 Query객체를 사용해야 하며, Object [] 타입을 반환한다. 

무슨 말인가 하면 SQL을 사용할 경우는 조회한 속성 값을 포함한 Member객체를 반환했다면 JPQL은 해당 조회한 속성만 반환한다. 

Query 사용 예제를 보자.

Query query = 
	em.createQuery("SELECT m.suername, m.age FROM Mebmer m");
    
List resultList = query.getResultList();
for (Object o : resultList){
	Object[] result = (Object[]) o;
    System.out.printIn("username = " + result[0]);
    System.out.printIn("age = " + result[1] );

}

 

결과 조회를 위한 메서드들이며, 호출 시 실제 쿼리를 실행해 데이터베이스를 조회한다.

 

  • query.getResultList() //결과가 없을 시 빈 컬렉션을 반환한다.
  • query.getSingleResult() //결과가 한 개가 아닐 시 예외가 발생한다.

 

JPQL은 파라미터 바인딩을 이름 기준으로도 지원한다.

 

String usernameParam = "User";

TypedQuery<Member> query = em.createQuery("SELECT m FROM Member m where m.username = :username",
Member.class);

query.setParameter("username", usernameParam);

 

혹은 메서드 체인 방식으로 작성할 수도 있다.

List<Member> members = em.createQuery("SELECT m FROM Member m where m.username = :username",
Member.class)
	.setParameter("username", usernameParam)
     .getResultList();
	

 

또한 JDBC처럼 위치 기반으로 1, 2, 3으로 파라미터를 바인딩할 수 있는데 더 명확한 건 이름 기반으로 파라미터를 바인딩하는 것이 더 좋다.

만약 파라미터 바인딩을 사용하지 않고 단순히 문자를 더하기로 만들어 넣게 된다면 SQL 인젝션 공격을 당할 수 있다. 

 

프로젝션

 SELECT 절에 조회할 대상을 지정하는 것을 projection이라 한다. 프로젝션 대상은 엔티티, 엠비 디드 타입, 스칼라 타입이 있다.

 

엔티티

 - SELECT m FROM Member m

 

임베디드 타입 프로젝션

 - 임베디드 타입은 엔티티의 속해있는 값 타입이다. 그렇기에 절대 시작점이 될 수 없으며 영속성 콘텍스트에서 관리되지 않는다.

 

 - SELECT o.address FROM Order o;

 

스칼라 타입 프로젝션

 - List <> String usernames = em.createQuery("SELCT username FROM Member m" , String.class). getResultList();

 - 중복 데이터를 제거하려면 DISTINCT키워드 사용한다.

 

여전히 프로젝션 대상이 여러 개라면 Query객체를 사용해 여러 타입을 받을 수밖에 없다. 다만 실제로는 UserDTO처럼 의미 있는 객체로 변환해서 사용할 것이다.

 

package chap13;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class Main {

	
	public static void main(String[] args) {
		
		EntityManagerFactory emf = Persistence.createEntityManagerFactory("spring5");
		EntityManager em = emf.createEntityManager();
		
		List<Object[]> resultList = 
				em.createQuery("SELECT m.username, m. age from Member m")
				.getResultList();
		List<UserDTO> userDTOs = new ArrayList<>();
		
		for(Object[] o : resultList) {
			UserDTO userDTO =new UserDTO((String)o[0],(int)o[1]);
			userDTOs.add(userDTO);			
		}
		
		
		
	}
}

 편하게 NEW명령어를 사용해 객체 변환 작업을 없애보자

		
		TypedQuery<UserDTO> query = em.createQuery("SELECT new chap13.UserDTO(m.username, m.age) FROM Member m"
				,UserDTO.class);
		
		List<UserDTO> resultList = query.getResultList();

 

select다음에 new명령어를 사용하면 반환받을 클래스를 지정할 수 있고 이 클래스 생성자에 JPQL조회 결과를 넘겨줄 수 있다. 대신 패키지 명을 포함한 전체 클래스 명을 입력해야 한다. 또한 순서와 타입이 일치하는 생성자가 필요하다.

 

반응형

'데이터 접근 기술 > JPA' 카테고리의 다른 글

[JPA] 벌크 연산  (0) 2021.01.31
[JPA] JPQL JOIN  (0) 2021.01.31
[JPA] JPQL 소개  (0) 2021.01.30
[JPA] 즉시 로딩과 지연 로딩  (0) 2021.01.28
[JPA] 상속 관계 매핑  (0) 2021.01.27

댓글