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

블로그 메뉴

  • 홈
  • 이력서
  • 이전 블로그
  • 글쓰기
  • 관리자페이지
  • 분류 전체보기 (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 정상우.
노트코드

노트코드

SPRING/JPA

[JPA]객체지향쿼리

2022. 2. 9. 13:35

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(): 결과가 정확히 하나일 때 사용한다.-결과가 1개보다 많으면 NonUniqueResultException예외가 발생
  • -결과가 없으면 NoResultException 예외가 발생한다.

파라미터 바인딩

-이름 기준 파라미터:

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을 입력, 두번째 파라미터는 조회할
엔티티의 타입을 입력한다.

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

[JPA]JPA,Hibernate, Spring Data JPA차이  (0) 2022.02.09
[JPA] 연관관계  (0) 2022.02.09
[JPA]스프링 데이터 Common:커스텀 리포지토리  (0) 2022.01.21
[JPA]MappedSuperClass  (0) 2022.01.21
[JPA] 프록시  (0) 2022.01.21
    'SPRING/JPA' 카테고리의 다른 글
    • [JPA]JPA,Hibernate, Spring Data JPA차이
    • [JPA] 연관관계
    • [JPA]스프링 데이터 Common:커스텀 리포지토리
    • [JPA]MappedSuperClass
    노트코드
    노트코드
    노션 블로그에서 티스토리로 이전공사중

    티스토리툴바