객체 지향 쿼리

김강현·2023년 3월 27일
2

ORM-JPA

목록 보기
8/9

JPQL

  • JPA는 SQL을 추상화한 JPQL 이라는 객체 지향 쿼리 언어 제공!
  • SQL 문법과 유사
  • JPQL은 엔티티 객체를 대상으로 쿼리
  • 이를 SQL로 변환하여 데이터베이스 테이블을 대상으로 쿼리
  • 특정 데이터베이스 SQL에 의존하지 않음. (구현체에서 DB별로 적용되도록 할테니)
String jpql = "select m From Member m where m.name like `%hello%`";

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

Criteria

  • JPQL의 여러 단점들을 보완하기 위해 만든 (동적쿼리 힘듬 등)
  • 쿼리를 JPQL 문이 아닌, 코드로 구성하기 위해서
  • 코드로 구성이 되기에, 컴파일 에러, 동적쿼리 면에서 훨씬 유리
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Member> query = cb.createQuery(Member.class);

Root<Member> m = query.from(Member.class);

CriteriaQuery<Member> cq = query.select(m).where(cb.equal(m.get("username"), "kim"));

List<Member> result = em.createQuery(cq).getResultList();

김영한 개발자님 曰 : 나는 실무에서 안씀. 생각보다 알아보기 힘들고, 유지보수가...

QueryDSL

  • 자바코드로 JPQL 작성 가능 (criteria 랑 비슷) -> 컴파일 시점 문법 오류
  • 동적 쿼리 작성이 편리함!
  • 단순하고 쉬움
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();

====>>> 실무 사용 권장 JPQL 마스터 하면, 자동으로 따라옴!!

네이티브 SQL

  • JPA 가 제공하는 SQL 문
  • DB별로 특별한 SQL 같은 것들, 구현체가 지원을 해주는 경우가 있음
String sql = "SELECT ID, AGE, TEAM_ID, NAME FROM MEMBER WHERE NAME = 'kim'";
List<Member> resultList = em.createNativeQuery(sql, Member.class).getResultList();

JPA 외부 라이브러리 DB Connection

  • JPA 관련 쿼리 함수들을 사용하면, 쿼리문이 날아갈때 자동으로 flush() 함수 호출
  • 외부 라이브러리들은 영속성 컨텍스트와 연결이 안되어 있어서, flush()를 수동 호출 필수!

JDBC, SpringJdbcTemplate, 마이바티스 등

JPQL 파고들기

  • Java Persistence Query Language

각종 기능

type query

  • TypedQuery 는 query 결과값 타입이 정해져있을때
  • 그냥 Query 는 결과값 타입이 명확하지 않을때
TypedQuery<Member> query = em.createQuery("select m from Member m", Member.class);
Query query2 = em.createQuery("select m.username, m.age from Member m");

결과 조회

List<Member> resultList = query.getResultList();
=> 결과가 없으면 빈 리스트 반환

Member result = query.getSingleResult();
=> 결과 없거나, 2개 이상이면 에러

파라미터 바인딩

Member result = em.createQuery("select m from Member m where m.username = :username", Member.class)
   .setParameter("username", "member1")
   .getSingleResult();

프로젝션

  • SELECT 절에 조회할 대상을 지정하는 것.
  • 프로젝션 대상 : 엔티티, 임베디드 타입, 스칼라 타입(기본 데이터)

엔티티 프로젝션

  • 엔티티 프로젝션의 경우 불러오면서 영속성 컨텍스트에 들어감
Member member = new Member();
member.setUsername("changer");
member.setAge(10);
em.persist(member);

em.flush();
em.clear();

List<Member> results = em.createQuery("select m from Member m", Member.class).getResultList();
Member findMember = results.get(0);
findMember.setAge(20);
  • Join 이 사용될때, 이것도 자동으로 인식해서 해줌
  • But, 코드에 join 을 넣어줘서 예측 가능하게 해주는 것이 더 좋음 (김영한 개발자님)
// 자동으로 jpql이 변환
List<Team> teamResults1 = em.createQuery("select m.team from Member m",Team.class).getResultList();
// 애초에 jpql 로 선언
List<Team> teamResults2 = em.createQuery("select t from Member m join m.team t",Team.class).getResultList();

임베디드 타입 프로젝션

List<Address> addressResults = em.createQuery("select o.address from Order o", Address.class).getResultList();

스칼라 타입 프로젝션

1

  • 받아오는 타입이 지정되어 있지 않기 때문에, Object[]로 변환 후 가져와야함.
List scalarResults = em.createQuery("select distinct m.username, m.age from Member m").getResultList();
Object o = scalarResults.get(0);
Object[] result = (Object[]) o;
System.out.println("result[0] = " + result[0]);
System.out.println("result[1] = " + result[1]);

2

  • scalarResults 변수 선언할때 해도 됨.
List<Object[]> scalarResults = em.createQuery("select distinct m.username, m.age from Member m").getResultList();
Object[] result = scalarResults.get(0);
System.out.println("result[0] = " + result[0]);
System.out.println("result[1] = " + result[1]);

3

  • 가장 깔끔한 방법은 이거임!

MemberDTO.java

public class MemberDTO {
    private String username;
    private int age;

    public MemberDTO(String username, int age) {
        this.username = username;
        this.age = age;
    }
}
em.createQuery("select new org.example.MemberDTO(m.username, m.age) from Member m", MemberDTO.class);
  • new __(패키지)__.MemberDTO() from Member
  • 해당 문구가 필요함!

페이징

  • jpa 는 페이징은 두 API 로 추상화를 끝내버림
  • setFirstResult : 조회 시작 위치
  • setMaxResults : 조회할 데이터 수
List<Member> pageResults = em.createQuery("select m from Member m order by m.age desc", Member.class)
       .setFirstResult(0)
       .setMaxResults(10)
       .getResultList();
for (Member mmm : pageResults) {
    System.out.println("member = " + mmm.toString());
}

조인

  • 내부 조인
String query = "select m from Member m inner join m.team t"; // inner 생략 가능
  • 외부 조인
String query = "select m from Member m left outer join m.team t"; // outer 생략 가능
String query = "select m from Member m right outer join m.team t"; // outer 생략 가능
  • 세타 조인
String query = "select m from Member m, Team t where m.username = t.name";

서브쿼리

  • jpa 는 where, having 절에서만 서브 쿼리 사용 가능
  • 하이버네이트의 경우 select 절에서도 지원
  • from 절에서의 서브쿼리는 현재 불가능 (조인으로 풀 수 있으면 해결)
// 나이가 평균보다 많은 회원
select m from Member m where m.age > (select avg(m2.age) from Member m2)

// 한 건이라도 주문한 고객
select m from Member m where (select count(o) from Order o where m = o.member) > 0

JPQL 타입 표현

profile
this too shall pass

0개의 댓글