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

나는 지금 JPQL이 필요하다! (2)

by oncerun 2022. 10. 26.
반응형

 

역시 줄임말을 펴줘야 한다 JPQL ( Java Persistence Query Language)

JPA랑 앞글자가 거의 같다. 뭐 JPA가 자카르타로 변경됐다고 듣긴 했다

 

JPQL을 적용하기로 결정했는데 JPQL 문법을 조금 알아보자

 

엔티티와 속성은 대소문자를 구분한다. 다만 JPQL 키워드는 대소문자 구문 하지 않는다. 

이 부분에서 매우 많은 오류가 예상된다.  별칭은 필수이다. as는 생략 가능하다.

 

 + 10/28 

결국 동적 쿼리를 작성하다가 오류를 만났다. 

 

띄어쓰기를 하지 않아 쿼리가 붙어버렸기 때문인데, 이에 대해서 인식하고 있어서 인지 오류를 스윽 보자마자 무슨 문제인지 확인했다. 

 

만약 동적 쿼리가 많다면  jpql은 사용하지 않는 것이 좋다. 정말 코드도 길어지고 유지보수도 힘들다. 

 

추가적으로 like에 관하여 문제가 발생했었는데

 

select m from Member m where m.name like :name

 

이 쿼리를 사용하는 곳에선 문제가 없었다. 다만 의도돼도 결과가 출력되지 않았다. 

하이버네이트가 만능은 아닌 것 같았다. like 연산자 뒤에 오는 파라미터에 자동적으로 성능을 고려해서 다음과 같이 해줄 줄 알았다. like :name %

 

어림도 없었고 직접 붙여줘야 했다. 

@Query("select m from Member m where m.name like :name%")

다시 어림도 없었다. 이번엔 오류가 발생했다.  따라서 다음과 같이 고쳐줬는데 이는 데이터베이스의 문법으로 추정된다.

 

 

stack overflow..

  1. 다음과 같이 사용:select... like :username
    List <User> findByUsernameLike(String username);
  2. 시작:select... like :username%
    List <User> findByUsernameStartingWith(String username);
  3. 종료:select... like %:username
    List <User> findByUsernameEndingWith(String username);
  4. 포함:select... like %:username%
    List <User> findByUsernameContaining(String username);

다들 spring-data-jpa의 쿼리 메서드를 활용한다. 

 

하지만 @Query를 쓰는 이유는 성능 때문이어서 방법을 찾아야 했고 그 방법은 이중 파이프를 사용했다는 점이다.

@Query("select m from Member m where m.name like :name ||'%'")

그렇다.. 더 좋은 방법을 찾고 있다. 

 

TypeQuery

 

반환 타입이 명확할 때 즉 칼럼의 타입들이 서로 다를 땐 타입 쿼리 대신 Query 타입을 사용한다.

 

getResultList는 빈 리스트를 반환하는데 getSingleResult는 결과가 없으면 NoResultException이 터지고 둘 이상이면 NonUniqueResultException이 발생한다.

 

파라미터 바인딩

SELECT m FROM Member m where m.username =:username

query.setParameter("username", usernameParam)

 

프로젝션

 

select 절에 조회할 대상을 지정하는 것이다.

 

프로젝션의 대상은 엔티티, 임베디드 타입, 스칼라 타입(숫자, 문자 등 기본 데이터 타입)

 

selecct m from Member m -> 엔티티 프로젝션

 

select m.team from Member m -> 엔티티 프로젝션 (이는 결과가 팀이다.)

엔티티 프로젝션을 통해 반환된 엔티티는 바로 영속성 콘텍스트에서 관리된다! 

 

되게 좋아 보이는 JPQL인데 다음과 같이 수정을 권장한다.

 

select t from Member m join m.team t 

 

실제 jpa가 날리는 쿼리는 같지만 가시적으로 join이 숨겨진다. join은 성능적으로 튜닝이 가능한 부분이기 때문이다. 

 

이를 묵시적 조인이라고 한다.

 

select m.address from Member m -> 임베디드 타입 프로젝션

 

 

select m.username, m.age from Member m -> 스칼라 타입 프로젝션

 

타입이 여러 개면 어떻게 가져올까?

 

1. Query로 가져온다.

2. Object []로  가져온다.

3. new 명령어로 조회한다.

단순 값을 DTO로 바로 조회한다.  (순서와 타입이 동일한 생성자 필수)

select new 패키 지명. 클래스(m.username, m.age) from Mebmer m, MemberDTO.class

 

DISTINCT

 

distinct는 많이 사용될거라고 추정된다. 이는 일대다 관계에서 조인을 한 경우 데이터는 무조건 다에 맞춰져서 가져오게 된다. 

 

하지만 JPA는 객체이기에 동일한 객체 여러 개가 필요가 없다.

일대다에서 distinct를 사용하지 않으면 기대한 결과의 수가 달라질 수 있다.

따라서 jpa는 동일한 엔티티를 애플리케이션 단에서 중복을 제거해 의도한 대로 작동되게 한다. 

CASE 조건식

 

sql을 공부하면서 case를 사용에 대해 의문점을 가지게 되었지만 이는 곧 해결됐다. 아주 복잡한 결과를 도출하는 경우에 사용하게 된다. 

 

JPQL은 case문도 지원한다. 

 

case when 조건식이 참이면 then 이 값이 출력
     else 아니라면 이 값이 출력
end
case A의 값
    when 이 값이라면 then 이 값을 출력
    when A의 값이 이값이라면 then 을 출력
    else 해당안되면 이 값을 출력
end

 

난 그냥 애플리케이션단에서 처리한다.

 

 

JPQL은 여러 함수를 지원한다. 이 함수들은 모든 데이터베이스에 호환되도록 만들어졌기 때문에 걱정 없이 사용할 수 있다.

 

 

 

반응형

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

벌크연산  (0) 2022.10.29
N+1 문제 해결  (0) 2022.10.29
나는 지금 JPQL이 필요하다!  (0) 2022.10.26
주말 정리  (0) 2022.10.24
영속성 컨텍스트  (0) 2022.10.23

댓글