10.2.8 경로 표현식

경로 표현식이라는 것은 쉽게 이야기해서 .을 찍어 객체 그래프를 탐색하는 것이다.

 

1
2
3
4
5
select m.username
from Member m
    join m.team t
    join m.orders o
where t.name ='팀A'
cs

경로 표현식의 용어 정리

  • 상태 필드: 단순한 값을 저장하기 위한 필드(필드 or 프로퍼티)
  • 연관 필드: 연관관계를 위한 필드, 임베디드 타입 포함 
    • 단일 값 연관 필드 : @ManyToOne, @OneToOne,대상이 엔티티
    • 컬렉션 값 연관 필드: @OneToMany,@ManyToMany,대상이 컬렉션

상태 필드는 단순히 값을 저장하는 필드고 연관 필드는 객체 사이의 연관관계를 맺기 위해 사용하는 필드다. 

 

경로 표현식과 특징

JPQL에서 경로 표현식을 사용해서 경로 탐색을 하려면 다음 3가지 경로에 따라 어떤 특징이 있는지 이해해야 한다.

 

  • 상태 필드 경로: 경로탐색의 끝이다. 더는 탐색x
  • 단일 값 연관 경로: 묵시적으로 내부 조인이 일어난다. 단일 값 연관경로는 계속 탐색할 수 있다.
  • 컬렉션 값 연관경로: 묵시적으로 내부 조인이 일어난다. 더는 탐색X. 단 FROM절에서 조인을 통해 별칭을 얻으면 별칭으로 탐색할 수 있다.

상태필드 경로탐색

select m.username,m.age from Member m

이 JPQL실행시 SQL은

select m.name, m.age

from Member m

 

상태 필드 경로 탐색은 이해하는 데 어려움이 없을 것이다.

 

단일 값 연관 경로 탐색

 

select o.member from Order o

JPQL 실행시 SQL

select m * from Orders o inner join Member m on o.member_id=m.id

 

JPQL을 보면 o.member를 통해 주문에서 회원으로 단일 값 연관 필드로 경로 탐색을 했다.

단일 값 연관 필드로 경로 탐색을 하면 SQL에서 내부 조인이 일어나는데 이것을 묵시적 조인이라 한다. 참고로 묵시적 조인은 모두 내부 조인이다. 외부 조인은 명시적으로 JOIN키워드를 사용해야 한다.

 

명시적 조인:JOIN을 직접 적어주는 것

SELECT m FROM Member m JOIN m.team t

 

복잡한 JPQL 분석

select o.member.team

from Order o

where o.product.name='productA' and o.address.city='JINJU'

 

주문 중에서 상품명이 'productA'고 배송지가 'JINJU'인 회원이 소속된 팀을 조회한다. 실제 내부 조인은 몇번일어날까?

1
2
3
4
5
6
7
select t*
from Order o
inner join Member m on o.member_id=m.id
inner join Team t on o.team_id=t.id
inner join Product p on o.product_id=p.id
where p.name='productA' and o.city='JINJU'
 
cs

예제 10.38의 실행된 SQL을 보면 총 3번의 조인이 발생했다. 참고로 o.address처럼 임베디드 타입에 접근하는 것도 단일 값 연관 경로 탐색이지만 주문 테이블에 이미 포함되어 있으므로 조인이 발생하지 않는다.

 

컬렉션 값 연관 경로 탐색

JPQL을 다루면서 많이 하는 실수 중 하나는 컬렉션 값에서 경로 탐색을 시도하는 것이다.

select t.members from Team t//성공

selecet t.members.username from Team t// 실패

 

t.members 처럼 컬렉션까지는 경로 탐색이 가능하다. 하지만 t.members.username처럼 컬렉션에서 경로 탐색을 시작하는 것은 허락하지 않는다. 만약 컬렉션에서 경로 탐색을 하고 싶으면 다음 코드처럼 조인을 사용해서 새로운 별칭을 획득해야 한다.

select m.username from Team t join t.members m

 

join t.members m으로 컬렉션에 새로운 별칭을 얻었다. 이제 별칭 m부터 다시 경로 탐색을 할 수 있다.

