출처 : https://www.inflearn.com/course/ORM-JPA-Basic
자바 ORM 표준 JPA 프로그래밍 - 기본편 강의 - 인프런
JPA 를 공부하고 책을 보며 어려웠던 내용을 위주로 먼저 보았습니다. 옆에서 1:1 과외해주는 것 같이 생생하고 이해 잘되는 설명, 예제(코드)가 너무 좋았습니다. 어느 것 하나 애매함없이 모두
www.inflearn.com
프로젝션 (SELECT)
- SELECT 절에 조회할 대상을 지정하는 것 (뭘 가져올건지)
- 프로젝션 대상 : 엔티티, 임베디드 타입, 스칼라 타입(숫자, 문자 등 기본 데이터 타입)
- SELECT m FROM Member m -> 엔티티 프로젝션(멤버 엔티티를 조회)
- SELECT m.team FROM Member m -> 엔티티 프로젝션(멤버와 연관된 팀을 조회)
- SELECT m.address FROM Member m -> 임베디드 타입 프로젝션(address가 임베디드 타입일 때)
- SELECT m.username, m.age FROM Member m -> 스칼라 타입 프로젝션
- DISTINCT로 중복 제거
엔티티 프로젝션
Member member = new Member();
member.setUsername("member1");
member.setAge(10);
em.persist(member);
// 영속성 컨텍스트 제거
em.flush();
em.clear();
// entity들이 반환 됨
List<Member> result = em.createQuery("select m from Member m", Member.class)
.getResultList();
Member findMember = result.get(0);
findMember.setAge(20);
// 반환된 엔티티의 나이를 변경했을 때 변경 됨. = 영속성 컨텍스트에서 모든 엔티티들을 관리하고 있음.
tx.commit();
여러 값 조회
SELECT m.username, m.age FROM Member m
응답 타입이 2개인데 어떻게 가져와야 할까?
1. Query 타입으로 조회
// query로 조회할 때는 타입이 없음.
List resultList = em.createQuery("select m.username, m.age from Member m")
.getResultList();
// Object로 반환됨.
Object o = resultList.get(0);
// 타입 캐스팅
Object[] result = (Object[]) o;
System.out.println("username = " + result[0]);
System.out.println("age = " + result[1]);
2. Object[] 타입으로 조회
// Generic에 Object[]로 선언
List<Object[]> resultList = em.createQuery("select m.username, m.age from Member m")
.getResultList();
// 타입 캐스팅 생략 가능
Object[] result = resultList.get(0);
System.out.println("username = " + result[0]);
System.out.println("age = " + result[1]);
3. new 명령어로 조회
- 단순 값을 DTO로 바로 조회
SELECT new jpabook.jpql.UserDTO(m.username, m.age) FROM Member m - 패키지 명을 포함한 전체 클래스 명 입력
- 순서와 티입이 일치하는 생성자 필요
MemberDTO 생성
package jpql;
public class MemberDTO {
private String username;
private int age;
public MemberDTO(String username, int age) {
this.username = username;
this.age = age;
}
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;
}
}
JPA 클래스
// 패키지를 포함한 전체 클래스 명 작성, 생성자를 통해서 호출 됨.
List<MemberDTO> result = em.createQuery("select new jpql.MemberDTO(m.username, m.age) from Member m", MemberDTO.class)
.getResultList();
MemberDTO memberDTO = result.get(0);
System.out.println("username = " + memberDTO.getUsername());
System.out.println("age = " + memberDTO.getAge());
페이징
- JPA는 페이징을 두가지 API로 추상화
- setFirstResult(int startPosition) : 조회 시작 위치 (0부터 시작)
- setmaxResults(int maxResult) : 조회할 데이터 수
몇 번째부터 몇 개 가져올지만 정하면 됨.
List<Member> result = em.createQuery("select m from Member m order by m.age desc", Member.class)
.setFirstResult(1)
.setMaxResults(10)
.getResultList();
System.out.println("result.size = " + result.size());
for (Member member1 : result) {
System.out.println("member1 = " + member1);
}
JPA가 사용하는 데이터베이스의 방언으로 자동으로 변환해서 쿼리를 날려줌.
조인
내부 조인 (inner 생략 가능)
String query = "select m from Member m inner join m.team";
List<Member> result = em.createQuery(query, Member.class)
.getResultList();
외부 조인 (outer 생략 가능)
String query = "select m from Member m left outer join m.team";
List<Member> result = em.createQuery(query, Member.class)
.getResultList();
세타 조인 : 연관관계가 없는 엔티티들을 조회
String query = "select m from Member m, Team t where m.username = t.name";
List<Member> result = em.createQuery(query, Member.class)
.getResultList();
ON절을 활용한 조인
1. 조인 대상 필터링
예) 회원과 팀을 조회하는데 팀 이름이 A인 팀만 조인
String query = "SELECT m, t FROM Member m LEFT JOIN m.team t ON t.name = 'teamA'";
List<Member> result = em.createQuery(query, Member.class)
.getResultList();
2. 연관관계 없는 엔티티 외부 조인(하이버네이트 5.1부터)
예) 회원의 이름과 팀의 이름이 같은 대상 외부 조인
String query = "SELECT m, t FROM Member m LEFT JOIN Team t on m.username = t.name";
List<Member> result = em.createQuery(query, Member.class)
.getResultList();
서브 쿼리
예) 나이가 평균보다 많은 회원
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
이렇게 하면 성능 잘 안 나옴
서브 쿼리 지원 함수
- [NOT] EXISTS (subquery) : 서브쿼리 결과가 존재하면 참
- {ALL | ANY | SOME} (subquery)
- ALL : 모두 만족하면 참
- ANY, SOME : 같은 의미, 조건을 하나라도 만족하면 참
- [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 서브 쿼리의 한계
- JPA는 WHERE, HAVING 절에서만 서브 쿼리 사용 가능
- SELECT 절도 가능 (하이버네이트에서 지원)
- FROM 절의 서브 쿼리는 현재 JPQL에서 불가능
= 조인으로 풀 수 있으면 풀어서 해결
JPQL 타입 표현과 기타식
- 문자 : 'HELLO', 'She" is'
- 숫자 : 10L(Long), 10D(Double), 10F(Float)
- Boolean : TRUE, FALSE
- ENUM : jpabook.MemberType.Admin (패키지명 포함)
- 엔티티 타입 : TYPE(m) = Member (상속 관계에서 사용)
ENUM 타입
예1)
// ENUM 타입 사용할 때 패키지명을 포함해서 입력
String query = "SELECT m.username, 'HELLO', true FROM Member m"
+ " where m.type = jpql.MemberType.USER";
List<Object[]> result = em.createQuery(query)
.getResultList();
for(Object[] objects : result){
System.out.println("objects = " + objects[0]);
System.out.println("objects = " + objects[1]);
System.out.println("objects = " + objects[2]);
}
예2)
// 파라미터 바인딩
String query = "SELECT m.username, 'HELLO', true FROM Member m"
+ " where m.type = :userType";
List<Object[]> result = em.createQuery(query)
.setParameter("userType", MemberType.ADMIN)
.getResultList();
for(Object[] objects : result){
System.out.println("objects = " + objects[0]);
System.out.println("objects = " + objects[1]);
System.out.println("objects = " + objects[2]);
}
엔티티 타입
예)
// DTYPE이 Book인 Item만 조회
em.createQuery("select i from Item i where type(i) = Book", Item.class)
.getResultList();
JPQL 기타
- SQL과 문법이 같은 식
- EXISTS, IN
- AND, OR, NOT
- =, >, >=, <, <=, <>
- BETWEEN, LIKE, IS NULL
조건식 - CASE 식
- 기본 case 식
select
case when m.age <= 10 then '학생요금'
when m.age >= then '경로요금'
else '일반요금'
end
from Member m
- 단순 case 식 : 조건과 정확하게 매칭될 때
select
case t.name
when '팀A' then '인센티브110%'
when '팀B' then '인센티브120%'
else '인센티브105%'
end
from Team t
- COALESCE : 하나씩 조회해서 null이 아니면 반환
- NULLIF : 두 값이 같으면 null 반환, 다르면 첫번째 값 반환
예) 사용자 이름이 없으면 이름 없는 회원을 반환
select coalesce(m.username, '이름 없는 회원') from Member m
예) 사용자 이름이 '관리자'면 null을 반환하고 나머지는 본인의 이름을 반환
select NULLIF (m.username, '관리자') from Member m
JPQL 함수
기본 함수
- CONCAT
- SUBSTRING
- TRIM
- LOWER, UPPER
- LENGTH
- LOCATE
- ABS, SQRT, MOD
- SIZE, INDEX (JPA 용도)
사용자 정의 함수
- 하이버네이트는 사용전 방언에 추가해야 한다.
- 사용하는 DB 방언을 상속받고, 사용자 정의 함수를 등록한다.
- select function ('group_concat', i.name) from Item i
클래스를 만들고 사용하는 dialect를 상속 받음
public class MyH2Dialect extends H2Dialect {
public MyH2Dialect(){
// group_concat이 이름
registerFunction("group_concat", new StandardSQLFunction("group_concat", StandardBasicTypes.STRING));
}
}
Persistence.xml
dialect 상속 받은 클래스로 value 변경
<property name="hibernate.dialect" value="dialect.MyH2Dialect"/>
JPA 클래스
String query = "select function('group_concat', m.username) From Member m";
List<String> result = em.createQuery(query, String.class)
.getResultList();
for (String s : result) {
System.out.println("s =" + s);
}
'코딩 > JPA' 카테고리의 다른 글
[JPA] 객체지향 쿼리 언어(JPQL) - 중급 문법 (0) | 2024.02.18 |
---|---|
[JPA] 객체지향 쿼리 언어(JPQL) - 기본 문법(1) (0) | 2024.02.05 |
[JPA] 값 타입 (0) | 2023.12.07 |
[JPA] 프록시와 연관관계 관리 (0) | 2023.12.07 |
[JPA] 상속관계 매핑 (0) | 2023.12.04 |