** 이거 몰랐던 건데 JPQL 조인은 엘리어스 줘서 조건(where) 주지마라. 
	JPA 설계 기본 사상은 모든게 조회가 된다고 생각 하는게
 기본 관례이기 때문. 조건(where)을 줘야 한다면 그냥 따로 조회 하자 
	그리고 cascade 등등의 이상한 옵션이 있는데 엘리어스 조건으로 일부만 조회 할 경우
	예상치 못한 동작을 할 수가 있다 (정합성 이슈)
	where 절 말고 join만 타고타고 해야 하는 경우는 별칭을 쓰면 된다.
**
** 둘 이상의 컬렉션은 fetch 조인 할 수 없다 (1 대 다 X 다) **
** 
컬렉션을 페치 조인하면 페이징 API를 사용할 수 없다. 
이 경우 하이버네이트는 경고 로그를 남기고 메모리에서 페이징 한다 (매우 위험) 
( .setFirstResult(0), .setMaxResults(1) .getResultList() )
(페이징 쿼리가 안나가고 메모리에서 알아서 페이징... 데이터를 다 긁어와서 장애나기 좋음)
(만약 조회 건수가 100만건이라면 100만건을 메모리에 다 퍼올려서 페이징 함...)
해결책은 일대 다 -> 다대 일로 조회 (team -> member , member -> team) 
방향을 뒤집어서 해결하는 방법이 있다.
아니면 join 하지 말고 LAZY로딩으로 하거나
@BatchSize 를 사용 (설정한 개수만큼 in 절에 놔눠서 쿼리 날림) (in 에 많이 태워지면 사용)
**
* 양방향은 되도록 필요할때만 처음에는 단방향으로 설계를 끝내야 함. 
* ToString -> 가급적 내부 필드만 (연관관계 없는 필드만)
* NoArgsConstructor AccessLevel.PROTECTED: 기본 생성자 막고 싶은데, JPA 스펙상 PROTECTED로
열어두어야 함
* 양방향 연관관계 주인의 반대편은 읽기만 가능
* 읽기만 가능 하지만 하위 코드에서 객체를 사용하면 1차 캐시에서 바로 가져와 사용 하므로
반대편도 넣어 줘야 한다. 아래는 자동으로 반대편도 넣어주는 패턴
* JPQL 에서 엔티티를 직접 사용하면 SQL에서 해당 엔티티의 기본 키 값을 사용 ex) select count(m)

* 벌크 연산은 영속성 무시하고 바로 디비 쿼리 
-> 벌크연산 후 flush() 국룰 그냥 마음 편하게 해주면 됨 해주면 마음이 편함
-> 벌크연산 먼저실행(자동 flush 실행됨) or 벌크연산 후 영속성 초기화 (벌크연산 엔티티에 반영됨) *
-> insert 후 벌크연산 실행시(쿼리 날리면) flush 자동실행 후 벌크연산 됨
	(벌크연산 후 기존 insert 된 엔티티 조회시 벌크연산은 반영 안된 상태 주의, DB는 반영 완료)
-> flush 자동호출은 commit 하거나, query 나가거나, 강제 flush() 호출시 flush 됨

// 반대편 set 해주는 편의메서드
public void changeOrder(Order order) {
        this.order = order;
        order.getOrderItem().add(this);
    }

public void addOrderItem(OrderItem orderItem) {
        orderItem.setOrder(this);
        this.orderItem.add(orderItem);
    }
// 둘 중 한곳에서만 선언해서 사용

연관관계의 주인
1. 객체의 두 관계중 하나를 연관관계의 주인으로 지정
2. 연관관계의 주인만이 외래 키를 관리(등록, 수정)
3. 주인이 아닌쪽은 읽기만 가능
4. 주인은 mappedBy 속성 사용X
5. 주인이 아니면 mappedBy 속성으로 주인 지정
6. *연관관계의 주인은 FK의 위치를 기준으로 정해야 간단해진다 반대 조회가 필요할때 편의메서드 사용*

