본문 바로가기

JAVA/JPA

JPA JPQL

반응형

JPQL

 

*가장 단순한 조회방법

 **EntityManager.find()

 **객체 그래프 탐색(a.getB().getC())

*나이가 18살이상인 회원을 모두 검색하고 싶다면?


JPA를 사용하면 엔티티 객체를 중심으로 개발

문제는 검색 쿼리

검색을 할때도 테이블이 아닌 엔티티 객체를 대상으로 검색

모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능

애플리케이션이 필요한 데이터만 DB에서 불러오면 결국 검색조건이 포함된 SQL이 필요


JPA는 SQL을 추상화한 JPQL이라는 객체지향 쿼리 언어 제공

SQL과 문법과 유사 SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 지원

JPQL은 엔티티 객체를 대상으로 쿼리

SQL은 데이터베이스 테이블을 대상으로 쿼리


//검색

String jpql = "select m from Member m where m.name like '%hello%'";

List<Member> result = em.createQuery(jpql, Member.class)

.getResultList();

테이블이 아닌 객체를 대상으로 검색하는 객체 지향 쿼리

SQL을 추상화해서 특정 데이터베이스 SQL에 의존X

JPQL을 한마디로 정의하면 객체 지향 SQL


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

JPQL 키워드는 대소문자 구분 안함(SELECT, FROM, where)

엔티티 이름을 사용, 테이블 이름이 아님(Member)

별칭은 필수(m)


query.getResultList(): 결과가 하나 이상, 리스트 반환

query.getSingleResult(): 결과과 정확히 하나, 단일 객체 반환(정확히 하나가 아니면 예외발생)


파라미터 바인딩 - 이름 기준, 위치 기준

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

query.setParameter("username", usernameParam);


SELECT m FROM Member m where m.username = ?1

query.setParamter(1, usernameParam);


항상 네임으로 하는게 좋다.


프로젝션

SELECT m FROM Member m -> 엔티티 프로젝션

SELECT m.team FROM Member m -> 엔티티 프로젝션

SELECT username, age FROM Member m -> 단순값 프로젝션


new 명령어: 단순값을 DTO로 바로 조회

SELECT new jpabook.jpql.UserDTO(m.username, m.age) FROM Member m


DISTINCT는 중복제거


JPA는 페이징 API로 추상화

setFirstResult(int startPoistion) 조회시작위치

setMaxResult(int maxResult) 조회할 데이터 수


예)

//페이징 쿼리

String jpql = "select m from Member m order by m.name desc";

List<Member> resultList = em.createQuery(jpql, Member.class)

.setFirstResult(10)

.setMaxResults(20)

.getResultList();


MySQL방언

SELECT

M.ID AS ID,

M.AGE AS AGE,

M.TEAM_ID AS TEAM_ID,

M.NAME AS NAME

FROM

MEMBER M

ORDER BY

M.NAME DESC LIMIT ?, ?

ORACLE 방언

SELECT * FROM

( SELECT ROW_.*,  ROWNUM ROWNUM_

FROM

( SELECT

M.ID AS ID,

M.AGE AS AGE,

M.TEAM_ID AS TEAM_ID,

M.NAME AS NAME

FROM MEMBER M

ORDER BY M.NAME

) ROW_

WHERE ROWNUM <= ?

)

WHERE ROWNUM_ > ?

페이징을 바꾸고싶다면? 네이티브 쿼리를 짜야한다.


집합

SELECT 

COUNT(m)

SUM(m.age)

AVG(m.age)

MAX(m.age)

MIN(m.age)

FROM Member m



조인

내부조인

select m from Member m [INNER] join m.team t


외부조인

select m from Member m LEFT [OUTER] JOIN m.team t


세타조인

select count(m) from Member m, Team t where m.username = t.name


참고: 하이버네이트 5.1부터 세타 조인도 외부 조인 가능


패치조인

엔티티 객체 그래프를 한번에 조회하는 방법

별칭을 사용할수 없다.

JPQL: select m from Memeber m join fetch m.team

SQL: SELECT M.*, T.* FROM MEMEBER M INNER JOIN TEAM T ON M.MEMBER_ID = T.ID


예시

String jpql = "select m from Member m join fetch m.team";

List<Member> members = em.createQuery(jqpl, Member.class)

.getResult();


