skarltjr / Memory_Write_Record

나의 모든 학습 기록
0 stars 0 forks source link

JPA #10

Open skarltjr opened 3 years ago

skarltjr commented 3 years ago

김영한님 강의를 통해 학습

jpa . 왜 사용하는가

  1. 계층형 아키텍쳐 -> 결국 계층 분할이 어렵다.

객체를 sql중심적으로가 아닌 자바 컬렉션에 저장하듯이 db에 저장할 순 없는가?

결론적으로 왜 jpa를 사용하는가?

JPA의 성능 최적화 기능

  1. 1차 캐시와 동일성(identity) 보장 • 같은 트랜잭션 안에서는 동일한 엔티티를 반환
    String memberId = "100";
    Member m1 = jpa.find(Member.class, memberId); //SQL
    Member m2 = jpa.find(Member.class, memberId); //캐시
    println(m1 == m2) //true
  2. 트랜잭션을 지원하는 쓰기 지연(transactional write-behind) • 트랜잭션을 커밋할 때까지 insert sql을 모은 후 commit할 때 한 번에 보낸다
  3. 지연 로딩(Lazy Loading) • 지연 로딩: 객체가 실제 사용될 때 로딩 • 즉시 로딩: JOIN SQL로 한번에 연관된 객체까지 미리 조회

jpa는 특정 db에 종속적이지 않다.

jpa의 모든 데이터 변경은 트랜잭션 안에서 수행

skarltjr commented 3 years ago

JPA에서 매우 중요한 영속성 - 영속성 컨텍스트

엔티티의 생명주기

-emf = entitymanagerFactory 화면 캡처 2021-01-23 012419

영속성 컨텍스트의 이점

• 1차 캐시 - 엔티티를 조회 시 조회하고자하는 엔티티가 이미 영속성 컨텍스트에 올라가있다면 db까지 접근하지 않고 영속성컨텍스트에 있는 것을 반환 -> 효율적 • 동일성(identity) 보장 -> 1차 캐쉬 덕분에 추가적으로 동일성도 보장 • 트랜잭션을 지원하는 쓰기 지연 (transactional write-behind) - commit을 할 때 한번에 • 변경 감지(Dirty Checking)

Flush

Flush는 영속성 컨텍스트를 비우는것이 아니라 영속성컨텍스트의 변경내용을 데이터베이스에 동기화

skarltjr commented 3 years ago

엔티티 매핑

@Entity- jpa가 관리하는 클래스

skarltjr commented 3 years ago

객체와 테이블 연관관계의 차이를 이해하자 / 객체의 참조와 테이블의 외래키 매핑

skarltjr commented 3 years ago

다양한 연관관계 매핑

연관관계 매핑 시 고려사항

단방향,양방향 / 테이블과 객체의 차이

연관관계의 주인


다대일

일대다

• 일대다 단방향은 일대다(1:N)에서 일(1)이 연관관계의 주인 • 테이블 일대다 관계는 항상 다(N) 쪽에 외래 키가 있음 • 객체와 테이블의 차이 때문에 반대편 테이블의 외래 키를 관리하 는 특이한 구조 • @JoinColumn을 꼭 사용해야 함. 그렇지 않으면 조인 테이블 방식을 사용함(중간에 테이블을 하나 추가함)

일대일

다대다 ★

다대다의 한게

화면 캡처 2021-01-23 162251

• 연결 테이블이 단순히 연결만 하고 끝나지 않음 • 주문시간, 수량 같은 데이터가 들어올 수 있음 - 추가적인 필드 변경이 x된다 = 외래키가 연결테이블에 존재하기때문

다대다 한계 극복

화면 캡처 2021-01-23 162517 화면 캡처 2021-01-23 162535 화면 캡처 2021-01-23 162546

skarltjr commented 3 years ago

고급매핑

@Inheritance(strategy=InheritanceType.XXX)

• JOINED: 조인 전략 • SINGLE_TABLE: 단일 테이블 전략 • TABLE_PER_CLASS: 구현 클래스마다 테이블 전략 -> 사용 xxxxxx + • @DiscriminatorColumn(name=“DTYPE”) • @DiscriminatorValue(“XXX”)

  1. 조인전략 화면 캡처 2021-01-23 164219

    • 장점 : 테이블 정규화 & 외래 키 참조 무결성제약조건활용가능 & 저장공간 효율화
    • 단점 : 조회시 조인을 많이사용하게된다 -> 성능저하 & 조회쿼리가 복잡하고 데이터저장시 insert쿼리 2방이 나간다
  2. 단일 테이블 전략 화면 캡처 2021-01-23 164427

    • 장점 : 조인이 필요없으므로 조회성능이 빠르고 조회쿼리가 단순하다
    • 단점 : 자식 엔티티가 매핑한 컬럼은 모두 null허용 & 단일테이블에 모든것을 저장하기떄문에 테이블이 커지고 오히려 성능이 저하될 수 있다.

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="dtype")
@Getter @Setter
public abstract class Item
{
    @Id @GeneratedValue
    @Column(name="item_id")
    private Long id;

    private String name;
    private int price;
    private int stockQuantity;

}
@Entity
@DiscriminatorValue("B")//싱글테이블이라서 무보클래스에서 dtype했으니 뭘로 구분할지
@Getter @Setter
public class Book extends Item{
    private String author;
    private String isbn;

}
skarltjr commented 3 years ago

