2019. 2. 9. 13:44ㆍWeb/JPA
JPA - JPQL(객체지향쿼리)
jpa-study.zip
(예제소스파일/ jpql package참조)
JPQL은 가장 중요한 객체지향 쿼리 언어이다. Criteria나 QueryDSL은 결국 JPQL을 편리하게 사용하도록 도와주는 기술이므로 JPA로 데이터베이스 엑세스를 다룬다면 JPQL은 꼭 필수라고 생각이 든다. SQL과 꼭 닮은 쿼리 언어이며 SQL은 데이터 중심의 쿼리라고 하면 JPQL은 엔티티를 대상으로 하는 쿼리 언어라고 할 수 있다. 결국 JPA에서 해당 JPQL을 분석한 다음 적절한 SQL로 변환해주어서 데이터베이스에서 데이터를 가져오는 것이다.
JPQL 특징
SELECT문
TypeQuery, Query
위에서 설명한 것과 같이 반환타입이 명확한가? 명확하지 않은가? 에 따라 두개중 하나의 쿼리 객체를 이용한다.
파라미터 바인딩(parameter bind)
2. 위치 기준 파라미터 - 위치 기준으로 파라미터를 구분하는 방법 "?"를 사용한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | /* * SELECT문 * 1. 엔티티와 속성은 대소문자를 구분한다. but JPQL keyword는 대소구분을 않는다. * 2. Member는 테이블명이 아닌, 클래스명도 아닌 엔티티 명이다. * 3. 엔티티명에 별칭은 필수값으로 사용해야한다. */ public void select(){ String jpql="select m from MemberJPQL m where m.username = '여성게'"; /* * TypeQuery - 반환타입이 명확할때 * Query - 반환타입이 명확하지 않을때 */ TypedQuery<MemberJPQL> query=em.createQuery(jpql,MemberJPQL.class); /* * getResultList() - 결과를 리스트로 받는다.(결과가 없으면 빈 컬렉션을 반환.) * * getSingleResult() - 결과가 정확히 하나임을 기대할때 사용한다. * - 결과가 없으면 javax.persistence.NoResultException * - 결과가 하나 이상이면 javax.persistence.NonUniqueResultException */ List<MemberJPQL> resultList=query.getResultList(); System.out.println("================select================="); for(MemberJPQL m:resultList){ System.out.println(m.toString()); } String jpql2="select m.username,m.age from MemberJPQL m where m.username = '여성게'"; Query query2=em.createQuery(jpql2); List resultList2=query2.getResultList(); for(Object o:resultList2){ Object[]result=(Object[])o; for(Object o2:result){ System.out.println("result element => "+o2); } } System.out.println("================select================="); } | cs |
프로젝션(projection)
SELECT 절에 조회할 대상을 지정하는 것을 프로젝션이라 한다. [SELECT {프로젝션 대상} FROM~] 프로젝션 대상은 엔티티, 임베디드 타입, 스칼라 타입이 있다. 스칼라 타입은 숫자,문자 등 기본 데이터 타입을 뜻한다(엔티티 클래스에서 int,Long,String 등의 필드)
여기서 하나 얘기할 것이 있다면 임베디드 타입 프로젝션은 절대 조회의 시작점이 될 수 없다. 즉, From절에서 엔티티를 정의하고 select에서 엔티티.임베디드필드로 참조해야한다.(SELECT m.embeddedTypeField FROM MemberEntity m ~)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | /* * projection - SELECT 절에 조회할 대상을 지정하는 것. * * ex) SELECT {projection} FROM ~ * * 1.엔티티 * 2.임베디드 * 3.스칼라(숫자,문자 등 기본 데이터타입) */ public void projection() { /* * 엔티티타입 프로젝션 - 조회한 엔티티는 영속성컨텍스트에서 관리된다. */ String jpql = "select m.team from MemberJPQL m where m.username = '여성게1'"; TypedQuery<TeamJPQL> query = em.createQuery(jpql,TeamJPQL.class); List<TeamJPQL> resultList = query.getResultList(); System.out.println("================projection================="); for(TeamJPQL t : resultList) { System.out.println(t.toString()); } /* * 임베디드타입 프로젝션 - 임베디드 타입은 조회의 시작점이 될 수 없기에 Order엔티티로 조회를 시작해 * 그다음 임베디드 타입을 참조한다. */ String jpql2 = "select o.address from OrderJPQL o where o.product.name ='맥북1'"; TypedQuery<AddressJPQL> query2 = em.createQuery(jpql2,AddressJPQL.class); List<AddressJPQL> resultList2 = query2.getResultList(); for(AddressJPQL o : resultList2) { System.out.println(o.toString()); } /* * 스칼라타입 - 여러종류의 스칼라타입을 받기 위해서는 TypeQuery를 사용할 수 없다. * 해결책 - new 명령어를 사용한 DTO 타입변환 * 반드시 밑 문자열에 들어간 파라미터의 순서대로 해당 dto에 생성자가 존재해야한다. */ String jpql3 = "select new com.spring.jpa.jpql.MemberDTO(m.username,m.age)" + " from MemberJPQL m where m.username = '여성게1'"; TypedQuery<MemberDTO> query3 = em.createQuery(jpql3,MemberDTO.class); List<MemberDTO> members = query3.getResultList(); for(MemberDTO dto : members) { System.out.println(dto.toString()); } System.out.println("================projection================="); } | cs |
위에서 조금 특이한 것이 있다면 jpql3이다. 조회할 대상이 여러개이면 TypeQuery로는 조회할 수 없기 때문에 하나의 DTO를 만들어서 위와 같이 "new" 명령어를 사용해서 결과를 DTO로 받아 TypeQuery 객체를 이용할 수 있다.
페이징 API
페이징을 하기 위해서는 다소 지루하고 반복적인 일이다. 게다가 데이터베이스 구현체에 따라 문법도 다르다. 하지만 JPA는 모든 데이터베이스에서 동일하게 사용할 수 있는 페이징 메소드를 제공해준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public void paging() { String jpql = "select m from MemberJPQL m "; TypedQuery<MemberJPQL> query = em.createQuery(jpql,MemberJPQL.class); //11부터 조회 query.setFirstResult(10); //20개 11~30까지 조회 query.setMaxResults(20); List<MemberJPQL> members = query.getResultList(); System.out.println("================paging================="); for(MemberJPQL m : members) { System.out.println(m.toString()); } System.out.println("================paging================="); } | cs |
현 예제는 값을 직접 입력해주었지만, 추후에는 컨트롤러 단에서 페이징에 대한 데이터를 직접 주입받아서 사용하지 않을까 싶다.(Pagable 클래스)
집합과 정렬
함수 |
설명 |
COUNT |
결과 수를 구한다. 반환타입:Long |
MAX,MIN |
최대,최소 값을 구한다. |
AVG |
평균값을 구한다. 반환타입: Double |
SUM |
합을 구한다. 정수합 반환타입 : Long 소수합 반환타입 : Double BigInteger,BigDecimal합 반환타입 : BigInteger,BigDecimal |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public void groupFunction() { String jpql = "select count(m) from MemberJPQL m "; Query query = em.createQuery(jpql); Long count = (Long) query.getSingleResult(); System.out.println("================groupFunction================="); System.out.println("count = "+count); System.out.println("================groupFunction================="); } public void groupByHaving() { String jpql = "select t.name, count(m.age) " + "from MemberJPQL m left join m.team t " + "group by t.name "; Query query = em.createQuery(jpql); List members = query.getResultList(); System.out.println("================groupByHaving================="); for(Object o : members) { Object[] result = (Object[]) o; for(Object o2 : result) { System.out.print("result element => "+o2); } System.out.println(); } System.out.println("================groupByHaving================="); } | cs |
집합함수 사용시 참고사항
1.Null 값은 무시하므로 통계에 잡히지 않는다.
2.만약 값이 없는데 집합함수를 사용한다면 Null값이 된다.(단 count는 0)
3.DISTINCT를 집합함수 안에 사용하여 중복된 값을 제거하고 집합을 구할 수 있다.
(ex select count(distinct m.age) from MemberJPQL m)
4.DISTINCT를 count에서 사용할 때 임베디드타입 필드는 지원하지 않는다.
여기까지 JPQL포스팅을 한번 끊고 양이 많아 JPQL 조인부터는 다음 포스팅에 정리하겠습니다. :)
'Web > JPA' 카테고리의 다른 글
JPA - JPQL 서브쿼리 (객체지향 쿼리 언어) (0) | 2019.02.09 |
---|---|
JPA - JPQL 조인(객체지향쿼리),Java Persistence Query Language (0) | 2019.02.09 |
JPA - @Embedded,@Embeddable 임베디드타입 (0) | 2019.02.04 |
JPA - 영속성 전이(Cascade)와 고아 객체(Orphan) (0) | 2019.02.04 |
JPA - 즉시로딩과 지연로딩(FetchType.EAGER,FetchType.LAZY) 그리고 프록시 (0) | 2019.02.04 |