직관적으로 차이를 알 수있다.
Option + Command + v
를 통해 자동적으로 얼추 잡아주니 크게 신경 쓸 필요는 없을 것 같다.
.getResultList()
: 결과가 하나 이상일 때, 리스트 반환.getSingleResult()
: 결과가 정확히 하나, 단일 객체 반환쿼리를 쏠 때, 매개변수를 넣어주는 것이다.
쿼리 내에 매개변수에 :변수명
을 넣고, .setParameter("name", "value")
를 통해 매개변수를 바인딩 한다.
매개변수의 변수명의 이름은 .setParameter()
의 이름과 같기만 하다면 상관 없으나 직관성을 생각하자.
매개변수를 이름이 아닌 인덱션을 기반으로도 사용이 가능하나,
DB의 특성상 실제 인덱스에 변화가 있어도 DB의 인덱스에는 변화가 없으니, 조심하도록 하자
프로젝션이란, SELECT 절에 조회할 대상을 지정하는 것 이다.
프로젝션 대상: 엔티티, 임베디드 타입, 스칼라 타입(숫자, 문자등 기본 데이터 타입)
SELECT m FROM Member m
-> 엔티티 프로젝션
아주 직관적으로 Member 엔티티를 대상으로 하고있다.
SELECT m.team FROM Member m
-> 엔티티 프로젝션
Member 엔티티의 연관관계 Team 엔티티를 대상으로 하고있다.
SELECT m.address FROM Member m
-> 임베디드 타입 프로젝션
일전 예제에서 보았듯 Address는 값 타입. 임베디드 타입을 대상으로 하고있다.
SELECT m.username, m.age FROM Member m
-> 스칼라 타입 프로젝션
엔티티내의 기본 데이터 타입. 스칼라 타입을 대상으로 하고있다.
DISTINCT로 중복 제거
위 내용이 무슨 의미를 가지나면..
위 코드는 어떤 쿼리를 내보낼까.
em.persist(member);
를 통해 insert 문이 나가고
영속성 컨테이너를 flush, clear 해주었으니, 다음엔 Select 쿼리가 나갈 것이다.
그리고 가져온 findMember의 나이를 바꾸어주었을때, 이 데이터가 영속성 컨테이너에 의해 관리되고 있다면 update문이 나갈것이고, 아니라면 나가지 않을것이다.
위 예시에서도 보았듯 여러가지 스칼라 타입도 함께 가져올수 있다.
이럴 땐 저장을 어떻게 해야할까?
정말 똑똑한 Intelli J, 단축키를 통해 iter를 돌려보니, object 타입으로 받고 있다.
하지만 실제로 살펴보면 이는 username과 age로 이루어진 배열의 형태이다.
그래서 배열로 받는 편이 깔끔하다.
또한 Object의 배열을 설정하여 TypeQuery로 받을 수 도 있다.
가장 추천 하는 방법은 단순 값을 DTO로 바로 조회 하는 것이다.
사용할 스칼라 타입을 넣고 생성자와 게터,세터를 만들고,
생성자를 통해 새 객체를 만드는 것이다.
주의 할 점은 패키지명을 포함한 전체 클래스 명을 입력하고,
순서와 타입이 일치하는 생성자 필요하다.
페이징의 개념은 데이터베이스나 정처기.. 등을 공부해봤다면 대충 개념은 알것이다.
페이징에서 중요한 메서드는 하이라이트 된 두 블럭.
솔직히 메서드 이름, 코드의 로직과 실행 로직만 보면 가타부타 설명하지 않아도 눈칫밥으로들 다 알 것이다.
그런데 페이징이 왜 중요할까. JPA의 장점 중 하나인, DB의 방언을 맞춰준다는 것..
H2 DB의 SQL
Oracle DB의 SQL
DB의 방언에 맞춰 SQL을 알아서 날려주기 때문에 페이징 문법이 까다로운 DB에서 아주 편리하다.
내부, 외부 조인은 익숙하지만 세타 조인이라는 용어가 익숙하지 않다.
하지만 문법을 보니 일반적으로 select문 으로 두 테이블을 비교하는 쿼리와 같다는 점을 명심하자.
연관관계가 없는 것을 막 조인 하는 것을 세타 조인이라고 하신다.. 명한선생님 께서
조인은 중요한 개념이지만, 어느정도 알고있기도 하고, 앞으로도 계속 보게 될 터이니 지금은 간단히 넘어가도록 하자
예) 회원과 팀을 조인하면서, 팀 이름이 A인 팀만 조인
JPQL:
SELECT m, t FROM Member m LEFT JOIN m.team t on t.name = 'A'
SQL:
SELECT m.*, t.* from Member m LEFT JOIN Team t ON m.TEAM_ID=t.id and t.name='A
일반 select문의 where 절이라고 생각하면 쉽다.
예) 나이가 평균보다 많은 회원
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
별로 어려운 내용은 아니다.
기초 SQL 문법
[NOT] EXISTS (subquery): 서브쿼리에 결과가 존재하면 참
{ALL | ANY | SOME} (subquery)
[NOT] IN (subquery): 서브쿼리의 결과 중 하나라도 같은 것이 있으면 참
예) 팀A 소속인 회원
select m from Member m where exists (select t from m.team t where t.name = ‘팀A')
예) 전체 상품 각각의 재고보다 주문량이 많은 주문들
select o from Order o where o.orderAmount > ALL (select p.stockAmount from Product p)
예) 어떤 팀이든 팀에 소속된 회원
select m from Member m where m.team = ANY (select t from Team t)
JPA는 select, where, having 절에서만 서브 쿼리를 사용 가능하다.
FROM 절의 서브 쿼리는 현재 JPQL에서 불가능하다.
-> 조인으로 풀수있으면 풀어서 해결 (이걸로 안되면 안댐 .ㅠ sql을 쪼개서 해야되려나)
문자
'HELLO', 'She''s'
숫자
10L(Long), 10D(Double), 10F(Float)
Boolean
TRUE, FALSE
ENUM
엔티티 타입
em.createQuery("select i from Item i where type(i) = Book", Item.class)
.getResultList();
select
case when m.age <= 10 then '학생요금'
when m.age >= 60 then '경로요금'
else '일반요금'
end
from Member m
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();
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_
select
case t.name
when '팀A' then '인센티브110%'
when '팀B' then '인센티브120%'
else '인센티브105%'
end
from Team t
String query =
"select coalesce(m.name, '이름 없는 회원') from Member m";
String query =
"select nullif(m.name, '관리자') from Member m";
JPQL이 제공하는 표준 함수
CONCAT
SUBSTRING
TRIM
LOWER, UPPER
LENGTH
LOCATE -> indexOf와 같이 자리수 반환
ABS, SQRT, MOD
SIZE, INDEX(JPA 용도)
값 타입 컬렉션에 컬렉션의 위치 값을 구할 때 @OrderColumn 사용. INDEX로 위치 참조 가능
String query = "select size(t.members) from Team t";
String query = "select index(t.members) from Team t";
하이버네이트는 사용전 방언에 추가해야 한다.
DB에 커스텀 펑션 만든 후 사용
select function ('group_concat', i.name) from Item i
select 'group_concat'(i.name) from Item i