JPA 데이터 타입은 크게 엔티티 타입과 값 타입이 있다.
이번 장에서는 값 타입에 대해 공부
값 타입에는 3가지가 존재
말 그대로 기본 값이다. 자바가 제공하는 기본 데이터 타입이다.
String, int …
새로운 값 타입을 직접 정의해서 사용
값 타입을 정의하는 곳에 @Embeddable
값 타입을 사용하는 곳에 @Embedded
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String name;
@Embedded Address homeAddress;
}
@Embeddable
public class Address {
@Column(name="city")
private String city;
private String street;
}
Member 엔티티가 더욱 응집력 있게 바뀌도록
임베디드 타입은 엔티티의 값일 뿐이기에, 테이블에 매핑은 똑같이 들어간다.
잘 설계한 ORM 애플리케이션은 매핑한 테이블의 수보다 클래스의 수가 더 많다
지루한 반복 작업은 JPA에 맡기고 더 세밀한 객체지향 모델을 설계하는데 집중하자
값 타입을 여러 엔티티에서 공유하면 위험하다.
(왜 인지는 알지??)
임베디드 타입 처럼 직접 정의한 값 타입은 기본 타입이 아니라 객체 타입이다.
자바에서 객체는 대입을 할 때 참조를 넘겨주기에 객체의 공유 참조를 피할 수 없다.
때문에 외부에서 원본 객체의 참조값을 받은 다른 객체가 원본 객체의 값을 수정할 수도 있는 것이다. 이를 방지하기 위해 수정자 메소드를 모두 제거하는게 안전하다.
→ 객체를 불변 객체로 만들자!!
동일성 비교 : == 연산자 , 인스턴스 참조값 비교
동등성 비교 : equals() 사용, 인스턴스 값 비교
동등성 비교를 위해서는 equals() 메소드를 재정의 해야 하고, hsahCode 도 재정의 하는 것이 안전하다
(hashCode 란 객체를 식별하는 정수값)
안볼랭
객체지향 쿼리 소개
JPQL
Criteria
QueryDSL
네이티브 SQL
객체지향 쿼리 심화
em.find()
메소드를 통해 엔티티 하나를 조회할 수 있지만,
복잡한 검색일 경우 SQL로 필요한 내용을 최대한 걸러서 조회해야 한다.
ORM은 DB 테이블이 아닌 엔티티 객체를 대상으로 개발하므로 검색도 엔티티 객체를 대상으로 하는 방법이 필요
이를 위해 만들어진게 JPQL
JPQL의 기본 문법과 쿼리 진행
(INSERT는 없음, em.persist() 메소드가 있기 때문에)
update, delete는 뒤에서 진행
SELECT m FROM Member AS m where m.name = "Sangwoo"
JPQL을 실행하려면 쿼리 객체를 만들어야 함.
반환할 타입이 명확하다면 TypeQuery
명확하지 않다면 Query
TypedQuery<Member> query =
em.createQuery("SELECT m FROM Member m", Member.class);
// 두번째 파라미터에 반환할 타입을 지정
Query query =
em.createQuery("SELECT m FROM Member m");
// 지정하지 않으면 Query 반환
// 결과 조회
List result = query.getResultList();
// 결과가 정확히 하나라면
query.getSingleResult() 사용
SELECT m.username, m.age FROM Member m
// 이름 기준
em.createQuery("SELECT m FROM Member m where m.username = :username", Member.class);
// 위치 기준 (? 다음에 위치값 주면 됨 (1부터 시작))
em.createQuery("SELECT m FROM Member m where m.username = ?1", Member.class)
.setParameter(1, usernameParam);
SELECT 절에 조회 대상을 지정하는 것
em.createQuery(”SELECT m.username, m.age FROM Member m”)
을 해주면 두 필드를 프로젝션해 Object를 받을 텐데,
이때 NEW 명령어를 통해 반환받을 클래스를 지정할 수 있다.
em.createQuery(”SELECT new jpabook.jpql.UserDTO(m.username, m.age) FROM Member m”
, UserDTO.class)
DB마다 페이징을 처리하는 SQL 문법이 다르다.
JPA는 이를 추상화하였음
집합 함수
COUNT: 결과 수 반환, 반환타입 Long
MAX, MIN
AVG: 평균 값, 반환타입 Double
SUM
GROUP BY, HAVING : 특정 그룹끼리 묶어줌
ORDER BY: 정렬할 때 사용
내부조인: INNER JOIN (INNER 생략 가능)
SELECT m FROM Member m INNER JOIN m.team t
where t.name = :teamName
외부 조인: LEFT JOIN
SELECT m FROM Member m LEFT JOIN m.team t
컬렉션 조인
SELECT t, m FROM Team t LEFT JOIN t.members m
세타 조인 where 절을 통해 할 수 있단다
내부 조인만 지원
(세타 조인은 내부조인과 비슷하지만, where를 통해서 간단하게 할 수 있는거)
select m from Member m, Team t
where m.username = t.name
JOIN ON 절 외부조인에서만 사용한단다.
조인 대상 필터링 하고 조인할 수 있다.
select m, t from Member m left join m.team t on t.name = 'A'
성능 최적화를 위해 제공하는 기능
연관된 엔티티나 컬렉션을 한 번에 조회하는 기능
엔티티 페치 조인
select m from Member m join fetch m.team
페치 조인과 일반 조인의 차이
select t from Team t join t.members m
where t.name = ‘TeamA’
JPQL문을 SQL로 변환할 때 조인한 회원을 조회하지 않고, 오로지 팀만 조회했다.
JPQL은 결과를 반환할 때 연관관계를 고려하지 않고, 단지 SELECT 절에 지정한 엔티티만 조회할 뿐이다.
페치 조인을 사용하면 연관된 엔티티도 함께 조회한다!!
페치 조인의 특징
엔티티에 직접 적용하는 로딩전략 (글로벌 로딩 전략) 보다 페치 조인을 우선한다.
@OneToMany(fetch = FetchType.LAZY) 보다 우선함
글로벌 로딩 전략은 지연로딩으로 하되, 최적화가 필요하면 페치 조인 적용하는게 효과적
한계
.(점) 을 찍어 객체 그래프를 탐색하는 것
알아야 할 용어
상태 필드: 값을 저장하기 위한 필드
연관 필드: 연관관계를 위한 필드
JPQL에서 경로 표현식을 사용해 경로 탐색을 하려면 3가지 경로에 따라 어떤 특징이 있는지 이해해야 함
상태 필드 경로 : 경로 탐색의 끝
select m.name from Member m
단일 값 연관 경로 : 묵시적으로 내부조인이 일어남, 계속 탐색 가능
select m.team from Member m
컬렉션 값 연관 경로 : 묵시적으로 내부조인이 일어남, 계속 탐색 불가능
select t.members from Team t
select t.members.name from Team t // 경로 탐색 시작 허락 안함, 불가능
JPQL로 부모 엔티티를 조회하면 자식 엔티티도 함께 조회
Item이라는 클래스의 자식으로 Book Album Movie가 있다 가정
조인 전략을 사용하면 모든 자식을 조인을 통해 같이 조회하지만
TYPE
을 통해 조회 대상을 특정 자식 타입으로 한정할 수 있다.
동적 쿼리: JPQL을 문자로 완성해서 직접 넘기는 것
정적 쿼리(Named 쿼리): 미리 정의한 쿼리에 이름을 부여해서 필요할 때 사용
@Entity
@NamedQuery(
name = "Member.findByUsername",
query = "select m from Member m where m.username = :username")
public class Member {
}
em.createNamedQuery("Member.findByUsername", Member.class)
.setParameter("username", "sangwoo")
.getResultList();
JPQL을 자바 코드로 작성하도록 도와주는 빌더 클래스 api다
코드로 JPQL을 작성하므로 문법 오류를 컴파일 단계에서 잡을 수 있다
JPA Criteria는 너무 장황하고 복잡하다. 반면에 QueryDSL은 같은 코드 기반이지만,
결과 코드가 JPQL과 비슷하고 직관적이다.
→ QueryDSL 써라
복잡하고 어려운 Criteria의 단점을 극복하기 위해 쉽고 간결한 오픈소스 프로젝트
처음에 몰랐던게 QMember?? 이런게 어디서 나온거지??
-> 알고보니까 QueryDSL 설정을 해주면 @Entity 를 보고 컴파일 할 때 Q 클래스들을 생성해준대!!