Entity 는 데이터베이스 테이블과 매핑되는 클래스.
객체를 데이터베이스의 테이블에 매핑하여 영속성을 관리할 수 있게 해준다.
@Entity 어노테이션 사용.
OneToMany 관계는 하나의 엔티티가 여러 개의 다른 엔티티와 관계를 맺는 경우를 의미.
⇒ 일대다 관계를 의미 !
예를 들어, 하나의 Author(작성자)가 여러 개의 Post(게시글)를 가질 수 있는 경우.
양방향 관계에서, 연관 관계의 주인을 지정.
연관 관계의 주인이란 JPA에서 관계를 맺고 있는 두 엔티티 중(양방향 관계) 관계를 관리 (데이터베이스의 외래 키를 업데이트)하는 책임을 가지는 쪽을 의미.
관계의 주인이 아닌 엔티티에서 mappedBy 속성을 통해 관계의 주인이 되는 필드를 지정한다.
@OneToMany(mappedBy = "author")
private List<Post> posts;
// Author 클래스에 Post 를 mappedBy 로 선언해줌.
Many 쪽 엔티티의 필드 이름을 값으로 설정.Many쪽임에 유의.엔티티 상태 변화(예: 저장, 삭제)를 연관된 엔티티에 전파하는 방식을 지정.
예를 들어, 부모 엔티티를 저장하거나 삭제할 때 자식 엔티티도 함께 저장되거나 삭제되도록 할 수 있다.
cascade를 사용하면, 부모 엔티티를 저장할 때 자동으로 연관된 자식 엔티티들도 함께 저장함으로서 코드를 간결하게 만들고, 엔티티 간의 관계를 명확하게 표현할 수 있다.
더티체킹과 비교하면 더티체킹은 변경사항에 대해서만 반영하지만, cascade옵션을 통해서는 연관 엔티티의 신규 사항 추가 가능
CascadeType.PERSIST: 부모 엔티티를 저장할 때 자식 엔티티도 함께 저장.
CascadeType.MERGE: 부모 엔티티를 병합할 때 자식 엔티티도 함께 병합합니다.
CascadeType.REMOVE: 부모 엔티티를 삭제할 때 자식 엔티티도 함께 삭제합니다.
CascadeType.REFRESH: 부모 엔티티를 새로 고침할 때 자식 엔티티도 함께 새로고침합니다.
CascadeType.DETACH: 부모 엔티티를 분리할 때 자식 엔티티도 함께 분리합니다.
CascadeType.ALL: 모든 작업을 자식 엔티티에도 전파합니다.
// cascade 단일 지정
@OneToMany(mappedBy = "author", cascade = CascadeType.ALL)
private List<Post> posts;
// cascade 복수 지정
@OneToMany(mappedBy = "author", cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE})
private List<Post> posts;

CascadeType.ALL 붙여준 뒤 → Post 가 있어도 Author 삭제가 가능하며, 자동으로 함께 삭제된다.
.getPosts() 를 위해 Post 선언 시 List<Post> posts 로 변경 후 진행.
가입과 동시에 가입 환영 인사가 작성되도록 설정하였다.
// List<Post> posts 로 선언해준 뒤, dto 파일에서 가입 시 초기화 진행
public Author toEntity(){
Author author = Author.builder().password(this.password).name(this.name)
.email(this.email).role(this.role)
.posts(new ArrayList<>())
.build();
return author;
}


JPA → 영속성 컨텍스트 ⇒ 중간(임시 저장소 개념)에서 Author 보내고 받고(생성 후), Post 에 author id 보내고 받고 → 임시 저장해서 순차적으로 처리함.
⇒ 이게 안되면 author 저장하고 post 저장하는 걸로 하나 하나 작동하게 해야 함 !
fetch는 가져오다라는 의미를 가진 단어로서, 엔티티 또는 엔티티의 연관된 컬렉션 및 엔티티를 데이터베이스로부터 어떻게 로드할지 결정하는 전략.FetchType.LAZY는 지연 로딩, FetchType.EAGER는 즉시 로딩@OneToMany와 OneToOne은 default설정이 FetchType.LAZY.@ManyToOne은 default가 FetchType.EAGER.Post 에서 멤버를 가져올 때 @ManyToOne 으로 설정되었기 때문에 default 값인 EAGER 로 설정이 되어있다.



