자바 ORM 표준 JPA 프로그래밍 공부 기록
목차
객체지향 쿼리 언어
테이블을 대상으로 쿼리하는 것이 아니라 엔티티 객체를 대상으로 쿼리함
SQL을 추상화해서 특정데이터베이스 SQL에 의존하지 않는다
작성한 JPQL을 실행하려면 쿼리 객체를 만들어야 한다.
쿼리 객체는 TypeQuery와 Query가 있다
TypedQuery<Member> query = em.createQuery("select m from Member m", Member.class);
Query query = em.createQuery("select m.userName, m.age from Member m");
query.getResultList
결과가 하나 이상일 때, 리스트 반환.
결과가 없으면 빈 컬렉션 반환
query.getResultList
결과가 정확히 하나, 단일 객체 반환
예외 발생
javax.persistence.NoResultException
javax.persistence.NonUniqueResultException
JDBC는 위치 기준 파라미터 바인딩만 지원하지만 JPQL은 이름 기준 파라미터 바인딩도 지원한다.
:
를 사용한다String usernaeParam = "User1";
TypedQuery<Member> query = em.createQuery("SELECT m FROM Member m where m.username=:username", Member.class);
query.setParameter("username", usernameParam);
?
다음에 위치 값을 주면 된다String usernaeParam = "User1";
TypedQuery<Member> query = em.createQuery("SELECT m FROM Member m where m.username=?1", Member.class);
query.setParameter(1, usernameParam);
위치 기준 파라미터 방식 보다는 이름 기준 파라미터 바인딩 방식
을 사용하는게 더 명확하다.
SQL 인젝션 공격이나 전체적인 성능 향상을 위해 파라미터 바인딩 방식은 선택이 아닌 필수이다!
SELECT 절에 조회할 대상을 지정하는 것을 프로젝션
이라고 한다.
프로젝션 대상 : 엔티티, 임베디드 타입, 스칼라 타이
엔티티 프로젝션
em.createQuery("select m from Member m", Member.class).getResultList();
엔티티 프로젝션에서 조회한 엔티티는 영속성 컨텍스트에서 다 관리된다.
임베디드 타입 프로젝션
em.createQuery("select o.address from Order o", Address.class).getResultList();
임베디드 타입은 엔티티 타입이 아닌 값 타입이다. 따라서 임베디드 타입은 영속성 컨텍스트에서 관리되지 않는다.
스칼라 타입 프로젝션
문자, 숫자, 날짜와 같은 기본 데이터 타입을 스칼라 타입이라 한다.
public class MemberDTO {
private String userName;
private int age;
public MemberDTO(String userName, int age) {
this.userName = userName;
this.age = age;
}
}
List<MemberDTO> list = em.createQuery("select new jpql.MemberDTO(m.userName, m.age) from Member m", MemberDTO.class).getResultList();
select
다음에 new
명령어를 사용하면 반환받을 클래스를 지정할 수 있는데 이 클래스의 생성자에 JPQL 조회 결과를 넘겨줄 수 있다. 그리고 new
명령어를 사용한 클래스로 TypeQuery
를 사용할 수 있어 객체 변환 작업을 줄일 수 있다.
• setFirstResult(int startPosition)
: 조회 시작 위치 (0부터 시작)
• setMaxResults(int maxResult)
: 조회할 데이터 수
데이터베이스 방언 (Dialect
) 덕분에 각기 다른 DB마다 같은 API로 페이징 처리를 할 수 있다.
FROM
절의 서브 쿼리는 현재 JPQL에서 불가능문자
작은 따옴표 사이에 표현한다. 작은 따옴표 표현하려면 연속 두개(''
) 사용
ex) 'Hello'
, 'She''s'
숫자 : 10L(Long)
, 10D(Double)
, 10F(Float)
Boolean: TRUE
, FALSE
Enum
패키지명을 포함한 전체 이름을 사용해야 한다
ex ) jpql.MemberType.ADMIN
String query = "select m.userName, 'HELLO', TRUE from Member m "
+ "where m.memberType = :userType";
List<Object[]> result = em.createQuery(query)
.setParameter("userType", jpql.MemberType.ADMIN)
.getResultList();
for(Object[] objects : result){
System.out.println(objects[0]);
System.out.println(objects[1]);
System.out.println(objects[2]);
}
Hibernate:
/* select
m.userName,
'HELLO',
TRUE
from
Member m
where
m.memberType = :userType */
select
member0_.userName as col_0_0_,
'HELLO' as col_1_0_,
1 as col_2_0_
from
Member member0_
where
member0_.memberType=?
TEST
HELLO
true
TYPE(m) = Member
특정 조건에 따라 분기할 때 CASE 식을 사용함. 종류는 다음과 같다
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();
심플 CASE
조건식이 없고 문법이 단순 (자바의 switch
와 유사)
COALESCE
스칼라식을 차례대로 조회해서 null
이 아니면 반환한다.
NULLIF