공부할 때 왜 그것을 배우는지 목적이 중요하다 생각한다. JPA를 왜 사용하는지도 마찬가지다.
<목적>
JPQL은 객체 지향 SQL 이다. 라고 생각하면 된다.
엔티티의 생명주기는 4가지로 분류할 수 있다.
**//객체를 생성한 상태(비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");**
1차 캐시 이점
트랜잭션을 지원하는 쓰기 지연
변경 감지
flush ⇒ 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화
JPA를 사용해서 테이블과 매핑하려면 @Entity를 붙여야 된다.
데이터베이스 ddl.auto 설정에서
JPA를 공부하던중 clob을 보고, 기존에 설계할 대 쓰던 blob과 차이점이 궁금하여 찾아봄
문자 대형 객체 (Character), Oracle Server는 CLOB과 VARCHAR2 사이에 암시적 변환을 수행
문자 기반 데이터 보관용
DB에 반영안하고 계산 용도로만 쓰고 싶을 때
@Transient// DB에 반영안하고 계산 용도로만 쓰고 싶을 때
private inttemp;
nullable = false →Not NULL
기본키를 Long타입으로 하는 이유 → 10억이 넘어갔을 때 Integer가 힘듬 그리고 10억 정도되는 규모에서 Integer → Long으로 변경하기도 힘들다.
Identity 전략은 em.persist()하는 순간 INSERT쿼리가 나간다.(쿼리를 날려야만 id값을 알 수 있음)
Sequence 전략은 next_val을 먼저 알고, 그다음에 INSERT 쿼리가 나간다.(쿼리 날리기전에 미리 id를 알 수 있음 = 버퍼를 사용해서 날린다 (Sequence전략은 성능 최적화를 위해 사용한다고 보면됨)
(집중) 양방향 연관관계와 연관관계의 주인
team에 member도 추가하고, member에 team도 넣어줘야함
team.getMembers().add(member);
member.setTeam(team);
em.persist(member);
----- 위 코드를 Member 엔티티에 setTeam할 때 들어가게 한다면?
public void setTeam(Team team) {
this.team = team;
team.getMembers().add(this);
}
이렇게 만들어주면
//team.getMembers().add(member); 를 작성할 필요없다.
이것을 '연관관계 편의 메소드' 라고 한다
사실setter방식을 권장하지 않기 때문에
setTeam -> chageTeam으로 변경해서 사용
JPA설계 할 때, 1차적으로 단방향으로 설계가 끝나야한다.(양방향없이)
Member 엔티티에 orders라는 List라는게 있는 것이 그렇게 좋지않음
mappedBy는 연결하려는 변수명에 name=””을 지정해준다고 생각하면 편한다.
단방향 관계만으로도 어플리케이션 가능하지만, JPQL하다보면 양방향 관계가 필요한 경우가 생긴다.
다대일은 (Member : Team) 를 생각 (Member가 주인인 case)
일대다는(Team: Membrer) , Team이 주인
일대일 관계는 주 테이블 or 대상 테이블 어디던 외래키 가져도 상관없다. 단, 외래 키에 데이터베이스 유니크 제약조건 추가
다대다 관계 → 일대다, 다대일로 풀어서 써라 객체는 다대다 (List List)로 가능하다.
논리모델을 물리모델로 바꾸는 방식
조인 전략(객체랑 잘맞고, 정규화 되고, 정석방식이라고 알아두면 된다.)
→ @DiscriminatorColumn ,@DiscriminatorValue공부하기
단일 테이블 전략( 테이블 한 개에 다 때려박음)
구현 클래스마다 테이블 전략(각각 다 갖는 방식)
@MappedSuperclass
프록시
예시
Member.refMember = em.getReference(Member.class, member1.getId());
System.out.println("refMember = " + refMember.getClass()); //Proxy
em.clear();
refMember.getUsername(); //여기서 에러발생
실무에서 즉시 로딩은 사용하지 않는다.
CASCADE할 때, Parent의 영속성을 Child로 전이할 때는 가능하지만 Child가 다른 것과 연결이 되어있을 때(참조하는 것이 하나일 때)는 CASCADE하면 안된다.
**orphanRemoval = true 고아객체, CASCADE.Remove처럼 동작한다라고 이해하면 될 것같다. 사실 CASCADE.ALL만해도 적용됨**
Order → Delivery 영속성 전이 ALL 설정, Order → OrderItems 영속성 전이 ALL
엔티티 타입
기본 타입
임베디드 타입
@Embeddable: 값 타입을 정의하는 곳에 표시
@Embedded: 값 타입을 사용하는 곳에 표시
객체와 테이블을 세밀하게 매핑하는 것이 가능
한 엔티티에서 값은 값 타입을 사용하려면?
- @AttributeOverrides를 사용해서 재정의
****
⇒느낀점: 구조를 복잡하게 테이블을 따로 만들 필요없이, 임베디드하게 설계 해주면 테이블 생성도 안해도되고, 깔끔하게 설계가 되는 것같다.
값 타입은 불변으로 만들어야 한다.
값 타입 컬렉션 컬럼들이 전부 PK인 이유 → PK를 하나만 설정해주면 엔티티로 취급하므로
정리
임베디드 타입은 “엔티티의 값"일 뿐이다.
그런 값 타입은 불변 객체로 설계해야하고, equals()로 비교한다.
값 타입은 eqauls()랑 hashcode를 오버라이딩 해서 사용한다.
JPQL - 테이블을 대상으로 x, 엔티티 객체를 대상으로 쿼리
SQL - 데이터베이스 테이블을 대상으로 쿼리
Mybatis, JdbcTemplates를 섞어서 쓸거면, SQL을 실행하기 직전에 영속성 컨텍스트
수동 플러시 해야한다.
getResultList(); → 결과가 하나 이상일 때
getSingleResult(); → 결과가 정확히 하나
Option + Commnad + V → 변수 생성
프로젝션 = SELECT할 것
new 연산자 사용(Best), 패키지 명이 길면 다 적어야 되는 단점
List<MemberDTO> result = em.createQuery("select new jpql.MemberDTO(m.username, m.age) from Member.m", MemberDTO.class)
.getResultList();
페이징
조인
**JPQL:**
SELECT m, t FROM Member m LEFT JOIN Team t on m.username = t.name SQL:SELECT m., t. FROM Member m LEFT JOIN Team t ON m.username = t.name username과 team의 name 전혀 관계 없지만 조인 가능 →내 생각: left outer라서 가능한듯?서브 쿼리
[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)
JPQL 타입 표현
조건식
경로 표현식
상태 필드: 단순히 값을 저장하기 위한 필드(ex m.username)
연관 필드: 연관관계를 위한 필드
단일 값 연관 필드:(묵시적 내부 조인 발생, 탐색 O)
@ManyToOne, @OneToMany, 대상이 엔티티 (ex m.team)
컬렉션 값 연관 필드:(묵시적 내부 조인 발생, 탐색X)
@OneToMany, @ManyToMany, 대상이 컬렉션 (ex m.orders)
명시적 조인: join 키워드 직접 사용
묵시적 조인: 경로 표현식에 의해 묵시적으로 SQL 조인 발생(내부 조인만 가능)
→ 결론: 실무에서 묵시적 조인 쓰지마라, 명시적 조인을 써라
why? 코드보고 유지보수하기가 더 용이, 문제 터졌을 때 찾기 힘들다.
묵시적 조인은 조인이 일어나느 상황을 한눈에 파악하기 어려움
페치 조인(실무에서 매우 중요)
페치 조인의 한계
별칭을 주지 말아라(줄 순 있으나, 문제터질 가능성있음)
컬렉션을 페치 조인하면 페이징API(setFirstResult, setMaxResults)를 사용할 수 없다.
→ 결론: 모든 것을 페치 조인으로 해결할 수는 없다. 페치 조인은 객체 그래프를 유지할 때 효과적이다.
여러 테이블을 조인해서 엔티티가 가진 모양이 아니라 전혀 다른 결과를 내야한다면, 일반 조인을 사용해서 필요한 데이터들만 조회해서 DTO로 반환하는 것이 효과적이다.
대부분의 성능 문제의 7~80%는 N+1 문제더라.
다형성 쿼리
벌크 연산
SQL의 UPDATE,DELETE지원
em.reateQuery("update Member m set m.age =20"); //이런식으로
벌크 연산은 영속성 컨텍스트를 무시하고, 데이터베이스에 직접 쿼리