노트코드
노트코드
노트코드

블로그 메뉴

  • 홈
  • 이력서
  • 이전 블로그
  • 글쓰기
  • 관리자페이지
  • 분류 전체보기 (57)
    • 코틀린 (2)
      • 실무 프로젝트로 배우는 Kotlin & Sprin.. (2)
    • JAVA (1)
      • 디자인패턴 (1)
      • 객체지향 5대원칙 (0)
    • SPRING (32)
      • JPA (11)
      • 스프링시큐리티 (1)
      • 스프링 (8)
      • QueryDsl (1)
      • 스프링배치 (11)
    • AZURE (0)
    • ETC (10)
      • MAVEN (0)
      • GIT (0)
      • ReMind (3)
      • Exception (1)
      • CS (6)
    • 책 (8)
      • 이것이 자바다 (8)

최근 글

최근 댓글

태그

  • JPA
  • 스프링
전체 방문자
오늘
어제
hELLO · Designed By 정상우.
노트코드

노트코드

카테고리 없음

[JPA] 객체지향쿼리

2022. 1. 21. 02:04

JPQL

JPQL은 엔티티 객체를 조회하는 객체지향 쿼리다.
JPQL은 SQL을 추상화해서 특정 데이터베이스에 의존하지 않으며, SQL에 비해 간결하다.

@Entity(name="MEMBER")
public class Member{
    @Column(name="name")
    private String username;
}

//사용

String jpql = "select m from Member as m where m.username = "kim"
List<Member> resultList = 
        em.createQuery(jpql, Member.class).getResultList();

작성한 JPQL을 실행하려면 쿼리 객체를 만들어야 한다. 쿼리 객체는

TypeQuery와 Query가 있는데 반환할 타입을 명확하게 지정할 수 있으면 TypeQuery객체를 사용하고 명확하게 지정할 수 없으면 Query 객체를 사용하면 된다.

//Type Query사용
TypeQuery<Member> query = em.createQuery("SELECT m FROM Member m", Member.class);
for(Member member : resultList)({
    System.out.println("member = " + member);
})

// Query사용
Query query = em.createQuery("SELECT m.username, m.age from Member m");
for(Object o : resultList)({
    Object[] result = (Object[]) o; //결과가 둘 이상이면 Object[]반환
})

SELECT 절에서 여러 엔티티나 컬럼을 선택할 때는 반환할 타입이 명확하지 않으므로 Query 객체를 사용해야 한다.

결과조회

  • query.getResultList() : 결과를 예제로 반환한다. 만약 결과가 없으면
    빈 컬렉션을 반환한다.

  • query.getStringResult(): 결과가 정확히 하나일 때 사용한다.

    -결과가 없으면 NoResultException 예외가 발생한다.

    -결과가 1개보다 많으면 NonUniqueResultException예외가 발생

파라미터 바인딩

-이름 기준 파라미터:

String usernameParam = "User1";

TypeQuery<Member> query = em.createQuery("SELECT m FROM Member m where m.username = :username",Member.class);

query.setParameter("username", usernameParam);
List<Member> resultList = query.getResultList();

-위치 기준 파라미터

List<Member> members = 
                em.createQuery("SELECT m FROm Member m where m.username =?1", Member.class)
                    .setParameter(1, usernameParam)
                    .getResultList(); 

프로젝션

SELECT 절에 조회할 대상을 지정하는 것을 프로젝션이라 한다.

프로젝션 대상은 엔티티, 엠비디드타입, 스칼라 타입이 있다.

-임베디드 타입 프로젝션

임베디드 타입은 조회의 시작점이 될 수 없다는 제약이 있다.

String query = "SELECT o.address FROM Order o ";
List<Address> addresses = em.createQuery(query,Address.class).getResultList();

임베디드 타입은 엔티티 타입이 아닌 값 타입이다. 따라서 이렇게 직접 조회한 임베디드 타입은 영속성 컨텍스트에서 관리되지 않는다.

-스칼라 타입 프로젝션

숫자, 문자 날짜와 같은 기본 데이터 타입들을 스칼라 타입이라 한다.

List<String> usernames = em.createQuery("select username FROM Member m ", String.class).getResultList();

중복 데이터를 제거하려면 DISTINCT를 사용한다.

-여러 값 조회

꼭 필요한 데이터들만 선택해서 조회할때 사용한다. 프로젝션에 여러 값을 선택하면 TypeQuery를 사용할 수 없고 대신에 Query를 사용해야 한다.

Query query = em.createQuery("SELECT m.username, m.age FROM Member m ");
List resultList = query.getResultList();

Iterator iterator = resultList.iterator();
while(iterator.hasNext()){
    Object[] row = (Object[]) iterator.next();
    String username = (String) row[0];
    Integer age = (Integer) row[1];
}
List<Object[]> resultList = 
Qem.createQuery("SELECT m.username, m.age FROM Member m ");
List resultList = query.getResultList();

Iterator iterator = resultList.iterator();
while(iterator.hasNext()){
    Object[] row = (Object[]) iterator.next();
    String username = (String) row[0];
    Integer age = (Integer) row[1];
}