@OneToMany 로 설정된 Member 에서의 조회는 lazy 가 적용되어 쿼리가 적게 실행되었고,
@ManyToOne 로 설정된 Post 에서의 조회는 실행되는 쿼리와 출력이 많은 것을 알 수 있다 !
OneToMany 는 로딩(fetch) 전략이 lazy(지연로딩)
→ Member 도 관련있는 post 가 있음에도 불구하고 쿼리를 내보내지 않는다.
OneToOne 또는 ManyToOne 은 로딩 전략이 eager(즉시 로딩)
post 를 통해 조회했을 때 N+1 문제가 발생하게 된다.
해결책은 lazy 로 직접 설정해주는 것 !


lazy 로 FetchType 을 지정해준 뒤, 이전에 post 만큼 나왔던 쿼리문이 나오지 않는 것을 확인할 수 있었다.
ManyToOne 관계는 여러 개의 엔티티가 하나의 다른 엔티티와 관계를 맺는 경우를 의미.
하나의 Author 가 여러 개의 Post 를 가질 때, 각 Post 는 하나의 Author 에 속한다 !
외래 키(FK)를 매핑할 때 사용함.
JPA 에서 엔티티 간의 관계를 정의할 때 사용되는 어노테이션.
// 연관 관계의 주인은 FK 가 있는 Post.
@ManyToOne(fetch = FetchType.LAZY) //참조 안 하면 안 나가게.
@JoinColumn(name = "author_id", nullable = false)
private Author author;
name 속성으로 외래 키 컬럼의 이름을 지정.
nullable 속성으로 nullable 또는 not null지정, unique 설정도 가능 !
@OneToMany와 동일하게 상태 변화를 전파하는 방식을 지정하는 것.
일반적으로는 부모엔티티에 설정한다.
@Repository
public interface PostRepository extends JpaRepository<Post, Long>{
// JPQL 문법.
// 네이밍룰을 통한 방식이 아닌 메서드 생성.
// select p.*, a.* from post p left join author a on p.author_id=a.id;
@Query("select p from Post p left join fetch p.author")
List<Post> findAllFetch();
findAllFetch()메서드는 모든 Post 객체를 가져오면서, 각 Post와 연관된 Author 객체도 함께 가져온다.
JPQL 쿼리: select p from Post p left join fetch p.author
left join fetch p.author: Post 객체와 연관된 Author 객체를 함께 가져옴.
// fetch 가 아닌 그냥 left join 이라면 ?
// select p.* from post p left join author a on p.author_id=a.id;
// => a 를 안 가져 옴 ! -> left join 을 뭐하러 하나요..?
// author 객체를 통한 조건문으로 post 를 filtering 할 때 사용함. -> 이름이 hong 인 사람의 post 를 가져오겠다!
// -> N+1 문제가 똑 같 이 발 생.
@Query("select p from Post p left join p.author")
List<Post> findAllNOFetch();
}
findAllNOFetch()메서드는 모든 Post 객체를 가져온다.
Post와 연관된 Author 객체와 조인은 하지만, Author 객체의 데이터는 가져오지 않는다 !
JPQL 쿼리: select p from Post p left join p.author
left join p.author: Post 객체와 연관된 Author 객체와 조인하지만, Author 객체의 데이터는 가져오지 않음
findAllFetch(): Post와 Author를 함께 가져오므로, 데이터베이스에서 한 번에 필요한 모든 데이터를 가져옴 → 이를 통해 성능을 향상시킬 수 있다!
하지만 필요한 데이터가 많아질 수 있다. ( 다 가져왔으니까 ! )
findAllNOFetch(): Post만 가져오고 Author는 가져오지 않는다.
데이터베이스에서 필요한 데이터만 가져옴 → 쿼리가 더 가볍다.
하지만 나중에 Author 데이터를 가져와야 한다면 추가 쿼리가 발생할 수 있다.
select post0_.id as id1_1_0_, author1_.id as id1_0_1_, post0_.author_id as author_i6_1_0_, post0_.contents as contents2_1_0_, post0_.created_time as created_3_1_0_, post0_.title as title4_1_0_, post0_.update_time as update_t5_1_0_, author1_.created_time as created_2_0_1_,
author1_.updated_time as updated_3_0_1_,
author1_.email as email4_0_1_, author1_.name as name5_0_1_,
author1_.password as password6_0_1_, author1_.role as role7_0_1_ from post
post0_ left outer join author author1_ on post0_.author_id=author1_.id
마지막 줄에서 left outer join 확인 됨 !
fetch join 쿼리 작성 ⇒ 그냥 join 은 안된다!(left join fetch 로 작성함) → 그냥 join 은 할 때마다 join 해주고 필요하면 또 해주고 . . → fetch join 은 임시 저장해두듯이 한번에 묶기. ⇒ 이것도 JPA 에서만 가능 ⇒ 영속성 컨텍스트