본문 바로가기
프로그래밍/JPA

[JPA] 객체지향 쿼리 언어

by yonmoyonmo 2021. 5. 29.
김영한님의 책 [JPA 프로그래밍]을 읽고 요약한 내용입니다.

객체지향 쿼리

기본적인 CRUD 외에 좀 더 복잡한 쿼리로 데이터를 조회하고 싶을 때, SQL을 쓰는게 더 낫겠다 싶을 때에는 JPA의 객체지향 쿼리를 쓰면 된다. 

SQL과 비슷하게 생겼지만 데이터베이스에 직접 조회하는 용도는 아니고 대신 JPA가 적절히 SQL로 변환해서 조회결과를 엔티티 객체로 반환해 준다.

JPQL

Java Persistence Query Language

엔티티 객체를 조회하는 객체지향 쿼리이다.

특정 데이터베이스 벤더에 의존하지 않으며, dialect만 변경하면 작성한 JPQL을 수정하지 않고도 다양한 관계형 데이터베이스를 바꿔가며 쓸 수 있다.

SQL보다 간결하게 생겼다.

JPQL 기본 문법과 쿼리 API 

SQL과 구조가 비슷하게 생겼다. SELECT, UPDATE, DELETE 문이 있고 INSERT EntityManager.persist()만 쓰면 되기 때문에 없다고 한다.

SELECT

Member라는 엔티티가 있을 때

select m from Member as m where m.username = "wonmo"

이런 느낌으로 생겼다.

엔티티와 관련된 것들은 대소문자를 구분하지만 JPQL 키워드는 대소문자 구분 없이 사용한다.

from 다음에는 엔티티가 와야한다.

as는 생략해도 되지만 별칭은 필수다.

*TypeQuery, Query 객체

작성한 JPQL을 실행하려면 쿼리 객체를 만들어야 한다. 쿼리 객체는  TypeQuery와 Query가 있다. 반환타입이 명확하다면 TypeQuery, 반대는 Query를 사용하면 된다.

TypeQuery<Member> query = em.createQuery("select m from member m", Member.class);

List<Member> resultList = query.getResultList();

요런 식으로 쓰면 된다고 한다.

EntityManager.createQuery()의 둘 째 파라미터로 반환 타입을 지정해 주지 않으면 Query타입으로 결과가 반환된다고 한다.

** 결과가 없으면 빈 컬렉션을 반환한다.

파라미터 바인딩

이름 기준 파라미터

파라미터를 이름으로 구분하는 방법이다. ' : ' 를 사용한다.

String usernameParam = "User01";
TypeQuery<Member> query = em.createQuery(
	"select m from Member m where m.username = :username", 
	Member.class
	);
query.setParameter("username", usernameParam);
List<Member> resultList = query.getResultList();

 

요런 식으로 사용한다.

:username으로 바인딩 할 곳을 세팅해 놓고, setParameter() 메소드로 바인딩 하면 된다.

위치 기준 파라미터

 ? 과 숫자를 합쳐서 바인딩할 위치를 마킹해 놓고 setParameter로 바인딩한다.

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

**JPQL API는 메소드 체인 방식으로 대부분 설계되어 있어서 체이닝이 가능하다.

요런 식으로 사용한다.

JPQL도 COUNT, SUM 등의 집합 함수를 쓸 수 있다.

GROUP BY, HAVING, ORDER BY 같은 것들도 쓸 수 있다.

JOIN도 쓸 수 있다. 아무튼 SQL로 할 수 있는 것은 거의 다 할 수 있다.

자세한 내용은 생략...(정리 할 바에 그냥 책 꺼내서 보고 만다 싶을 정도로 양이 많음)

벌크연산

update와 delete는 한 번에 수정하거나 삭제할 수 있도록 되어 있다. 이것을 벌크연산이라고 한다.

String query = "update Product p set p.price = p.price * 1.1 where p.stockAmount < :stockAmount";
int resultCount = em.createQuery(query).setParameter("stockAmount", 10).executeUpdate();

요런식으로 한 방에 싹 다 수정하거나

String query = "delete from Product p where p,price < :price";
int resultCount = em.createQuery(query).setParameter("price", 100).executeUpdate();

요런식으로 한 방에 싹 다 삭제하거나 할 때 쓴다.

벌크연산은 영속성 컨텍스트를 무시하고 데이터베이스에 바아아로 변환된 SQL을 때린다.

그래서 영속성 컨텍스트에서 업데이트 된 값으로 뭘 하려면 한 번 리프레쉬 해 줘야한다. 새로고침 해 줘야지 업데이트 된 값으로 다시 영속화 할 수 있다.

executeUpdate()를 하고 업데이트 된 값을 바로 다음 코드에서 사용하려면 em.refresh() 같은 것을 사용해 줘야한다.

**JPQL로 조회한 것 중에 엔티티가 아닌 것은 영속성 컨텍스트에 영속화 되지 않는 점을 주의!

JPQL 외의 객체지향 쿼리

Criteria

JPQL을 자바 코드로 작성하도록 도와주는 빌더 클래스 API

문자열이 아니라 코드로 JPQL을 작성하므로 문법 오류를 컴파일 단계에서 잡을 수 있고 문자 기반의 JPQL보다 동적 쿼리를 안전하게 생성할 수 있다는 점이 장점이지만 코드가 괜히 복잡해진다는 단점이 있다.

QueryDSL

Criteria의 단점인 복잡성을 잡고 JPQL과 유사한 모습의 코드로 가독성을 높인 객체지향 쿼리 프로젝트이다.

그 밖에 그냥 SQL을 쓸 수 있는 기능도 있다.

 

 

'메모장/JPA' 카테고리의 글 목록

프로그래밍 & 일상

wonmocyberschool.tistory.com

 

댓글