[JPA] JPQL

보람·2023년 5월 8일
0

Spring

목록 보기
16/18

💻 JPQL

1. JPQL

  • Java Persistence Query Language
  • JPA가 제공하는 객체 지향 쿼리 언어
    • JPA를 사용하면 엔티티 객체를 중심으로 개발
    • JPA는 SQL을 추상화환 JPQL이라는 객체 지향 쿼리 언어 제공
    • JPA는 JPQL을 분석한 후 적절한 SQL을 만들어서 데이터베이스를 조회
  • SQL vs JQPL
    • SQL : 데이터베이스 테이블을 대상으로 쿼리
    • JQPL : 엔티티 객체를 대상으로 쿼리
      • SQL과 문법 유사 -> select, from, where, group by, having, join 지원
      • SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않음

💡 SQL

	select * from Member m where m.age>18

💡 JPQL

	String jpql = "select m from Member m where m.age > 18"; 
	List<Member> result = em.createQuery(jpql, Member.class)
    						.getResultList();

2. JPQL 사용하기

(1) 사용 기본

① 대소문자 구분

  • 엔티티와 속성은 대소문자를 구분
    • Member -> member ❌, age-> Age ❌
  • JPQL 키워드는 대소문자 구분 ❌
    • SELECT -> select ⭕, WHERE -> where ⭕

② 엔티티 이름

  • 테이블명 대신 엔티티명을 사용
    • @Entity(name="")으로 설정 가능
  • 지정하지 않을 시 클래스 명을 기본값으로 사용(추천)

③ 별칭은 필수(알리야스)

  • JPQL은 별칭은 필수
  • as는 생략 가능
	em.createQuery( "select m from Member as m" );
    						↓
    em.createQuery( "select m from Member m" );

(2) JPQL 문법

  • select
	select 절
	from 절
	where 절
	group by 절
	having 절
	orderby 절
  • update
	update 절 where 절
  • delete
  delete 절 where 절
  • 집합정렬
	select count(m),		// 회원수
	        sum(m.age), 	// 나이 합
	        avg(m.age),		// 평균 나이
	        max(m.age),		// 최대 나이
	        min(m.age)		// 최소 나이
	from Member m

(3) 반환 타입 지정

  • TypeQuery : 반환 타입이 명확할 때 사용
    💡 반환타입 정확히 MEMBER
	TypeQuery<Member> query = 
		em.createQuery("select m from Member m", Member.class);
  • Query : 반환 타입이 명확하지 않을 때 사용
    💡 여러개 SELECT -> 타입 불명확
	Query Query = 
		em.createQuery("select m.username, m.age from Member m");   

(4) 결과 조회

  • 반환된 객체 출력하기 위해 사용

  • query.getResultList()

    • 결과가 하나 이상일 때, 리스트 반환
      • 결과가 없으면 빈 리스트 반환
      • 빈 collection이 반환되기 때문에 NullPointerException에 대한 걱정은 하지 않아도 됨
  • query.getSingleResult()

    • 결과가 정확히 하나(조심)
    • 단일 객체 반환
      • 결과가 없으면 : javax.persistence.NoResultException
      • 결과가 둘 이상이면 : javax.persistence.NonUniqueResultException

(5) 파라미터 바인딩

  • 이름 기준 : 변수앞에 :
 Member singleResult1 = em.createQuery("select m from Member as m where m.username = :username", Member.class)
                    	  .setParameter("username", "member1")
 	                      .getSingleResult();
  • 위치 기준 : 실질 사용 ❌
em.createQuery("select m from Member as m where m.username = ?1", Member.class)
  .setParameter(1, "member1");

(6) 프로젝션

  • select 절에 조회할 대상을 지정하는 것
  • 프로젝션 대상 : 엔티티, 임베디드 타입, 스칼라 타입(숫자, 문자등 기본 타입)