프록시와 연관관계 관리- 지연로딩

프록시의 특징

• 실제 클래스를 상속 받아서 만들어짐 • 실제 클래스와 겉 모양이 같다. • 사용하는 입장에서는 진짜 객체인지프록시 객체인지 구분하지 않고 사용하면 됨(이론상) • 프록시 객체는 실제 객체의 참조(target)를 보관 • 프록시 객체를 호출하면 프록시 객체는 실제 객체의 메소드 호출 • 프록시 객체는 처음 사용할 때 한 번만 초기화 • 프록시 객체를 초기화 할 때, 프록시 객체가 실제 엔티티로 바뀌는 것은 아님, 초 기화되면 프록시 객체를 통해서 실제 엔티티에 접근 가능 • 프록시 객체는 원본 엔티티를 상속받음, 따라서 타입 체크시 주의해야함 (== 비 교 실패, 대신 instance of 사용)

왜 , 언제 프록시가 사용되는가?

화면 캡처 2021-01-23 171906

N+1 - > 즉시로딩

추가로

@NamedEntityGraph(name = "Account.withTagsAndZones",attributeNodes = {
        @NamedAttributeNode("tags"),
        @NamedAttributeNode("zones")
})
@Entity @Builder
@Getter @Setter
@NoArgsConstructor @AllArgsConstructor
@EqualsAndHashCode(of = "id")
public class Account {

    @Id @Column(name = "account_id")
    @GeneratedValue
    private Long id;

    @Email
    @Column(unique = true)
    private String email;

    @Column(unique = true)
    private String nickname;

    private String password;
    ..
   ..
   ..
    @ManyToMany
    private Set<Tag> tags = new HashSet<>();
    @ManyToMany
    private Set<Zone> zones = new HashSet<>();

화면 캡처 2021-01-23 172528

영속성전이 cascade

• 특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만들도 싶을 때

skarltjr commented 3 years ago

값 타입

기본 값 타입은 생명주기를 엔티티에 의존 & 값 타입은 공유 x

임베디드 타입 = 복합 값 타입 - 새로운 값 타입을 직접 정의가능

화면 캡처 2021-01-23 174305

• @Embeddable: 값 타입을 정의하는 곳에 표시 • @Embedded: 값 타입을 사용하는 곳에 표시 • 기본 생성자 필수

임베디드 타입의 장점 - 매핑하는 테이블은 변하지않는다.

• 재사용 • 높은 응집도 • Period.isWork()처럼 해당 값 타입만 사용하는 의미 있는 메소드를 만들 수 있음 • 임베디드 타입을 포함한 모든 값 타입은, 값 타입을 소유한 엔티티에 생명주기를 의존함

임베디드 타입의 단점

그러면 어떻게 설정해야하는가 ?? = 불변객체 - 생성자 ok / setter x

정리

화면 캡처 2021-01-23 175111

skarltjr commented 3 years ago

객체지향 쿼리언어 JPQL / Querydsl - 네이티브sql JPA Criteria...

skarltjr commented 3 years ago

https://jojoldu.tistory.com/457를 참고하여 학습 후 적용

문제상황 : jpa의 MultipleBagFetchException

-> 여행 예산 프로젝트 검색 기능 구현 중 발생 -> 하나의 여행 계획(plan)에는 여러개의 post가 존재한다 -> 검색은 공개된 plan 중 plan의 타이틀에 keyword가 포함된 모든 여행들을 검색 ( 가격 검색은 현재 상황에서 제외) -> 여기서 plan 하위의 여러 post 중 post의 타이틀이 keyword를 포함한 plan도 검색에 포함시켜보고 싶었다. -> plan과 post 양방향 건 후 -> 모두 fetch join 적용

-> 그런데 plan 엔티티의 planTagList와 posts 둘 다 ~ToMany

-> jpa에서 fetch join은 ~ToMany의 경우 1개만 가능 -> 문제 해결을 위해 spring.jpa.properties.hibernate.default_batch_fetch_size=1000를 사용 후 잘 동작하는 것을 확인 화면 캡처 2021-06-29 222014

-> 당연히 나머지 부분에서는 가능한 최적화는 수행하기! (위처럼 2개 이상의 ~ToMany에 fetch join이 필요할 때 batch_fetch_size 설정 후 둘 중 좀 더 효율적이라고 판단되는 곳에 fetch join을 설정하고 나머지는 설정을 활용하는 쪽으로.) -> 이 때 여기서 나는 planTag + tag쪽에 fetch join을 설정하는 것이 더 좋다고 판단하여

skarltjr commented 2 years ago

Transactional(readOnly=true)

skarltjr commented 2 years ago

JPA조인 전략

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "team_id", nullable = false)
skarltjr commented 2 years ago

JPA인덱스 정의

@Entity
@Table(
    name = "files",
    indexes = [Index(name = "file_url_index", columnList = "url")]
)
class File(
    @field:Id
    @field:GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long? = null,

    @field:Column(name = "url", nullable = false)
    val url: String,

    @field:Column(name = "ext", nullable = false)
    val ext: String
) {
}
skarltjr commented 2 years ago

코틀린에서의 jpa와 lazy loading