JPA - @NamedQuery , 정적 쿼리

2019. 2. 9. 14:59Web/JPA

JPA - @NamedQuery , 정적 쿼리



JPA는 크게 동적쿼리와 정적 쿼리로 나뉜다. 그 중에 정적쿼리(@NamedQuery)는 애플리케이션 로딩 시점에 JPQL 문법을 체크하고 미리 파싱해둔다. 따라서 오류를 빨리 확인할 수 있고, 사용하는 시점에는 파싱된 결과를 재사용하므로 성능상 이점도 있다. 그리고 정적쿼리는 변하지 않는 정적 SQL이 생성되므로 데이터베이스의 조회 성능상 최적화에 도움이된다. 왜냐하면 데이터베이스는 내부적으로 한번 사용된 쿼리는 캐싱? 해놓기 때문에 다음에 동일한 쿼리가 있을 경우 재사용하기 때문이다.




@NamedQuery



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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
@Entity
@Table(name = "MEMBER_JPQL")
@Getter
@Setter
@ToString
@NamedQuery(
        name="MemberJPQL.findByName",
        query="select m from MemberJPQL m where m.username = :username"
)
public class MemberJPQL {
    
    @Id
    @Column(name = "MEMBER_JPQL_ID")
    @GeneratedValue(strategy=GenerationType.TABLE, generator = "MEMBER_JPQL_SEQ_GENERATOR")
    @TableGenerator(
            name="MEMBER_JPQL_SEQ_GENERATOR",
            table="MY_SEQUENCE",
            pkColumnName="SEQ_NAME"//MY_SEQUENCE 테이블에 생성할 필드이름(시퀀스네임)
            pkColumnValue="MEMBER_JPQL_SEQ"//SEQ_NAME이라고 지은 칼럼명에 들어가는 값.(키로 사용할 값)
            allocationSize=1
    )
    private Long id;
    
    @Column(name = "USERNAME")
    private String username;
    
    @Column(name = "USERAGE")
    private int age;
    
    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private TeamJPQL team;
    
    @OneToMany(mappedBy = "member")
    private List<OrderJPQL> orders = new ArrayList<OrderJPQL>();
}
 
 
 
/*
 * 정적쿼리 @NamedQuery
 * 애플리케이션 로딩 시점에 JPQL 문법을 체크하고 미리 파싱해둔다. 따라서 오류를 빠르게 확인할 수 있고,
 * 사용하는 시점에는 미리 파싱된 결과를 재사용하므로 성능상 이점이 있다. 그리고 Named쿼리는 변하지 않는
 * 정적쿼리가 생성되므로 데이터베이스 조회 성능 최적화에 크게 도움된다.
 */
    public void namedQuery() {
        List<MemberJPQL> member = em.createNamedQuery("MemberJPQL.findByName",MemberJPQL.class)
                              .setParameter("username""여성게1")
                              .getResultList();
        
        System.out.println("================namedQuery=================");
        
        for(MemberJPQL m : member) {
            System.out.println(m.toString());
        
        }
        System.out.println("================namedQuery=================");
        
    }
}
cs




위 소스코드에도 보이지만 정적쿼리 네이밍규칙?은 아니지만 엔티티명.정적쿼리명으로 네이밍규칙을 지키는 것이 좋다. 엔티티마다 같은 필드명이 존재할 수도 있으니 유니크한 이름을 위해 엔티티명을 필히 붙여주는 것이 좋다. 그리고 파라미터 바인딩 변수도 엔티티의 필드명과 동일하게 하는 것이 사용자로 하여금 편리함을 줄 수 있다.


위와 같이 어노테이션으로 정적쿼리를 작성할 수도 있지만, XML파일로 정적쿼리를 한번에 다 몰아서 작성할 수 있다. 두가지는 각각 장단점이 존재한다. XML은 복잡하지만 자바언어에서 여러줄의 스트링을 다루기 힘든 반면에 xml은 여러줄의 로우를 다루기 편하고, 한곳에 모든 정적쿼리를 모아담을 수 있다. 하지만 정적쿼리가 많지 않다면 필자는 어노테이션으로 정의해도 크게 문제 없다 생각한다.


마지막으로 한 엔티티에 여러 정적쿼리를 어노테이션으로 다루고 싶다면,


@NamedQueries({

@NamedQuery....

,@NamedQuery....

,@NamedQuery....

})


처럼 여러개 작성이 가능하다.