본문 바로가기

스터디/JPA

객체지향 쿼리 언어 - (2)

728x90
반응형

JPQL - (1)


기본 문법과 쿼리 API

SQL과 비슷하게 SELECT, UPDATE, DELETE 문을 사용할 수 있다

 

select m from Member as m where m.username = 'Hello'

 

대소문자 구분

엔티티와 속성은 대소문자를 구분한다 (Member, username)

JPQL 키워드는 대소문자를 구분하지 않는다 (select, from, as)

 

엔티티 이름

Member는 엔티티 명으로 엔티티 클래스에 지정할 수 있다

기본값은 클래스 명이다

 

별칭은 필수

AS를 사용해 별칭을 주었는데 JPQL은 별칭이 필수이다

AS 키워드는 생략 가능하다

 

 

TypeQuery, Query

JPQL을 실행하기 위한 쿼리 객체가 필요한데,

반환할 타입을 명확하게 지정가능하면 TypeQuery 객체를 사용하고, 그렇지 않으면 Query 객체를 사용한다

TypedQuery<Member> query = em.createQuery("SELET m FROM member m", Member.class);
List<Member> resultList = query.getResultList();

Query query = em.createQuery("SELECT m.username, m.age from Member m");
List resultList = query.getResultList();

Query를 사용하면 Object[] 혹은 Object로 반환한다

 

 

결과 조회

  - query.getResultList(): 결과를 컬렉션으로 반환한다. 결과가 없는 경우 빈 컬렉션을 반환한다

  - query.SingleResult(): 결과가 정확히 하나일 때 사용한다

    - 결과가 없으면 NoResultException 예외가 발생한다

    - 결과가 1개보다 많으면 NoUniqueResultException이 발생한다

파라미터 바인딩

JDBC와 다르게 위치 기준 파라미터 바인딩 뿐만 아니라 이름 기준 파라미터 바인딩도 제공한다

 

 

이름 기준 파라미터

파라미터를 이름으로 구분하는 방법이다

파라미터 앞에 :를 사용한다

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

 

 

위치 기준 파라미터

파라미터를 위치로 구분하는 방법이다

? 다음에 위치 값을 준다

위치 값은 1부터 시작한다

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

 

위치 기준 파라미터보다는 이름 기준 파라미터를 사용하는 것이 더 명확하다

 

파라미터 바인딩을 사용하지 않고 직접 문자를 더해 넣는 방식을 사용하면 SQL 인젝션 공격을 당할 수 있다

-> 선택이 아닌 필수로 사용해야 한다

프로젝션

SELECT 절에 조회할 대상을 지정하는 것을 프로젝션이라고 한다

 

프로젝션의 대상은 엔티티, 임베디드 타입, 스칼라 타입이 있다

스칼라 타입은 숫자, 문자 등 기본 데이터 타입을 의미한다

 

 

엔티티 프로젝션

SELECT m FROM Member m 
SELECT m.team FROM Member m

조회한 엔티티는 영속성 컨텍스트에서 관리된다

 

 

임베디드 타입 프로젝션

엔티티와 거의 비슷하게 사용된다

임베디드 타입은 조회의 시작점이 될 수 없다는 제약이 있다

SELECT a FROM Address a //잘못된 쿼리
SELECT o.address FROM Order o

 

엔티티를 통해 임베디드 타입을 조회할 수 있다

임베디드 타입은 엔티티 타입이 아닌 값 타입으로 영속성 컨텍스트에서 관리되지 않는다

 

 

스칼라 타입 프로젝션

숫자, 문자, 날짜와 같은 기본 데이터 타입을 스칼라 타입이라 한다

SELECT username FROM Member m
SELECT DISTINCT username FROM Member m //중복 데이터 제거
SELECT AVG(o.orderAmount) FROM Order o //통계 쿼리

 

 

여러 값 조회

필요한 데이터들만 선택해서 조회해야 하는 경우가 있다

반환 타입이 명확하지 않으므로 TypeQuery 대신 Query를 사용해야 한다

 

스칼라 타입 뿐만 아니라 엔티티 타입도 여러 값을 함께 조회할 수 있다

List<Object[]> resultList = 
	em.createQuery("SELECT o.member, o.product, o.orderAmount FROM Order o")
	.getResultList();
	
for (Object[] row : resultList) {
	Member member = (Member) row[0]; //엔티티
	Product product = (Product) row[1]; //엔티티
	int orderAmount = (Integer) row[2]; //스칼라
}

 

 

NEW 명령어

public class UserDTO {
	private String username;
	private int age;
	
	public UserDTO(String username, int age) {
		this.username=username;
		this.age = age;
	}
	...
}
TypeQuery<UserDTO> query = 
	em.createQuery("SELECT new jpabook.jpql.UserDTO(m.username, m.age)
	FROM Member m", UserDTO.class);
	
List<UserDTO> resultList = query.getResultList();

쿼리 결과를 Object[]로 반환받아 직접 객체 변환을 하는 대신에 NEW 명령어를 사용해 반환받을 클래스를 지정할 수 있다

 

 

주의할 점

패키지 명을 포함한 전체 클래스 명을 입력해야 한다

순서와 타입이 일치하는 생성자가 필요하다

728x90
반응형

'스터디 > JPA' 카테고리의 다른 글

객체지향 쿼리 언어 - (4)  (0) 2025.02.18
객체지향 쿼리 언어 - (3)  (0) 2025.02.17
객체지향 쿼리 언어 - (1)  (0) 2025.02.16
값 타입 - (2)  (0) 2025.02.16
값 타입 - (1)  (0) 2025.02.14