참고로 컬렉션은 컬렉션의 크기를 구할 수 있는 size라는 특별한 기능을 사용할 수 있다.

size를 사용하면 COUNT함수를 사용하는 SQL로 적절히 변환된다.

 

select t.members.size from Team t

 

경로 탐색을 사용한 묵시적 조인 시 주의사항

경로 탐색을 사용하면 묵시적 조인이 발생해서 SQL에서 내부 조인이 일어날 수 있다. 이때 주의사항은 다음과 같다.

 

  • 항상 내부 조인이다.
  • 컬렉션은 경로 탐색의 끝이다. 컬렉션에서 경로 탐색을 하려면 명시적으로 조인해서 별칭을 얻어야 한다.
  • 경로 탐색은 주로 SELECT,WHERE 절에서 사용하지만 묵시적 조인으로 인해 SQL의 FROM절에 영향을 준다.

조인의 성능상 차지하는 부분은 아주 크다. 묵시적 조인은 조인이 일어나는 상황을 한눈에 파악하기 어렵다는 단점이 있다. 따라서 단순하고 성능에 이슈가 없으면 크게 문제가 안되지만 성능이 중요하면 분석하기 쉽도록 묵시적 조인보다는 명시적 조인을 사용하자.

 

10.2.9 서브 쿼리

JPQL도 SQL처럼 서브 쿼리를 지원한다. 여기에는 몇 가지 제약이 있는데, 서브쿼리를 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

 

참고로 이 쿼리는 다음처럼 컬렉션 값 연관 필드의 size 기능을 사용해도 같은 결과를 얻을 수 있다.

 

select m from Member m

where m.orders.size> 0

 

서브 쿼리 함수 

서브쿼리는 다음 함수들과 같이 사용할 수 있다. 

 

  • [NOT] EXISTS (subquery)
  • {ALL | ANY | SOME } (subquery)
  • [NOT] IN (subquery)

EXISTS

문법: [NOT] EXISTS (subquery)

설명: 서브쿼리에 결과가 존재하면 참이다. NOT은 반대

예 팀A 소속인 회원

 

select m from Member m

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

 

{ALL | ANY | SOME }

문법: {ALL | ANY | SOME } (subquery)

설명: 비교 연산자와 같이 사용한다. {= | >| >= | < |<= |<>}

-ALL:조건을 모두 만족하면 참이다. 

-ANY 혹은 SOME: 둘은 같은 의미다. 조건을 하나라도 만족하면 참이다. 

 

ex)전체 상품 각각의 재고보다 주문량이 많은 주문들

select o from Order o

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

 

ex)어떤 팀이든 팀에 소속된 회원

select m from Member m

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

 

IN

문법: [NOT] IN (subquery)

설명: 서브쿼리의 결과 중 하나라도 같은 것이 있으면 참이다. 참고로 IN은 서브쿼리가 아닌곳도 사용.

 

ex)20세 이상을 보유한 팀

select t from Team t

where t IN (select t2 From Team t2 JOIN t2.members m2 where m2.age >=20)

 

10.2.10 조건식

생략

 

10.2.11 다형성 쿼리

JPQL로 부모 엔티티를 조회하면 그 자식 엔티티도 함께 조회한다. 

1
2
3
4
5
6
7
8
9
10
11
12
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {...}
 
@Entity 
@DiscriminatorValue("B")
public class Book extends Item{
    ...
    private String author;
}
//Album,Movie 생략
cs

 

다음과 같이 조회하면 Item의 자식도 함께 조회한다.

List resultList= 

            em.createQuery("select i from Item i").getResultList();

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
단일 테이블 전략 사용시 실행 SQL
//SQL
SELECT * FROM ITEM
 
조인 전략 사용시 
//SQL
SELECT 
    i.ITEM_ID,i.DTYPE , i.name, i.price , i.stockQuantity,
    b.author , b.isbn,
    a.artist, a.etc,
    m.actor,m.director
FROM
    Item i
left outer join
    Book b on i.ITEM_ID=b.ITEM_ID
left outer join
    Album a on i.ITEM_ID=a.ITEM_ID
left outer join
    Movie m on i.ITEM_ID=m.ITEM_ID
