ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • #JPA-19 페이징 API, JOIN, JPA 기타
    JPA 2021. 2. 23. 16:38

    [출처] 인프런 김영한 강사님 -자바 ORM 표준 JPA 프로그래밍 기본

    <페이징 API>

    • JPA는 페이징을 다음 두 API로 추상화

    • setFirstResult(int startPosition) : 조회 시작 위치 (0부터 시작)

    • setMaxResults(int maxResult) : 조회할 데이터 수

     

    페이징 API 예시

     

    페이징 API - MySQL 방언

    페이징 API - Oracle 방언

     

    <조인>

    • 내부 조인: SELECT m FROM Member m [INNER] JOIN m.team t

    JpaMain

                Team team = new Team();
                team.setName("teamA");
                em.persist(team);
    
                Member member = new Member();
                member.setUsername("member1");
                member.setAge(10);
    
                member.setTeam(team);
                em.persist(member);
    
                em.flush();
                em.clear();
    
                String query ="select m from Member  m inner join  m.team t";
                List<Member> result = em.createQuery(query, Member.class).getResultList();
    
    
                tx.commit();

    Member Class 1:N은 조심해야할게 Fetch를 LAZY로 내둬야한다.

    package jpql;
    
    import javax.persistence.*;
    
    @Entity
    public class Member {
    
        @Id @GeneratedValue
        private Long id;
        private String username;
        private int age;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "TEAM_ID")
        private Team team;
    
        public void chageTeam(Team team){
            this.team = team;
            team.getMembers().add(this);
        }
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    

    console

    Hibernate: 
        /* select
            m 
        from
            Member  m 
        inner join
            m.team t */ select
                member0_.id as id1_0_,
                member0_.age as age2_0_,
                member0_.TEAM_ID as TEAM_ID4_0_,
                member0_.username as username3_0_ 
            from
                Member member0_ 
            inner join
                Team team1_ 
                    on member0_.TEAM_ID=team1_.id

     

    • 외부 조인: SELECT m FROM Member m LEFT [OUTER] JOIN m.team t (outer는 생략가능)

    JpaMain Class

    String query ="select m from Member  m left outer join  m.team t";
    List<Member> result = em.createQuery(query, Member.class).getResultList();

    console

    Hibernate: 
        /* select
            m 
        from
            Member  m 
        left outer join
            m.team t */ select
                member0_.id as id1_0_,
                member0_.age as age2_0_,
                member0_.TEAM_ID as TEAM_ID4_0_,
                member0_.username as username3_0_ 
            from
                Member member0_ 
            left outer join
                Team team1_ 
                    on member0_.TEAM_ID=team1_.id

     

    • 세타 조인: select count(m) from Member m, Team t where m.username = t.name (크로스 조인)

    JpaMain Class

     String setaJoinQuery = "select m from Member m, Team t where m.username = t.name";
     List<Member> result = em.createQuery(setaJoinQuery, Member.class).getResultList();

    console

    Hibernate: 
        /* select
            m 
        from
            Member m,
            Team t 
        where
            m.username = t.name */ select
                member0_.id as id1_0_,
                member0_.age as age2_0_,
                member0_.TEAM_ID as TEAM_ID4_0_,
                member0_.username as username3_0_ 
            from
                Member member0_ cross 
            join
                Team team1_ 
            where
                member0_.username=team1_.name

     

     

    조인 - ON 절

    • ON절을 활용한 조인(JPA 2.1부터 지원)

        • 1. 조인 대상 필터링

        • 2. 연관관계 없는 엔티티 외부 조인(하이버네이트 5.1부터)

     

     

     

    1. 조인 대상 필터링

    • 예) 회원과 팀을 조인하면서, 팀 이름이 A인 팀만 조인 (팀의 데이터를 줄인 다음에 뽑고싶어.)

    JPQL:

     SELECT m, t FROM Member m LEFT JOIN m.team t on t.name = 'A'

    JpaMain

    String query = "select m from Member m left join m.team t on t.name = 'teamA'";
    List<Member> result = em.createQuery(query, Member.class).getResultList();

    console

    Hibernate: 
        /* select
            m 
        from
            Member m 
        left join
            m.team t 
                on t.name = 'teamA' */ select
                    member0_.id as id1_0_,
                    member0_.age as age2_0_,
                    member0_.TEAM_ID as TEAM_ID4_0_,
                    member0_.username as username3_0_ 
            from
                Member member0_ 
            left outer join
                Team team1_ 
                    on member0_.TEAM_ID=team1_.id 
                    and (
                        team1_.name='teamA'
                    )

    조인 조건문 안에 team1_.name = 'teamA' 가 추가되는 것이다.

     

    SQL:

     SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.TEAM_ID=t.id and t.name='A'

     

     

     

    2. 연관관계 없는 엔티티 외부 조인

    • 예) 회원의 이름과 팀의 이름이 같은 대상 외부 조인

    JPQL: on절에다가 명시해주면 된다.

    SELECT m, t FROM Member m LEFT JOIN Team t on m.username = t.name

    JpaMain

    String query = "select m from Member m left join m.team t on t.name = 'teamA'";
    List<Member> result = em.createQuery(query, Member.class).getResultList();

    console

        /* select
            m 
        from
            Member m 
        left join
            Team t 
                on m.username = t.name */ select
                    member0_.id as id1_0_,
                    member0_.age as age2_0_,
                    member0_.TEAM_ID as TEAM_ID4_0_,
                    member0_.username as username3_0_ 
            from
                Member member0_ 
            left outer join
                Team team1_ 
                    on (
                        member0_.username=team1_.name
                    )

    id값이 없어지고 username과 name만 비교하게 된다.

     

    SQL:

    SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.username = t.name

     

     

     

    서브 쿼리 (쿼리가 있는데 그안에서 또 서브로 쿼리를 만드는 것)

    • 나이가 평균보다 많은 회원

    select m from Member m where m.age > (select avg(m2.age) from Member m2)

    main query와 sub query가 같은 Member를 쓰지 않고 따로 m2라고 정의 해서 사용하고 있다. 전혀 관계가 없다.

    서브 쿼리는 이렇게 짜야지 성능이 잘나온다.

     

    • 한 건이라도 주문한 고객

    select m from Member m where (select count(o) from Order o where m = o.member) > 0

    메인의 m을 서브쿼리로 들고왔는데 이건 성능이 않좋다.

     

     

     

    서브 쿼리 지원 함수

    • [NOT] EXISTS (subquery): 서브쿼리에 결과가 존재하면 참

        • {ALL | ANY | SOME} (subquery)

        • ALL 모두 만족하면 참

        • ANY, SOME: 같은 의미, 조건을 하나라도 만족하면 참

    • [NOT] IN (subquery): 서브쿼리의 결과 중 하나라도 같은 것이 있으면 참

     

     

     

     

    서브 쿼리

    - 예제

    • 팀A 소속인 회원 (exists)

    select m from Member m where exists (select t from m.team t where t.name = ‘팀A')

     

    • 전체 상품 각각의 재고보다 주문량이 많은 주문들(ALL) -모두 만족하면 참

    select o from Order o where o.orderAmount > ALL (select p.stockAmount from Product p)

     

    • 어떤 팀이든 팀에 소속된 회원(ANY) -조건을 하나라도 만족하면 참

    select m from Member m where m.team = ANY (select t from Team t)

    ANY( select t from Team t) 팀이 어느 팀에나 속해 있으면

     

     

    JPA 서브 쿼리 한계

    • JPA는 WHERE, HAVING 절에서만 서브 쿼리 사용 가능

     

    • SELECT 절도 가능(하이버네이트에서 지원) -JPA 표준 스팩에서는 지원하지 않는다.

     

    FROM 절의 서브 쿼리는 현재 JPQL에서 불가능

    이런게 불가능 ㅠㅠㅠㅠㅠㅠ 

      ->조인으로 풀 수 있으면 풀어서 해결 , 이래도 복잡하고 안되면 naitive로 넘기지만 native를 안쓰는 이유는?

        sql에서 view에 대한 로직이나 view가 원하는 값을 바꾸는 로직이있는 부분은

         JPA에서 말고 VIEW에서해결하면 서브쿼리가 줄긴한다.

     

     

     

    JPQL 타입 표현

    • 문자: ‘HELLO’, ‘She’’s’

    • 숫자: 10L(Long), 10D(Double), 10F(Float)

    • Boolean: TRUE, FALSE

    ENUM: jpabook.MemberType.Admin (패키지명 포함) - 조심해야함.

    MemberType enum

    package jpql;
    
    public enum MemberType {
        ADMIN, USER
    
    }
    

    Member Class에 추가

        @Enumerated(EnumType.STRING)
        private MemberType type;

     

    JpaMain

    'ENUM 쓰기전  'HELLO'와 TRUE도 들어갈 수 있다는 것을 보여주기위한 코드

    console

    Hibernate: 
        /* select
            m.username,
            'HELLO',
            TRUE 
        From
            Member m */ select
                member0_.username as col_0_0_,
                'HELLO' as col_1_0_,
                1 as col_2_0_ 
            from
                Member member0_
    objects[0] = member1
    objects[0] = HELLO
    objects[0] = true

     

    ENUM 사용방법 1) 패키지명을 다 넣어야함.

    JpaMain

    console

    Hibernate: 
        /* select
            m.username,
            'HELLO',
            TRUE 
        from
            Member m 
        where
            m.type = jpql.MemberType.ADMIN */ select
                member0_.username as col_0_0_,
                'HELLO' as col_1_0_,
                1 as col_2_0_ 
            from
                Member member0_ 
            where
                member0_.type='ADMIN'

     

    ENUM 사용방법 2)

    JpaMain

    console

    Hibernate: 
        /* select
            m.username,
            'HELLO',
            TRUE 
        from
            Member m 
        where
            m.type = :userType */ select
                member0_.username as col_0_0_,
                'HELLO' as col_1_0_,
                1 as col_2_0_ 
            from
                Member member0_ 
            where
                member0_.type=?

    • 엔티티 타입: TYPE(m) = Member (상속 관계에서 사용)

    -book관련된 것만 조회하고싶어

    JpaMain

    console

    JPQL 기타

    • SQL과 문법이 같은 식

    • EXISTS, IN

    • AND, OR, NOT

    • =, >, >=, <, <=, <> • BETWEEN, LIKE, IS NULL

     

    조건식 - CASE

    기본 CASE 식

    JpaMain

    String query =
                        "select " +
                                "case when m.age <= 10 then '학생요금' " +
                                "     when m.age >= 60 then '경로요금' " +
                                "     else '일반요금' " +
                                "end" +
                        " from Member m";
    
                List<String> result = em.createQuery(query, String.class).getResultList();
                for (String s : result) {
                    System.out.println("s = " + s);
                }

    console

    Hibernate: 
        /* select
            case 
                when m.age <= 10 then '학생요금'      
                when m.age >= 60 then '경로요금'      
                else '일반요금' 
            end 
        from
            Member m */ select
                case 
                    when member0_.age<=10 then '학생요금' 
                    when member0_.age>=60 then '경로요금' 
                    else '일반요금' 
                end as col_0_0_ 
            from
                Member member0_
    s = 학생요금

     

    단순 CASE 식

     

     

    조건식 - CASE 식

    • COALESCE(컬레스): 하나씩 조회해서 null이 아니면 반환

     

    • NULLIF: 두 값이 같으면 null 반환, 다르면 첫번째 값 반환

     

    사용자 이름이 없으면 이름 없는 회원을 반환

    JpaMain -coalesce

                //조건식 case 식
                String query = "select coalesce(m.username,'이름 없는 회원') from Member m ";
                //username이 없으면 이름없는 회원
                
                List<String> resultList = em.createQuery(query, String.class).getResultList();
                for (String s : resultList) {
                    System.out.println("s = " + s);
                }

    console -coalesce

    Hibernate: 
        /* select
            coalesce(m.username,
            '이름 없는 회원') 
        from
            Member m  */ select
                coalesce(member0_.username,
                '이름 없는 회원') as col_0_0_ 
            from
                Member member0_
    s = 이름 없는 회원

     

    사용자 이름이 ‘관리자’면 null을 반환하고 나머지는 본인의 이름을 반환

    JpaMain -nullif

                //조건식 case 식
                String query = "select nullif(m.username,'관리자') from Member m ";
                
                //username이 없으면 이름없는 회원
                List<String> resultList = em.createQuery(query, String.class).getResultList();
                
                for (String s : resultList) {
                    System.out.println("s = " + s);
                }

    console -nullif

    Hibernate: 
        /* select
            nullif(m.username,
            '관리자') 
        from
            Member m  */ select
                nullif(member0_.username,
                '관리자') as col_0_0_ 
            from
                Member member0_
    s = null

     

     

    JPQL 기본 함수(표준 함수)

    기본 적으로 hibernate구현체를 쓰면

    ctrl alt shift n

     

    DB가 제공하는 함수가 다 등록되어 있는 것을 확인 할 수 있다.

     

    • CONCAT

    JpaMain

    concat

    console -문자가 합쳐진것을 확인할 수도 있음.

    Hibernate: 
        /* select
            'a' || 'b' 
        from
            Member m  */ select
                ('a'||'b') as col_0_0_ 
            from
                Member member0_
    s = ab

     

    • SUBSTRING

    • TRIM

    • LOWER, UPPER

    • LENGTH

    • LOCATE

    • ABS, SQRT, MOD

    • SIZE, INDEX(JPA 용도)

     

     

    사용자 정의 함수 호출 - JPQL 기본 함수(표준 함수)가 사용이 안될 경우

    사용이 안되는 이유 : DB에서는 표준함수를 알순있어도 JPA에서는 모를 수도 있다.

    여기에도 없을때 만드는것!

     

    • 하이버네이트는 사용전 방언에 추가해야 한다.

     

    • 사용하는 DB 방언을 상속받고, 사용자 정의 함수를 등록한다.

    ex) group_concat이라는 함수가 있다고 가정하고 만드는 예제

    dialect 디렉토리 하위에 사용자정의 Dialect class 생성

    MyH2Dialect class

    -내가 사용하는 Dialect를 상속받는다.

    -Function 정의하는 것은 아래의 H2Dialect Class를 찾아가보면 기존 함수는 어떻게 생성하였는지 참고해서 만들면 된다.

     

    H2Dialect Class

    H2Dialect

    persistence.xml에서 hibernate.dialect를 내가 사용할 dialect로 변경해준다.

    실제 사용

    그냥 function으로 사용하는 방법

     

    직관적이게 사용하는 방버

    표준 문법은 function이라고하고 group_concat을 해주면된다.

     

    console 

    Hibernate: 
        /* select
            function('group_concat',
            m.username) 
        From
            Member m */ select
                group_concat(member0_.username) as col_0_0_ 
            from
                Member member0_
    s = 관리자1,관리자2

    실제는 두줄로 나오는데 결과가 한줄로 나오는 함수를 만든것.

    'JPA' 카테고리의 다른 글

    #JPA-21 JPQL - 페치 조인(fetch join)  (0) 2021.02.23
    #JPA-20 JPQL- 경로 표현식  (0) 2021.02.23
    #JPA-18 JPQL(Java Persistence Query Language)  (0) 2021.02.23
    #JPA-17 JPA가 지원하는 다양한 쿼리 방법  (0) 2021.02.23
    #JPA-16 값 타입  (0) 2021.02.22

    댓글

Designed by Tistory.