for(Member member : members) {

System.out.println("username = " + member.getUsername() + ", " +

"teamname = " + member.getTeam().name());

현업에서 fetch 사용


lazy에서 계속 리스트 나감 N+1 문제


JPQL 기타

서브쿼리 지원

EXISTS, IN

BETEWEEN, LIKE, IS NULL


JPQL 기본함수

CONCAT

SUBSTRING

TRIM

LOWER, UPPER

LENGTH,

LOCATE

ABS, SQRT, MOD

SIZE, INDEX(JPA  용도)


CASE식


select

case when m.age <= 10 then '학생요금'

    when m.age >= 60 then '경로요금'

    else '일반요금'

end

from Member m


단순식

select

case t.name

when '팀A' then '인센티브 110%'

when '팀B' then '인센티브 120%'

else '인센티브105%'

end

from Team t


COALESCE : 하나씩 조회해서 null이 아니면 반환

NULLIF: 두값이 같으면 null반환, 다르면 첫번째 값 반환


select coalesce(m.username,'이름없는 회원') from Member m

select NULLIF(m.username, '관리자') from Member m


사용자 정의 함수 호출

하이버네이트는 사용전 방언에 추가해야한다.

select function('group_concat', i.name) from Item i



*Named 쿼리 - 정적쿼리

미리 정의해서 이름을 부여해두고 사용하는 JPQL

어노테이션 XML에 정의

애플리케이션 로딩 시점에 초기화후 재사용

애플리케이션 로딩 시점에 쿼리를 검증


예)

@Entity

@NamedQeury(

name="Member.findByUsername",

query="select m from Member m where m.username = :username")

public class Memeber {

...

}


List<Member> resultList = em.createQuery("Member.findByUsername", Member.class)

.setParameter("username", "회원1")

.getResultList();



버그

1. 컴파일 버그

2. 서버 띄울때 버그

3. 런타임 버그


XML에 정의할수 있다.



JPA 기반 프로젝트

Spring Data JPA

QueryDSL



반복되는 CRUD


public class MemberRepository {


public void save(Member member) {}

public Member findOne(Long id) {}

public List<Member> findAll() {}

public Member findByUsername(String username) {}

}


public class ItemRepository {

public void save(Item item) {}

public Member findOne(Long id) {}

public List<Member> findAll() {}

}


save, findOne, findAll

다 비슷한 유형들...


스프링 데이터 JPA

지루하게 반복되는 CRUD 문제를 세련된 방법으로 해결

개발자는 인터페이스만 작성

스프링 데이터 JPA가 구현 객체를 동적으로 생성해서 주입


//JpaRepository에서 공통적인거 다 해놓음

public interface MemberRepository extends JpaRepsoitory<Member, Long> {

Member findByUsername(String username);

}


public interface ItemRepository extends JpaRepsoitory<Item, Long> {

//비어있음

}


interface JpaRepository


save()

findOne()

findAll()

...


<<interface>>

ItemRepository

|

|

Spring Data JPA ---생성---> ItemRepostory 구현클래스


공통인터페이스 기능

JpaRepository 인터페이스 : 공통 CRUD 제공

제네릭<엔티티, 식별자>설정


스프링 데이터


스프링 데이터 JPA



메서드 이름으로 쿼리생성

메서드 이름만으로 JPQL 쿼리 생성

public interface MemberRepository extends JpaRepository<Member, Long> {

List<Member> findByName(String username);

}


List<Member> members = memberRepository.findByName("hello");


실행SQL

SELECT * FROM MEMBER M WHERE M.NAME = 'hello'




Web 페이징과 정렬기능

컨트롤러에서 페이징 처리 객체를 바로 받음

page : 현재페이지

size : 한 페이지에 노출할 데이터 건수

sort : 정렬 조건


/member?page=0&size=20&sort=name,desc


@RequestMapping(value= "/members", method = RequestMethod.GET)

String list(Pageable pageable, Model model) {}



QueryDSL


SQL, JPQL을 코드로 작성할 때 도와주는 빌더 API

JPA 크리테리아에 비해서 편리하고 실용적임

오픈소스


SQL, JPQL은 문자, Type-check 불가능

해당 로직 실행전까지 작동여부 확인 불가


SELECT * FROM MEMBERR WHERE MEMBER_10 =100

MEMBERR 오류발견(실행시점에)


장점

문자가 아닌 코드로 작성

컴파일시점에 문법 오류 발견

코드 자동완성 IDE 도움

단순하고 쉬움: 코드 모양이 JPQL과 거의 비슷

동적쿼리


Member.java@Entity => APT =>생성  QMember.java 


JPAFactoryQuery query = new JPAQueryFactory(em);

QMember m = QMember.member;


List<Member> list = query.selectFrom(m)

.where(m.age.gt(18))

.orderBy(m.name.desc())

.fetch();


출처 - 자바 ORM 표준 JPA 프로그래밍 저자- 김영한

출처 - SKplanet Tacademy

반응형

'JAVA > JPA' 카테고리의 다른 글

JPA 기본  (0) 2019.01.03