cs

 

TYPE

TYPE은 엔티티의 상속 구조에서 조회 대상을 특정 자식 타입으로 한정할 때 주로 사용한다.

예 Item중에 Book,Movie를 조회하라

//JPQL

select i from Item i

where type(i) IN (Book,Movie)

//SQL

SELECT i FROM Item i

WHERE i.DTYPE in ('B','M')

 

 

TREAT(JPA2.1)

TREAT는 JPA 2.1에 추가된 기능.

JPA표준은 FROM,WHERE절에서 사용할수 있지만,하이버네이트는 SELECT 절에서도 TREATE를 사용할 수 있다.

 

부모인 Item 과 자식 Book이 있다.

 

//JPQL

select i from Item i where treat(i as Book).author = 'kim'

 

//SQL

select i.* from Item i

where

    i.DTYPE='B'

    and i.author='kim'

 

JPQL을 보면 treat를 사용해서 부모 타입인 Item을 자식 타입인 Book으로 다룬다. 따라서 author필드에 접근할 수 있다.

 

10.2.12 사용자 정의 함수 호출(JPA2.1)

문법:

function_invocation::=FUNCTION(function_name {, function_arg}*)

 

ex)

select function('group_concat',i.name) from Item i

 

하이버네이트 구현체를 사용하면 다음코드와 같이 방언 클래스를 상속해서 구현하고 사용할 db함수를 미리 등록해야함.

 

1
2
3
4
5
6
7
8
9
10
11
public class MyH2Dialect extends H2Dialect{
    public MyH2Dialect(){
        registerFunction("group_concat",new StandardSQLFunction
                ("group_concat", StandardBasicTypes.STRING));
    }
}
//hibernate.dialect에 해당 방언 등록.
<property name="hibernate.dialect" value="hello.MyH2Dialect"/>
 