양방향 매핑시 가장 많이 하는 실수
** 사실상 단방향 연관관계만 있어도 개발 가능, 양방향은 편의성을 위해서(조회, JPQL) **
** lombok 에서 toString() 쓰지 마라 (연관관계 무한루프) **
** 컨트롤러에서 Entity 반환하지 마라 (JSON 생성 무한루프) **
1. 연관관계의 주인에 값을 입력하지 않음 (mappedBy 설정된 엔티티를 불러와 값을 넣었을 경우)
2. 주인의 값은 무조건 셋 하면, 반대편도 해 주는걸 추천(1차 캐시에 넣어서 밑에서 쓸수 있도록)
3. 무한루프 조심 ( toString(), lombok, JSON 생성 라이브러리)
-> toString() 무한루프는 한쪽에서 toString() 으로 참조대상 까지도 toString() 대상에 들어가고
- 만약 한쪽에 Many 라면 하나하나 다 번갈아 가면서 toString() 을 찍게 되어 무한루프에 걸린다.
-> JSON 생성 라이브러리 또한 마찬가지다 양방향 시 Entity 를 JSON 으로 바꾸는 순간 걸린다.
- ex) 컨트롤러에서 Entity를 바로 반환할 때 Entity를 JSON 으로 바꾸면서 장애가 난다.
- ex) Member를 JSON 으로 바꿈 --> TEAM 이 있네? TEAM을 JSON 으로 바꿈 --> MEMBER가있네?
4. One 쪽의 반대편이 Many 라면 new ArrayList<>(); 로 NullpointException 을 방지해준다.

mappedBy (여기에 값을 넣어봐야 아무런 소용이 없다는걸 내가 주인이 아니다 를 명시적으로 지정)
1. Foreign Key 없는쪽에 mappedBy 설정 권장
2. 연관관계의 주인이 누구 인지를 나타냄
3. 연관관계의 주인만이 외래키를 관리(등록, 수정)
3. One 쪽에 선언, name은 반대 쪽의 엔티티 객체를 적어주면 됨 (Ctrl + click 이동가능)

joinColumn
1. 반대편에 mappedBy 가 선언되어 있다면 joinColumn이 연관관계의 우선권을 가져감
2. 연관관계의 주인을 나타냄 (일반적으로는 FK를 가진 쪽)
3. Many 쪽에 선언
4. 생략시 `{ 대상 엔티티 클래스 이름 }_id` 이지만 명시적으로 선언 해주는걸 추천

@ManyToOne
1. 기본적으로 Eager로 되어 있기 때문에 특별한 경우가 아니라면 LAZY 옵션을 준다.
2. 일일 관계에서 FK를 가지고 있는 쪽과 관리하는쪽(연관관계의 주인)이 다르면 안된다. 
3. 외래키를 가진쪽을 연관관계의 주인으로 설정