💡 엔티티 프로젝션

  • Member 엔티티 조회
	List<Member> result = em.createQuery("select m from Member m", Member.class)
							.getResultList();
  • Member와 관련된 team 조회
	List<Team> result2 = em.createQuery("select m.team from Member m", Team.class)
						   .getResultList();
  • join문으로 Team 조회
	List<Team> result2 = em.createQuery("select t from Member m join m.team t", Team.class)
						   .getResultList(); 

💡 임베디드 타입 프로젝션

	// 직접 가져올 수 없고 o.address가 소속되어 있는 엔티티에서 가지고 와야 함
	List<Address> findAddress = em.createQuery("select o.address from Order o", Address.class)
  								  .getResultList();

💡 스칼라 타입 프로젝션

  • object 받아오기
	// 여러 값 조회
	List resultList = em.createQuery("select m.username, m.age from Member m")
						.getResultList();
                        
	// 타입을 지정하지 못하니까 object 받아오기
	Object o = resultList.get(0);
    
    // 배열로 담겨 있어야 꺼내쓰기가 쉬움
	Object[] result3 = (Object[])o;
	System.out.println("username = " + result3[0]);
	System.out.println("age = " + result3[1]);                        
  • Object배열의 리스트
	List<Object[]> resultList2 = em.createQuery("select m.username, m.age from Member m")
							       .getResultList();
	
	Object[] result4 = resultList2.get(0);
	System.out.println("username = " + result4[0]);
	System.out.println("age = " + result4[1]);
  • DTO
	List<MemberDTO> result5 
		= em.createQuery("select new com.codingbox.jpql.MemberDTO(m.username, m.age) from Member m")
			.getResultList();
	
	MemberDTO memberDTO = result5.get(0);
	
	System.out.println("memberDTO = " + memberDTO.getUsername());
	System.out.println("memberDTO = " + memberDTO.getAge());
- MemberDTO
	@Getter @Setter
	public class MemberDTO {

		private String username;
		private int age;
	
		public MemberDTO(String username, int age) {
			super();
			this.username = username;
			this.age = age;
		}
	}

(7) 페이징 API

  • 페이징 처리를 위해서는 order by 사용
  • JPA는 페이징을 다음 두 API로 추상화
    • setFirstResult(int startPosition) : 조회 시작 위치(0부터 시작)
    • setMaxResults(int maxResult) : 조회할 데이터 수
      -> 몇 번째 부터 몇 개 가지고 올 것인가
	// 나이 순으로 멤버 조회
	String jpql = "select m from Member m order by m.age desc";
    // 10번째 부터 20개 데이터 조회
	List<Member> resultList = em.createQuery(jpql, Member.class)
								.setFirstResult(10)
								.setMaxResults(20)
								.getResultList();
	
    // 멤버 리스트 조회
	System.out.println("result.size : " + resultList.size());
	
	for(Member member1 : resultList) {
		System.out.println("member 1 = " + member1);
	}

(8) 조인

  • 문법이 객체 스타일
  • 내부 조인 : inner 생략 가능
	String jpql = "select m from Member m inner join m.team t";
	List<Member> resultList = em.createQuery(jpql, Member.class)
								.getResultList();
  • 외부 조인 : outer 생략 가능
	String jpql2 = "select m from Member m left outer join m.team t";
	List<Member> resultList2 = em.createQuery(jpql2, Member.class)
								 .getResultList();
  • 세타 조인 (연관관계가 없는 조인)
	String jpql3 = "select m from Member m, Team t where m.username = t.name";
	List<Member> resultList3 = em.createQuery(jpql3, Member.class)
								 .getResultList();

(9) 서브 쿼리

  • SQL과 마찬가지로 서브쿼리도 JPQL에서 지원
  • 하지만 FROM문에 대한 서브쿼리는 지원 ❌
  • 조인으로 풀 것
	String jpql = "select m from Member m where m.age > (select avg(m2.age) from Member m2)";
	List<Member> resultList = em.createQuery(jpql, Member.class)
								.getResultList();
profile
안녕하세요, 한보람입니다.

0개의 댓글