오늘 진행한 학습 요약
1. Spring 심화 2주차
- 1 : N 연관관계
- 1 : 1 연관관계
- N : M 연관관계
- 상속관계 매핑
- Proxy
- 지연로딩, 즉시로딩
- 영속성 전이
- JPA와 Transaction
2. 알고리즘 코드카다 Day43(작성 생략)
CodingTest Git-hub 링크 : https://github.com/chews26/CodingTest
학습 정리
1. Spring 심화 2주차
1 : N 연관관계
- 연관관계 매핑
- JPA 연관관계 매핑을 통해 데이터베이스 테이블 간의 관계를 객체 지향적으로 표현하여 엔티티 클래스들 간의 관계를 설정
- JPA를 통해 연관관계를 매핑하면 SQL을 직접 작성하지 않고도 객체 간의 관계를 활용하여 쉽게 데이터를 조회하고 조작
- 연관관계 매핑
- N:1, 1:N, 1:1, N:M 연관관계
- N:1 : @ManyToOne
- 1:N : @OneToMany
- 1:1 : @OneToOne
- N:M : @ManyToMany
- 단방향, 양방향 연관관계
- 테이블
- 외래 키(FK) 하나로 모든 테이블 JOIN이 가능
- 객체
- 외래키가 있는 객체만 참조가 가능
- 단방향
- 참조용 필드가 양쪽에 있는 경우 양방향
- 단방향 두개로 이루어진 것이 양방향
- 외래키가 있는 객체만 참조가 가능
- 테이블
- 연관관계의 주인
- 외래 키(FK)를 관리하는 객체
- 주인이 아닌 경우 조회만 가능
- N:1, 1:N, 1:1, N:M 연관관계
- 연관관계 매핑
- 1 : N 단방향
- 한 엔티티가 @OneToMany를 통해 여러 엔티티와 관계를 맺는 경우
- 연관관계의 주인은 1에서 가지고 있음
- @JoinColumn 을 사용하지 않으면 중간 테이블 방식을 사용하므로 사용 필수
- 설계가 복잡해져도 N:1 양방향 매핑을 사용하면 관리하기 쉬움
- 1 : N 양방향
- 양방향 연관 관계는 하나의 엔티티가 다른 엔티티와 관계를 맺고 그 반대 방향에서도 서로 참조가 가능하도록 설정한 관계
- 연관관계의 주인이 되지 않도록 insertable = false, updatable = false 설정
- 1:N 단방향과 같은 이유로 N:1 양방향을 쓰면된다.
1 : 1 연관관계
- 1 : 1 단방향
- 두 Entity가 @OneToOne 을 통해 서로 관계를 맺는 경우
- 1 : 1 양방향
- N:1 양방향 연관관계와 유사
- 1:1 연관관계 양방향
- 대상 테이블에 외래 키 양방향
- 1 : 1 연관관계 외래 키
- 1:1 연관관계에서 외래 키는 양쪽 모두가 관리할 수 있음
- 둘중 어떤 테이블을 사용해도 무방
- 단, 테이블은 한번 만들어지면 변경이 어려움
N : M 연관관계
- N : M 연관관계
- 두 Entity가 @ManyToMany를 통해 서로 다수의 관계
- 관계형 데이터베이스는 N:M 연관관계를 구현할 수 없다
- 중간 테이블을 추가해서 1:N, N:1 관계로 설정하면 된다.
- N : M 단방향, 양방향
- @OneToOne 양방향 처럼 동작하지만 중간 테이블이 생성된다.
- N : M 매핑의 문제점
- @ManyToMany로 N:M 연관관계 설정을 하게되면 편리해 보이지만 실제로 사용하기 까다롭다.
- 실제 설계에서는 level, license 와 같은 추가적인 데이터가 필요하다.
- 중간 테이블이 숨겨져 있어서 생각하지 못한 SQL Query가 실행된다.
- 두 id 를 묶어서 PK로 설정된다.
상속관계 매핑
- 테이블 전략
- JPA에서 엔티티 상속 구조를 데이터베이스 테이블에 매핑하는 방법
- JPA는 엔티티의 상속 구조를 처리하기 위해 3가지의 테이블 전략을 제공
- 각각의 전략은 데이터 저장 방식과 성능에 차이가 있으므로 프로젝트의 요구사항에 맞게 선택
- 상속관계 매핑 구현방법
- 조인 전략
- 단일 테이블 전략
- 구현 클래스
- JPA의 테이블 전략
- JPA는 모든 전략으로 테이블을 구현할 수 있도록 지원
- Annotation
- @Inheritance(strategy = InheritanceType.${전략})
- @DiscriminatorColumn(name = "dtype")
- @DiscriminatorValue("${값}")
- JPA의 조인 전략에서 @DiscriminatorColumn을 선언하지 않으면 DTYPE 컬럼이 생성되지 않는다.
- JOIN을 통해 테이블을 구분할 수 있지만, DTYPE 컬럼을 넣어주는 것이 명확
- Annotation
- JOINED
- 장점
- 테이블 정규화
- 외래 키 참조 무결성
- 저장공간 효율
- 단점
- 조회시 JOIN을 많이 사용.
- 데이터 저장시 INSERT SQL 이 2번 호출.
- SQL Query가 복잡하여 성능이 저하.
- 장점
- SINGLE_TABLE
- 장점
- JOIN을 사용하지 않음.
- 실행되는 SQL이 단순.
- 단점
- 자식 Entity가 매핑한 컬럼은 모두 null을 허용
- 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있음.
- 상황에 따라서 조회 성능이 오히려 느려질 수 있음.
- 장점
- TABLE_PER_CLASS
- 테이블끼리 연관짓기 힘듬
- 사용하지 않는것을 권장.
- 장점
- 자식 클래스를 명확하게 구분해서 처리.
- not null 제약조건 사용이 가능.
- 단점
- 여러 자식 테이블을 함께 조회할 때 성능이 느림.
- 부모 객체 타입으로 조회할 때 모든 테이블을 조회.
- JPA는 모든 전략으로 테이블을 구현할 수 있도록 지원
Proxy
- Entity 조회
- em.getReference()
- JPA의 EntityManager에서 제공하는 메서드로 특정 엔티티의 프록시 객체를 반환
- 지연 로딩(Lazy Loading)을 활용해 데이터베이스 조회를 미루고 실제로 엔티티의 속성에 접근할 때만 데이터베이스를 조회하도록 함
- em.find()
- 데이터베이스를 통해 실제로 저장된 Entity 를 조회한다.
- em.getReference()
- 데이터베이스에 저장된 Entity가 아닌 가짜 Entity 객체를 조회
- proxyTutor.getName()
- 실제 값이 사용되는 시점에 SQL Query가 실행
- proxyTutor
- Hibernate가 만드는 Proxy 객체
- em.getReference()
- Proxy
- JPA에서 엔티티 객체의 지연 로딩(Lazy Loading)을 지원하기 위해 사용하는 대리 객체
- 실제 엔티티 객체를 생성하거나 데이터베이스에서 값을 읽어오지 않고도 엔티티의 참조를 사용할 수 있음
- 최초로 사용(실제 Entity에 접근)할 때 한 번만 초기화된다.
- 프록시 객체를 통해 실제 Entity에 접근할 수 있다.
- em.getReference() 호출 시 영속성 컨텍스트에 Entity가 존재하면 실제 Entity가 반환
- 준영속 상태에서 프록시를 초기화하면 LazyInitializationException 예외가 발생한다.
지연로딩, 즉시로딩
- Lazy Loading
- 데이터를 실제로 사용할 때 데이터베이스에서 조회하는 방식
- fetch 속성 사용
- FetchType.LAZY : 지연로딩
- 지연로딩을 사용하면 Proxy 객체를 조회한다.
- 연관된 객체(Company)를 매번 함께 조회하는것은 낭비인 경우에 사용
- Eager Loading
- 엔티티를 조회할 때 연관된 데이터까지 모두 한 번에 로드하는 방식
- fetch 속성 사용
- FetchType.EAGER : 즉시 로딩
- Proxy 객체를 조회하지 않고 한 번에 연관된 객체까지 조회한다.
- 관된 객체(Company)를 매번 함께 조회하는것이 효율적인 경우에 사용
- 즉시 로딩 주의점
- 즉시 로딩(Eager Loading)을 사용하면 개발자가 예상하지 못한 SQL이 실행
- JPQL에서 N+1 문제가 발생
- 꼭 필요한 경우가 아니라면 지연 로딩을 사용
영속성 전이
- Cascade
- 영속성 전이(Cascade)란 JPA에서 특정 엔티티를 저장, 삭제 등의 작업을 할 때 연관된 엔티티에도 동일한 작업을 자동으로 적용하도록 설정하는 기능
- CascadeType.ALL
- 사용 방법과 주의점
- 영속성 전이(Cascade)
- 단순히 Entity를 저장, 삭제할 때 연관된 Entity에도 동일한 작업을 적용
- 속성 종류
- ALL : 모두 적용
- PERSIST : 영속
- REMOVE : 삭제
MERGEREFRESHDETACH
- 단일 Entity에 완전히 종속적인 경우 생명주기가 같다면 사용
- 영속성 전이(Cascade)
- 고아 객체
- JPA에서 부모 엔티티와의 연관관계가 끊어진 자식 엔티티
- 부모 엔티티와 연관관계가 끊어진 자식 Entity를 자동으로 삭제
- orphanRemoval = true 사용
- 기본 값 : false
- CascadeType.REMOVE와 비슷하게 동작
- CascadeType.ALL과 orphanRemoval=true 를 함께 사용하는 경우 부모 Entity를 통해서 자식 Entity의 생명주기를 관리할 수 있다
- JPA에서 부모 엔티티와의 연관관계가 끊어진 자식 엔티티
JPA와 Transaction
- 트랜잭션 전파
- 하나의 트랜잭션이 다른 트랜잭션 내에서 어떻게 동작할지를 결정하는 규칙
- 여러 개의 트랜잭션이 포함된 시스템에서 특정 작업이 다른 작업에 어떻게 영향을 미칠지를 정의
- 현재 클래스의 트랜잭션과 다른 클래스의 트랜잭션을 교통정리
- 트랜잭션이 여러 계층 또는 메서드에서 어떻게 처리될지 정의한다.(@Transactional)
- propagation 속성을 통해 트랜잭션의 동작 방식을 제어
- 다양한 비즈니스 요구 사항에 맞춰 복잡한 트랜잭션 흐름을 유연하게 설계할 수 있도록 돕는다.
- 데이터 무결성과 비지니스 로직의 안정성을 보장할 수 있다.
- 트랜잭션 전파 종류
- propagation 속성
- REQUIRED(Default)
- 기존 트랜잭션이 있다면 기존 트랜잭션을 사용
- 기존 트랜잭션이 없다면 트랜잭션을 새로 생성
- REQUIRES_NEW
- 항상 새로운 트랜잭션을 시작하고, 기존의 트랜잭션은 보류
- 두 트랜잭션은 독립적으로 동작
- SUPPORTS
- 기존 트랜잭션이 있으면 해당 트랜잭션을 사용
- 기존 트랜잭션이 없으면 트랜잭션 없이 실행
- NOT_SUPPORTED
- 기존 트랜잭션이 있어도 트랜잭션을 중단하고 트랜잭션 없이 실행
- MANDATORY
- 기존 트랜잭션이 반드시 있어야 함
- 트랜잭션이 없으면 실행하지 않고 예외를 발생
- NEVER
- 트랜잭션 없이 실행되어야 함
- 트랜잭션이 있으면 예외를 발생
- NESTED
- 현재 트랜잭션 내에서 중첩 트랜잭션을 생성
- 중첩 트랜잭션은 독립적으로 롤백할 수 있음
- 기존 트랜잭션이 Commit되면 중첩 트랜잭션도 Commit
- REQUIRED(Default)
- propagation 속성
- 트랜잭션 전파 종류