N:1
@ManyToOne, @JoinColumn(name = "b_id), private B b; (A쪽에 b_id FK 존재 )
@OneToMany(mappedBy = "b") private List<A> aList;

fetch조인
데이터를 조회할때 N + 1 해결함(바로 조인해서 조회) 과 동시에 select 절에 조인한
데이터를 자동으로 추가해준다. Repository 에 @(Query) 에 선언해(JPQL) 사용
(간단할땐 EntityGraph 복잡할땐 JPQL Fetch 조인)

entityGraph
fetch 조인시 JpaRepository 네이밍 룰과 동시에 사용 가능
(간단할땐 EntityGraph 복잡할땐 JPQL Fetch 조인)

@ManyToOne, @OneToOne 같은ToOne 시리즈들은 기본 로딩이 EAGER(즉시) 로딩이다.

OneToOne 주의사항

// 공통 주의점
* 단방향 관계가 가능 하나 대부분의 상황에서 양방향 관계를 사용 (일부 특정한 상황에서나 단방향)

* OneToOne 관계에서는 관계의 대상인 엔티티의 프라이머리 키가 외래키로 참조 되므로 
외래키의 값은 중복되지 않아야 함. 그러므로 UNIQUE 제약 조건을 추가해야 함

* 외래키의 값을 직접 지정해야 하는 경우는 자제 (JPA 에서는 자동으로 외래키 값을 생성 관리 )
만약 직접 외래키 값을 지정 할 경우 JPA 구현체에서 예기치 않은 결과 가능성 주의

* LAZY 로딩시 성능 이점 얻음, 하지만 LAZY 로딩시 프록시 객체 사용 하므로, 
지연 로딩시 예외발생 가능성 주의

* OneToOne 관계에서는 기본적으로 Optional 속성이 true로 설정되어 있으며, 
이 경우에는 외래 키가 NULL 일 수 있습니다. 만약에 이러한 상황을 방지하려면, 
OneToOne 관계에서는 반드시 Optional 속성을 false로 설정해야 합니다.

* OneToOne 관계에서는 양쪽 엔티티 중 하나를 연관관계의 주인으로 설정해야 합니다. 
이를 설정하지 않으면 JPA는 기본적으로 둘 중 하나를 임의로 선택하여 주인으로 설정합니다. 
따라서 OneToOne 관계에서는 반드시 연관관계의 주인을 설정해야 합니다.

* OneToOne 관계에서는 두 엔티티 사이에 고유 제약 조건이 설정되어야 합니다. 
이는 OneToOne 관계에서 연관된 두 엔티티가 항상 서로를 참조하고 있어야 하기 때문입니다.

* OneToOne 관계에서는 FetchType을 LAZY로 설정하는 것이 좋습니다. 
EAGER로 설정하면 연관된 엔티티가 즉시 로딩되어 성능 이슈가 발생할 수 있습니다.

* OneToOne 관계에서는 일반적으로 부모 엔티티에서 
자식 엔티티로 cascading을 적용하는 것이 좋습니다. 
이를 통해 부모 엔티티의 변경 사항이 자식 엔티티에 전파되어 관리하기 쉬워집니다.

* 일대일 관계는 그 반대도 일대일

* 주 테이블이나 대상 테이블 중에 외래 키 선택 가능

* 외래 키에 데이터베이스 유니크(UNI) 제약조건 추가

* 다대일 양방향 매핑 처럼 외래 키가 있는 곳이 연관관계 주인

* 반대편은 mappedBy 적용

// 일대일 정리

* 주 테이블에 외래 키 (Foreign Key)
	-> 주 객체가 대상 객체의 참조를 가지는 것 처럼 주 테이블에 외래키를 두고 대상 테이블을 찾음
	-> 객체지향 개발자 선호
	-> Foreign Key null 이면 조회 안하면 되니까 불필요한 조회 없어 성능상 이득
	-> JPA 매핑 편리
	-> 너무 먼 미래를 생각하지 않고 보통 이걸 선택함
	-> 장점: 주 테이블만 조회해도 대상 테이블에 데이터가 있는지 확인 가능
	-> 단점: 값이 없으면 외래 키에 null 허용, 나중에 ManyToOne 변경 가능성 있을 시 바꾸기 힘듬

* 대상 테이블에 외래 키
	-> 대상 테이블에 외래 키가 존재
	-> 전통적인 데이터베이스 개발자 선호
	-> 장점: 주 테이블과 대상 테이블을 일대일에서 일대다 관계로 변경할 때 테이블 구조 유지
	-> 단점: 프록시 기능의 한계로 지연로딩으로 설정해도 항상 즉시 로딩됨
	

// 단방향
* (대상 테이블에 왜래 키 단방향-반대편 테이블의 외래키 관리) 단방향 관계는 JPA 지원X 불가능
// 양방향
* (대상 테이블에 외래 키 단방향-반대편 테이블의 외래키 관리) 양방향 관계는 JPA 지원
* 주 테이블에 FK가 있는경우 대상 테이블을 조회하면 LAZY 라도 EAGER 로딩이 된다.
- 프록시 사용 시 외래 키를 직접 관리하지 않는 일대일 관계는 즉시로딩
- 자기와 연관된 주 테이블 ROW 가 있는지 select 하게 된다.
- 관계의 주인을 FK를 가진 쪽으로 바꿔준다.

// CASCADE

단방향일 경우 
- 부모(보통은 OneToMany 쪽, ForeignKey 없는 쪽)에만 
Cascade : 부모삭제 -> 자식 삭제, 자식삭제 -> 영향없음 ( 단방향일때 일반적)

- 자식에만 Cascade : 부모삭제 -> 영향없음, 자식삭제 -> 부모삭제 (권장되지 않음)

양방향일 경우
- 부모에만 Cascade : 부모삭제 -> 자식 삭제, 자식삭제 -> 영향없음
하지만 반대편 엔티티에도 관계를 매핑해줘야 하므로 mappedBy 속성을 이용해 매핑 정보를 정의해
주어야 함, 이 때 CascadeType 옵션을 부모 엔티티에만 걸어주면 됨 
(자식에서 부모를 참조하는 필드에 대해서)
- 자식에만 Cascade : 부모삭제 -> 영향없음, 자식삭제 -> 부모삭제 mappedBy 속성을 이용해 
매핑 해줘야 함 
(자식에서 부모를 참조하는 필드에 대해서)

ManyToOne 주의사항

// 주요 속성
optional: false 로 설정하면 연관된 엔티티가 항상 있어야 한다.(기본값 TRUE)
fetch: 글로벌 페치 전략을 설정한다. (- @ManyToOne=FetchType.EAGER- @OneToMany=FetchType.LAZY)
cascade: 영속성 전이 기능을 사용한다
targetEntity: 연관된 엔티티의 타입 정보를 설정한다. 
이 기능은 거의 사용하지 않는다. 컬렉션을 사용해도
제네릭으로 타입 정보를 알 수 있다.

// 공통 주의점
* N + 1 문제 발생 가능성 주의

* 다수의 엔티티 로딩시 Lany Loading 으로 설정 않으면 성능문제 발생 가능성 존재
-> Fetch Join, Entity Graph, Batch Size 등의 방법으로 필요 할 때만 즉시로딩 해서 해결

* 외래키(Foreign Key)관리에 주의
-> ManyToOne 관계에서 외래키를 어떻게 관리 할지에 대한 설정이 필요
	-> 방법으로는 Join Column, Join Table, OneToMany 와 mappedBy 등의 방법 존재

* Cascade 옵션을 주의
-> 연관된 엔티티를 삭제할 때는 orphanRemoval 옵션을 설정하여 자식 엔티티를 자동 삭제 가능

* ManyToOne 관계에서 연관된 엔티티를 검색할 때 성능을 향상시키기 위해서는 적절한 인덱스 설정

* LazyInitializationException 발생 가능성 존재
-> N + 1 쿼리가 나간다고 가정 했을때 나가는 시점에 영속성컨텍스트가 종료 되었을 경우 발생
		-> N + 1 상황을 안만들고 Fetch Join 이나 Entity Graph 사용 
		-> Hibernate.initialize() 메서드 사용, Open Session in View 패턴 이용

* CascadeType.PERSIST 사용 시 연관된 엔티티만 영속화하므로 안전함

// 단방향 관계
* ManyToOne 단방향 관계에서 기본적으로 ManyToOne 쪽이 연관관계의 주인
	-> 반드시 ManyToOne 쪽에 mappedBy 속성을 사용해 단방향 관계를 설정

* ManyToOne 단방향 관계에서 기본적으로 외래키가 ManyToOne 쪽에 위치
	-> 외래키에 Not Null 제약조건 시 ManyToOne 쪽 엔티티 저장할 때 외래키 없으면 에러 발생 주의

* ManyToOne 단방향 관계에서 mappedBy 대신 @JoinColumn 어노테이션 사용해 외래키 지정 가능
	 

// 양방향 관계
* ManyToOne 관계는 OneToMany 관계와 함께 사용하여 양방향 관계 설정 가능

* mappedBy, JoinColumn 등의 설정이 필요하며, 연관 엔티티의 상태를 일관되게 관리 필요

* 객체 참조의 일관성 유지
-> 서로의 참조가 서로 일치해 일관성을 유지 하도록 해야 함

* 연관관계의 주인을 명시(외래키를 관리하는 엔티티)

* 무한 루프 방지
-> 양방향 매핑시 객체 간에 상호 참조가 가능해져 무한 루프가 발생할 수 있음
	-> @JsonIgnore, @JsonManagedReference, @JsonBackReference 등의 어노테이션 사용해 방

OneToMany 연관관계의 주인이 ONE 쪽에 있을때


* 일대다는(연관관계의 주인이 ONE쪽) 되도록 쓰지말자
* One 쪽은 @JoinColumn 의 name 에 자기 자신의 ID 컬럼
- Many 쪽은 @JoinColumn 의 name 에 One쪽의 ID컬럼을 넣어주되 
- JoinColumn(insertable=false, updatable=false)
- Many 쪽에 JoinColumn 을 안할시 중간 테이블(조인테이블)이 생성된다. (운영이 쉽지않다.)

// 단방향 관계
**(일대다 단방향 관계 관계의 주인이 One 쪽에 있을때) **
	-> 다쪽 테이블에 외래키가 있어야 함  
	-> 단방향 관계에서는 다쪽 엔티티에서 일반적으로 외래키를 관리할 수 없음
	-> 연결 테이블을 사용 하거나 꼭 @JoinColumn, @JoinTable을 사용하여 외래키를 관리
	-> @JoinColumn 없을시 중간테이블(조인테이블) 이 생성됨
	-> 단점으로는 연관관계 관리를 위해 추가로 UPDATE SQL 실행
	-> 일대다 단방향 매핑 보다는 다대일 양방향 매핑을 사용하자
	-> 이런 매핑은 공식적으로 존재X
	-> JoinColumn(insertable=false, updatable=false)
	-> 읽기 전용 필드를 사용해서 양방향 처럼 사용
	-> 다대일 양방향을 사용하자

OneToMany 주의사항


// 주요 속성
mappedBy: 연관관계의 주인 필드를 선택한다.
fetch: 글로벌 페치 전략을 설정한다. (- @ManyToOne=FetchType.EAGER- @OneToMany=FetchType.LAZY)
cascade: 영속성 전이 기능을 사용한다
targetEntity: 연관된 엔티티의 타입 정보를 설정한다. 
이 기능은 거의 사용하지 않는다. 컬렉션을 사용해도
제네릭으로 타입 정보를 알 수 있다.

// 공통 주의점
* CascadeType 설정의 주의
-> CascadeType.ALL 로 설정 시 연관된 모든 엔티티를 삭제 하므로 예상치 못한 문제 발생 가능성
-> CascadeType.REMOVE로 설정하면 연관된 엔티티만 삭제 하므로 안전 (필요한 CascadeType만 설정)

// 양방향 관계

* 반듯이 연관관계의 주인을 설정해서 데이터의 일관성을 유지

* ManyToOne 쪽에서 mappedBy 속성을 사용하여 양방향 관계를 설정

* 양방향 관계에서는 양쪽 엔티티에 대한 참조를 각각 설정해야 함
-> 연관 관계 편의 메서드를 구현하는 것이 좋음

* CascadeType 설정에 주의
-> CascadeType.PERSIST와 CascadeType.MERGE 를 함께 사용하면 문제가 발생
	-> Reason: 양방향 관계에서도 연관된 엔티티를 두 번 저장하기 때문
	-> 결론: CascadeType.REMOVE 로 설정 하는 것이 안전

// 단방향 관계

* 다쪽 테이블에 외래키가 있어야 함
-> 이때 키 생성 전략을 GenerationType.AUTO로 설정 하면 예상치 못한 문제 발생 가능성 존재
	-> Reason: 자동 생성 된 키를 다쪽 엔티티에 저장하면, 같은 키 값을 가진 엔티티가 중복 생성
		-> 일대다 단방향은 키 생성 전략을 GenerationType.IDENTITY나 GenerationType.SEQUENCE로

* CascadeType 설정에 주의
-> CascadeType.PERSIST와 CascadeType.MERGE 를 함께 사용하면 문제가 발생
	-> Reason: 단방향 관계의 다 쪽 엔티티에서 외래키를 관리하지 못하기 때문
	-> 결론: CascadeType.REMOVE 로 설정 하는 것이 안전

ManyToMany

* 관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없음
* 연결 테이블을 추가해서 일대다, 다대일 관계로 풀어내야함
* 객체는 컬렉션을 사용해서 객체 2개로 다대다 관계 가능
* @ManyToMany 사용
* @JoinTable로 연결 테이블 지정
* 다대다 매핑: 단방향, 양방향 가능

// 다대다 매핑의 한계
* 편리해 보이지만 실무에서 사용X
* 연결 테이블이 단순히 연결만 하고 끝나지 않음
* 주문시간, 수량 같은 데이터가 들어올 수 있음
// 다대다 한계 극복
* 연결 테이블용 엔티티 추가(연결 테이블을 엔티티로 승격)
* @ManyToMany -> @OneToMany, @ManyToOne

*** 실전에선 사용하지 말자 ***

Entity

* DB의 id 컬럼이 AUTO_INCREMENT 를 사용 함에도 엔티티의 id에 @GeneratedValue 를 사용하는 이유
-> JPA 가 DB마다 다른 ID 생성 방식을 처리 하도록 @GeneratedValue 를 사용
-> JPA 가 Persist 할 때 생성 해줌

* id 컬럼을 직접 지정하게 되면 ( 저장 )
-> save JPA 의 save 매서드를 잘 보면 새로운 객체인지 판단하고
em.persist, em.merge 둘 중 하나를 해 줌 보통은 @GeneratedValue 를 사용해
id 컬럼이 비어 있다고 판단해 em.persist 해 주지만
직접 id 컬럼을 넣게 되면 select 쿼리를 날려 객체를 조회하고 없으면 insert 해줌
	-> 비효율 발생
		-> Entity 에 implements Persistable<id 의 타입> 를 하고 추가 소스로 해결
		-> @EntityListeners(AuditingEntityListener.class) 도 엔티티에 추가
			->
					@CreatedDate
			    private LocalDateTime createdDate; 
					@Override
				  public boolean isNew() {
		       return createdDate == null;
		    }

			->  @CreatedDate 는 em.persist 후에 생성 되는걸 이용 한 것 (isNew 가 커스텀 되어있음)
					@Transactional (JPA 에서 제공하는 소스 참고하면 isNew 를 호출하는게 보임)
					@Override
					public <S extends T> S save(S entity) {
				
						Assert.notNull(entity, "Entity must not be null");
				
						if (entityInformation.isNew(entity)) {
							em.persist(entity);
							return entity;
						} else {
							return em.merge(entity);
						}
					}
				

JoinColumn