하이버네이트 구현체 사용 시 다음과 같이 축약 사용가능
selet group_concat(i.namefrom Item i
cs

10.2.14 엔티티 직접 사용

기본 키 값

객체 인스턴스는 참조 값으로 식별하고 테이블 로우는 기본 키 값으로 식별한다. 

따라서 JPQL에서 엔티티 객체를 직접 사용하면 SQL에서는 해당 엔티티의 기본 키 값을 사용한다.

다음 JPQL예제를 보자.

 

select count(m.id) from Member m//엔티티의 아이디를 사용

select count(m) from Member m //엔티티를 직접 사용

 

두번째의 count(m)을 보면 엔티티의 별칭을 직접 넘겨줌. 이렇게 엔티티 직접 사용 시 JPQL이 SQL로 변환될 때

해당 엔티티의 기본 키를 사용한다. 따라서 다음 실제 실행된 SQL은 둘 다 같다.

 

select count(m.id) as cnt

from Member m

 

JPQL의 count(m)이 SQL에서 count(m.id)로 변환된 것을 확인 가능.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
String qlString="select m from Member m where m =:member";
List resultList = em.createQuery(qlString)
                    .setParameter("member",member)
                    .getResultList();
 
실행된 SQL
select m.*
from Member m 
where m.id=?
 
String qlString="select m from Member m where m.id :=memberId";
List resultList = em.createQuery(qlString)
        .setParameter("memberId",4L)
        .getResultList();
cs

외래 키 값

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
Team team=em.find(Team.class,1L);
 
String qlString = "select m from Member m where m.team =:team";
List resultList = em.createQuery(qlString)
            .setParameter("team",team)
            .getResultList();
 
select m.*
from Member m 
where m.team_id=?
String qlString = "select m from Member m where m.team.id =:teamId";
List resultList = em.createQuery(qlString)
        .setParameter("teamId",1L)
        .getResultList();
cs

예제에서 m.team.id를  를 보면 Member와 Team간에 묵시적 조인이 일어날 것 같지만

MEMBER테이블이 team_id 외래 키를 가지고 있으므로 묵시적 조인 X. 물론 m.team.name을  을 호출하면 묵시적 조인이 일어남. 따라서 m.team을 사용하든 m.team.id를  를 사용하든 생성되는 SQL은 같다.

 

10.4 QueryDSL

JPA Criteria는 너무 복잡하고 어렵다. 쉽고 간결하며 쿼리와 비슷하게 개발할 수 있는 QueryDSL을 배워보자.

 

필요 라이브러리 

porm.xml에 추가

1
2
3
4
5
6
7
8
9
10
11
12
<!-- QueryDSL-->
        <dependency>
            <groupId>com.mysema.querydsl</groupId>
            <artifactId>querydsl-jpa</artifactId>
            <version>3.6.3</version>
        </dependency>
        <dependency>
            <groupId>com.mysema.querydsl</groupId>
            <artifactId>querydsl-apt</artifactId>
            <version>3.6.3</version>
            <scope>provided</scope>
        </dependency>
cs

querydsl-jpa : QueryDSL JPA 라이브러리

querydsl-apt: 쿼리 타입을 생성할 때 필요한 라이브러리

 

환경설정

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  <plugin>
                <groupId>com.mysema.maven</groupId>
                <artifactId>apt-maven-plugin</artifactId>
                <version>1.1.3</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>process</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>
                                target/generated-sources/java
                            </outputDirectory>
                            <processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
                        </configuration>
                    </execution>
                </executions>
 
            </plugin>
cs

콘솔에서 mvn compile을 입력하면 outputDirectory에 지정한 target/generated-sources위치에 QMember.java처럼 Q로 시작하는 쿼리 타입들이 생성된다.

이제 taget/generated-sources를 소스 경로에 추가하면 된다.

 

10.4.2 시작

QueryDSL을 어떻게 사용하는지 알아보자.

1
2
3
4
5
6
7
8
9
10
public void queryDSL(){
    EntityManager em=emf.createEntityManager();
    JPAQuery query=new JPAQuery(em);
    QMember qMember= new QMember("m"); //생성되는 JPQL의 별칭이 m
    List<Member> members=
            query.from(qMember)
            .where(qMember.name.eq("회원1"))
            .orderBy(qMember.name.desc())
            .list(qMember);
}
cs

QueryDSL을 사용하려면 우선 com.mysema.query.jpa.impl.JPAQuery 객체를 생성해야하는데 이때 엔티티 매니저를 생성자에 넘겨준다. 다음으로 사용할 쿼리 타입(Q)을 생성하는데 생성자에게 별칭을 주면 된다.

이 별칭을 JPQL에서 별칭으로 사용하낟. 

 그 다음에 나오는 from,where,orderBy,list 직관적으로 이해.

실행되는 JPQL을 보자

select m from Member m

where m.name =?1

order by m.name desc

 

기본 Q 생성

쿼리 타입(Q)은 사용하기 편리하도록 예제 10.80과 같이 기본 인스턴스를 보관하고 있다. 하지만 같은 엔티티를 조인하거나 같은 엔티티를 서브쿼리에 사용하면 같은 별칭이 사용되므로 이때는 별칭을 직접 지정해서 사용해야 한다.

 

10.4.3 검색 조건 쿼리

QueryDSL의 기본 쿼리 기능을 알아보자.

JPAQuery query=new JPAQUery(em);

QItem item=QItem.item;

List<Item> list=query.from(item)

       .where(item.name.eq("좋은상품").and(item.price.get(20000)))

       .list(item); //조회할 프로젝션 지정

 

위 코드 실행 시 JPQL이 생성되고 ,실행된다.

select item 

from Item item

where item.name = ?1 and item.price > ?2

 

QueryDSL의 where절에는 and나 or을 사용할 수 있다. 여러 검색 조건을 사용해도 된다.

item.price.between(10000,20000);

item.name.contains("상품1");

item.name.startsWith("고급");

 

10.4.4 결과 조회

쿼리 작성이 끝나고 결과 조회 메소드를 호출하면 실제 db를 조회한다.

보통 uniqueReslut() 나 list()를 사용하고 파라미터로 프로젝션 대상을 넘겨준다. 결과 조회 API는 com.mysema.query.Projectable에 정의되어 있다.

대표적인 결과 조회 메소드는 다음과 같다. 

  • uniqueResult() : 조회 결과가 한 건일 때 사용한다. 조회 결과가 없으면 null을 반환하고 결과가 하나 이상이면 com.mysema.query.NonUniqueResultException예외가 발생한다. 
  • singleResult():uniqueResult()와 같지만 결과가 하나 이상이면 처음 데이터반환.
  • list():결과가 하나 이상일 때 사용한다. 결과 없으면 빈 컬렉션을 반환한다.

10.4.5 페이징과 정렬

1
2
3
4
5
6
7
8
QItem item= QItem.item;
 
 
query.from(item)
    .where(item.price.gt(20000))
    .orderBy(item.price.desc(), item.stockQuantity.asc())
    .offset(10).limit(20)
    .list(item);
cs

정렬은 orderBy이 사용하는데 쿼리 타입이 제공하는 asc(),desc()를 사용한다.

페이징은 offset과 limit를 적절히 조합해서 사용함.

 페이징은 restrict() 메소드에 com.mysema.query.QueryModifiers를 파라미터로 사용해도 된다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
QueryModifiers queryModifiers = new QueryModifiers(20L,10L); //limit,offset
List<Item> list =
        qeury.from(items)
        .restrict(queryModifiers)
        .list(item);
실제 페이징 처리를 하려면 검색된 전체 데이터 수 알아야 함. 이떄는 list() 대신
listResults() 사용
SearchResults<Item> result=
    query.from(item)
        .where(item.price.gt(10000))
        .offset(10).limit(20)
        .listResults(item);
 
long total =result.getTotal();
long limit = result.getLimit();
long offset =result.getOffset();
List<Item> results=result.getResults(); //조회된 데이터
cs

listResults()를 사용하면 전체 데이터 조회를 위한 count쿼리를 한 번 더 실행한다.

그리고 SearchResults를 반환하는데 이 객체에서 전체 데이터 수를 조회할 수 있다.

 

10.4.6 그룹

그룹은 다음과 같이 groupBy를 사용하고 그룹화된 결과를 제한하려면 having을 사용

1
2
3
4
query.from(item)
    .groupBy(item.price)
    .having(item.price.gt(10000))
    .list(item);
cs

 

10.4.7 조인

조인은 InnerJoin(join),leftJoin,rightJoin,fullJoin을 사용할 수 있고

추가로 JPQL의 on과 성능 최적화를 위한 fetch 조인도 사용할 수 있다.

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

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 
//기본 조인
QOrder order= QOrder.order;
QMember member = QMember.member;
QOrderItem orderItem = QOrderItem.orderItem;
 
query.from(order)
    .join(order.member,member)
    .leftJoin(order.orderItems,orderItem)
    .list(order);
 
//on 사용 join
query.from(order)
    .leftJoin(order.orderItems,orderItem)
    .on(orderItem.count.gt(2))
    .list(order);
 
//fetch 조인 사용
query.from(order)
    .innerJoin(order.member,member).fetch()
    .leftJoin(order.orderItems,orderItem).fetch()
    .list(order);
 
//세타 조인 방법
QOrder order = QOrder.order;
QMember member = QMember.member;
 
query.from(order,member)
    .where(order.member.eq(member))
    .list(order);
 
cs

10.4.8 서브 쿼리

서브 쿼리는 JPASubQuery를 생성해서 사용한다. 서브 쿼리 결과가 하나면 unique(), 여러 건이면 list()를 사용할수있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
QItem item=QItem.item;
QItem itemSub = new QItem("itemSub");
 
query.from(item)
    .where(item.price.eq(
            new JPASubQuery().from(itemSub).unique(itemSub.price.max())
        ))
    .list(item);
 
query.from(item)
    .where(item.in(
            new JPASubQuery().from(itemSub)
            .where(item.name.eq(itemSub.name))
            .list(itemSub)
        ))
    .list(item);
cs

 

10.4.9 프로젝션과 결과 반환

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

 

프로젝션 대상이 하나

list(item.name);

 

여러 컬럼반환과 튜플

List<Tuple> result=query.from(item).list(item.name,item.price);
//List<Tuple> result=query.from(item).list(new QTuple(item.name,item.price));
//같다.

 

빈 생성

쿼리 결과를 엔티티가 아닌 특정 객체로 받고 싶으면 빈 생성 기능을 사용.

  • 프로퍼티 접근
  • 필드 직접 접근
  • 생성자 사용

 원하는 방법 지정하기위해 Projections를 사용.

List<ItemDTO> result=query.from(item).list(Projections.bean(ItemDTO.class, item.name.as("username"),item.price));

 

쿼리 결과는 name인데 ItemDTO는 username 프로퍼티를 가지고 있따. 이처럼 쿼리 결과와 매핑할 프로퍼티 이름이 다르면 as를 사용해서 별칭을 주면 된다. 프로퍼티 접근은 Setter를 사용.

 

필드 직접 접근

List<ItemDTO> result= query.from(item).list(

                    Projections.fields(ItemDTO.class,item.name.as("username"),item.price));

Projections.fields() 메소드를 사용하면 필드에 직접 접근해서 값을 채워준다. 필드를 private로 설정해도 동작한다.

 

생성자 사용

List<ItemDTO> result =query.from(item).list(

            Projections.constructor(ItemDTO.class,item.name,item.price)

);

프로젝션과 파라미터 순서가 같은 생성자가 필요하다.

 

DISTINCT

query.distinct().from(item) ...

 

10.4.10 수정,삭제 배치 쿼리

QueryDSL도 수정,삭제 같은 배치 쿼리를 지원. JPQL 배치 쿼리처럼 영속성 컨텍스트를 무시하고 db를 직접 쿼리함.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
//수정
QItem item=QItem.item;
JPAUpdateClause updateClause = new JPAUpdateClause(em,item);
long count=updateClause.where(item.name.eq("시골책"))
    .set(item.price,item.price.add(100))
    .execute();
//삭제
QItem item=QItem.item;
JPADeleteClause deleteClause = new JPADeleteClause(em,item);
long count=deleteClause.where(item.name.eq("시골책"))
    .execute();
 
 
cs

 

10.4.11 동적 쿼리

BooleanBuilder사용하면 특정 조건에 따른 동적 쿼리를 편리하게 생성 가능.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
SearchParam param=new SearchParam();
param.setName("서울개발자");
param.setPrice(10000);
 
QItem item =QItem.item;
 
BooleanBuilder builder=new BooleanBuilder();
if(StringUtils.hasText(param.getName())){
    builder.and(item.name.contains(param.getName()));    
}
if(param.getPrice() !=null){
    builder.and(item.name.contains(param.getPrce()));    
}
List<Item> result =query.from(item)
    .where(builder)
    .list(item);
cs

 

10.4.12 메소드 위임

메소드 위임 기능을 사용하면 쿼리 타입에 검색 조건을 직접 정의가능.

1
2
3
4
5
6
7
8
public class ItemExpression {
    @QueryDelegate(Item.class)
    public static BooleanExpression isExpensive(QItem item, Integer price) {
        return item.price.gt(price);
    }
}    
 
 
cs

정적 메소드를 만들고 @QueryDelegate어노테이션 속성으로 이 기능 적용할 엔티티 지정함.

정적 메소드의 첫번째 파라미터에는 대상 엔티티의 쿼리타입(Q)을 지정하고 나머지는 필요한 파라미터 정의.

 

query.from(item).where(item.isExpensive(30000)).list(item);

필요하다면 String,Date같은 자바 기본 내장 타입에도 메소드 위임 기능을 사용할 수 있다.

 

10.4.13 QueryDSL 정리

1. 문자가 아닌 코드로 안전하게 쿼리 작성

2. 복잡한 동적 쿼리를 어떻게 해결해야 하는가

QueryDSL은 두가지를 모두 만족하면서 쉽고 단순하다.

 

'자바 ORM 표준 JPA 프로그래밍' 카테고리의 다른 글

실전! 스프링 부트와 JPA 활용 jpashop 완성  (0) 2021.05.12
엔티티 설계 주의점  (0) 2021.05.10
객체지향 쿼리언어  (0) 2021.05.05
8장 프록시와 연관관계 관리  (0) 2021.05.05
7장  (0) 2021.05.04

+ Recent posts