-NEW 명령어

  • 패키지 명을 포함한 전체 클래스 명을 입력해야 한다.
  • 순서와 타입이 일치하는 생성자가 필요하다.
TypedQuery<UserDTO> query = em.createQuery("SELECT new jpabook.jpql.UserDTO(m.username, m.age) FROM Member m", UserDTO.class);
List<UserDTO> resultList = query.getResultList();

List<UserDTO> resultList = query.getResultList();

페이징 API

  • setFirstResult(int startPosition): 조회 시작 위치(0부터 시작한다)
  • setMaxResults(int maxResult): 조회할 데이터 수
TypeQuery<Member> query = em.createQuery("SELECT m FROM Member m ORDER BY m.username DESC"), Member.class);
query.setFirstResult(10);
query.setMaxResult(20);
query.getResultList();

-페치 조인

페치 조인은 JPQL에서 성능 최적화를 위해 제공하는 기능이다.
이것은 연관된 엔티티나 컬렉션을 한 번에 같이 조회하는 기능인데
join fetch 명령어로 사용할 수 있다.

엔티티 페치 조인

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

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

for(Member member : members){
    //패치 조인으로 회원과 팀을 함께 조회하기 때문에 지연 로딩 발생안함
    System.out.println(member.getUsername()+"teamname = "+member.getTeam().name());
}

Criteria쿼리

JPQL을 생성하는 빌더 클래스다. Criteria의 장점은 문자가 아닌
query.select(m).where(...)처럼 프로그래밍 코드로 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();

Criteria쿼리 생성 방법

  • Criteria쿼리를 생성하려면 먼저 Criteria 빌더를 얻어야 한다. Criteria빌더는 EntityManager나
    EntityManagerFactory에서 얻을 수 있다.
    CriteriaBuilder cb = em.getCriteriaBuilder();
  • Criteria 쿼리 빌더에서 Criteria쿼리를 생성한다. 이떄 반환 타입을 지정할 수 있다.
    CriteriaQuery<Member> cq = cb.createQuery(Member.class)
  • From 절을 생성한다. 반환된 값 m은 Criteria에서 사용하는 특별한 별칭이다. m을 조회의 시작점
    이라는 의미로 쿼리 루트라 한다.
    Root<Member> m = cq.from(Member.class)
  • SELECT 절을 생성한다.
    cq.select(m);

조건절 생성 방법

  • Predicate usernameEqual = cb.equal(m.get("username"),"회원1")
  • Order ageDesc = cb.desc(m.get("age"))
  • cq.select(m).where(usernameEqual).orderBy(ageDesc)
  • em.createQuery(cq).getResultList();

튜플

Criteria는 Map과 비슷한 튜플이라는 특별한 반환 객체를 제공한다.

CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Tuple> cq = cb.createTupleQuery();
        Root<Member> m = cq.from(Member.class);
        cq.multiselect(m.get("username").alias("username"),
                       m.get("age").alias("age")
        );

        TypedQuery<Tuple> query = em.createQuery(cq);
        List<Tuple> resultList = query.getResultList();
        for (Tuple tuple : resultList) {
            String username = tuple.get("username", String.class);
            Integer age = tuple.get("age",Integer.class);
        }

QueryDSL

QueryDSL도 Criteria처럼 JPQL 빌더 역할을 한다. QueryDSL의 장점은 코드 기반이면서 단순하고 사용하기 쉽다.

EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        JPAQuery query = new JPAQuery(em);
        QMember qMember = new QMember("m");
        List<Member> members = query.from(qMember)
                                                                        .where(qMember.name.eq("회원1"))
                                                                        .orderBy(qMember.name.desc())
                                                        .list(qMember);

조인

QJoinParent joinParent = QJoinParent.joinParent;
QJoinChild joinChild = QJoinChild.joinChild;

List<JoinParent> list = query.from(joinParent).leftJoin(joinParent.joinChild, joinChild)
        .fetch().on(joinChild.isNotNull()).list(joinParent);

조인의 기본 문법은 첫 번째 파라미터에 조인 대상을 지정하고, 두 번째 파라미터에 별칭으로 사용할 쿼리 타입을 지정하면 된다.
→join(조인대상, 별칭으로 사용할 쿼리 타입)

네이티브 SQL

SQL을 직접 사용할 수 있는 기능으로 SQL은 지원하지만 JPQL이 지원하지 않는 기능이 있을때 네이티브 SQL을 사용한다.

네이티브 쿼리 API는 다음3가지가 있다.

//결과 타입 정의
public Query createnativeQuery(String sqlString, Class resultClass)
//결과 타입을 정의할 수 없을 때
public Query createnativeQuery(String sqlString)
//결과 매핑 사용
public Query createnativeQuery(String sqlString, String resultSetMapping)

엔티티 조회
네이티브 SQL은 em.createNativeQuery(SQL, 결과 클래스)를 사용한다.
첫번째 파라미터는 네이티브 SQL을 입력, 두번째 파라미터는 조회할
엔티티의 타입을 입력한다.

    노트코드
    노트코드
    노션 블로그에서 티스토리로 이전공사중

    티스토리툴바