🚀
Incheol's TECH BLOG
  • Intro
  • Question & Answer
    • JAVA
      • JVM
      • String, StringBuffer, StringBuilder
      • JDK 17일 사용한 이유(feat. JDK 8 이후 훑어보기)
      • 스택 오버 플로우(SOF)
      • 블럭킹 | 논블럭킹 | 동기 | 비동기
      • 병렬처리를 이용한 이미지 리사이즈 개선
      • heap dump 분석하기 (feat. OOM)
      • G1 GC vs Z GC
      • JIT COMPILER
      • ENUM
      • STATIC
      • Thread(쓰레드)
      • hashCode()와 equals()
      • JDK 8 특징
      • break 와 continue 사용
      • STREAM
      • Optional
      • 람다와 클로저
      • Exception(예외)
      • Garbage Collector
      • Collection
      • Call by Value & Call by Reference
      • 제네릭(Generic)
    • SPRING
      • Spring 특징
      • N+1 문제
      • 테스트 코드 어디까지 알아보고 오셨어요?
      • 테스트 코드 성능 개선기
      • RestTemplate 사용시 주의사항
      • 동시성 해결하기(feat. TMI 주의)
      • redisson trylock 내부로직 살펴보기
      • DB 트래픽 분산시키기(feat. Routing Datasource)
      • OSIV
      • @Valid 동작 원리
      • mybatis @Builder 주의사항
      • 스프링 클라우드 컨피그 갱신 되지 않는 이슈(feat. 서비스 디스커버리)
      • ImageIO.read 동작하지 않는 경우
      • 카프카 transaction 처리는 어떻게 해야할까?
      • Spring Boot 특징
      • Spring 5 특징
      • JPA vs MyBatis
      • Filter와 Interceptor
      • 영속성 컨텍스트(Persistence Context)
      • @Transactional
      • @Controlleradvice, @ExceptionHandler
      • Spring Security
      • Dispatcher Servlet
      • @EnableWebMvc
      • Stereo Type(스테레오 타입)
      • AOP
      • JPA Repository 규칙
    • DATABASE
      • Database Index
      • SQL vs NoSQL
      • DB 교착상태
      • Isolation level
      • [MySQL] 이모지 저장은 어떻게 하면 좋을까?
      • SQL Hint
      • JOIN
    • INFRA
      • CLOUD COMPUTING
      • GIT
      • DOCKER
      • 카프카 찍먹하기 1부
      • 카프카 찍먹하기 2부 (feat. 프로듀서)
      • 카프카 찍먹하기 3부 (feat. 컨슈머)
      • JENKINS
      • POSTMAN
      • DNS 동작 원리
      • ALB, NLB,ELB 차이는?
      • 카프카 파티션 주의해서 사용하자
      • DEVOPS
      • JWT
      • OSI 7 Layer
      • MSA
      • 서비스 디스커버리는 어떻게 서비스 등록/해제 하는걸까?
      • 핀포인트 사용시 주의사항!! (feat 로그 파일 사이즈)
      • AWS EC2 도메인 설정 (with ALB)
      • ALB에 SSL 설정하기(feat. ACM)
      • 람다를 활용한 클라우드 와치 알림 받기
      • AWS Personalize 적용 후기… 😰
      • CloudFront를 활용한 S3 성능 및 비용 개선
    • ARCHITECTURE
      • 객체지향과 절차지향
      • 상속보단 합성
      • SOLID 원칙
      • 캡슐화
      • DDD(Domain Driven Design)
    • COMPUTER SCIENCE
      • 뮤텍스와 세마포어
      • Context Switch
      • REST API
      • HTTP HEADER
      • HTTP METHOD
      • HTTP STATUS
    • CULTURE
      • AGILE(Feat. 스크럼)
      • 우리는 성장 할수 있을까? (w. 함께 자라기)
      • Expert Beginner
    • SEMINAR
      • 2022 INFCON 후기
        • [104호] 사이드 프로젝트 만세! - 기술만큼 중요했던 제품과 팀 성장기
        • [102호] 팀을 넘어서 전사적 협업 환경 구축하기
        • [103호] 코드 리뷰의 또 다른 접근 방법: Pull Requests vs. Stacked Changes
        • [105호] 실전! 멀티 모듈 프로젝트 구조와 설계
        • [105호] 지금 당장 DevOps를 해야 하는 이유
        • [102호] (레거시 시스템) 개편의 기술 - 배달 플랫폼에서 겪은 N번의 개편 경험기
        • [102호] 서버비 0원, 클라우드 큐 도입으로 해냈습니다!
  • STUDY
    • 오브젝트
      • 1장 객체, 설계
      • 2장 객체지향 프로그래밍
      • 3장 역할, 책임, 협력
      • 4장 설계 품질과 트레이드 오프
      • 5장 책임 할당하기
      • 6장 메시지와 인터페이스
      • 7징 객체 분해
      • 8장 의존성 관리하기
      • 9장 유연한 설계
      • 10장 상속과 코드 재사용
      • 11장 합성과 유연한 설계
      • 12장 다형성
      • 13장 서브클래싱과 서브타이핑
      • 14장 일관성 있는 협력
      • 15장 디자인 패턴과 프레임워크
      • 마무리
    • 객체지향의 사실과 오해
      • 1장 협력하는 객체들의 공동체
      • 2장 이상한 나라의 객체
      • 3장 타입과 추상화
      • 4장 역할, 책임, 협력
    • JAVA ORM JPA
      • 1장 JPA 소개
      • 2장 JPA 시작
      • 3장 영속성 관리
      • 4장 엔티티 매핑
      • 5장 연관관계 매핑 기초
      • 6장 다양한 연관관계 매핑
      • 7장 고급 매핑
      • 8장 프록시와 연관관계 관리
      • 9장 값 타입
      • 10장 객체지향 쿼리 언어
      • 11장 웹 애플리케이션 제작
      • 12장 스프링 데이터 JPA
      • 13장 웹 애플리케이션과 영속성 관리
      • 14장 컬렉션과 부가 기능
      • 15장 고급 주제와 성능 최적화
      • 16장 트랜잭션과 락, 2차 캐시
    • 토비의 스프링 (3.1)
      • 스프링의 이해와 원리
        • 1장 오브젝트와 의존관계
        • 2장 테스트
        • 3장 템플릿
        • 4장 예외
        • 5장 서비스 추상화
        • 6장 AOP
        • 8장 스프링이란 무엇인가?
      • 스프링의 기술과 선택
        • 5장 AOP와 LTW
        • 6장 테스트 컨텍스트 프레임워크
    • 클린코드
      • 1장 깨끗한 코드
      • 2장 의미 있는 이름
      • 3장 함수
      • 4장 주석
      • 5장 형식 맞추기
      • 6장 객체와 자료 구조
      • 9장 단위 테스트
    • 자바 트러블슈팅(with scouter)
      • CHAP 01. 자바 기반의 시스템에서 발생할 수 있는 문제들
      • CHAP 02. scouter 살펴보기
      • CHAP 03. scouter 설정하기(서버 및 에이전트)
      • CHAP 04. scouter 클라이언트에서 제공하는 기능들
      • CHAP 05. scouter XLog
      • CHAP 06. scouter 서버/에이전트 플러그인
      • CHAP 07. scouter 사용 시 유용한 팁
      • CHAP 08. 스레드 때문에(스레드에서) 발생하는 문제들
      • CHAP 09. 스레드 단면 잘라 놓기
      • CHAP 10. 잘라 놓은 스레드 단면 분석하기
      • CHAP 11. 스레드 문제
      • CHAP 12. 메모리 때문에 발생할 수 있는 문제들
      • CHAP 13. 메모리 단면 잘라 놓기
      • CHAP 14. 잘라 놓은 메모리 단면 분석하기
      • CHAP 15. 메모리 문제(Case Study)
      • CHAP 24. scouter로 리소스 모니터링하기
      • CHAP 25. 장애 진단은 이렇게 한다
      • 부록 A. Fatal error log 분석
      • 부록 B. 자바 인스트럭션
    • 테스트 주도 개발 시작하기
      • CHAP 02. TDD 시작
      • CHAP 03. 테스트 코드 작성 순서
      • CHAP 04. TDD/기능 명세/설계
      • CHAP 05. JUnit 5 기초
      • CHAP 06. 테스트 코드의 구성
      • CHAP 07. 대역
      • CHAP 08. 테스트 가능한 설계
      • CHAP 09. 테스트 범위와 종류
      • CHAP 10. 테스트 코드와 유지보수
      • 부록 A. Junit 5 추가 내용
      • 부록 C. Mockito 기초 사용법
      • 부록 D. AssertJ 소개
    • KOTLIN IN ACTION
      • 1장 코틀린이란 무엇이며, 왜 필요한가?
      • 2장 코틀린 기초
      • 3장 함수 정의와 호출
      • 4장 클래스, 객체, 인터페이스
      • 5장 람다로 프로그래밍
      • 6장 코틀린 타입 시스템
      • 7장 연산자 오버로딩과 기타 관례
      • 8장 고차 함수: 파라미터와 반환 값으로 람다 사용
      • 9장 제네릭스
      • 10장 애노테이션과 리플렉션
      • 부록 A. 코틀린 프로젝트 빌드
      • 부록 B. 코틀린 코드 문서화
      • 부록 D. 코틀린 1.1과 1.2, 1.3 소개
    • KOTLIN 공식 레퍼런스
      • BASIC
      • Classes and Objects
        • Classes and Inheritance
        • Properties and Fields
    • 코틀린 동시성 프로그래밍
      • 1장 Hello, Concurrent World!
      • 2장 코루틴 인 액션
      • 3장 라이프 사이클과 에러 핸들링
      • 4장 일시 중단 함수와 코루틴 컨텍스트
      • 5장 이터레이터, 시퀀스 그리고 프로듀서
      • 7장 스레드 한정, 액터 그리고 뮤텍스
    • EFFECTIVE JAVA 3/e
      • 객체 생성과 파괴
        • 아이템1 생성자 대신 정적 팩터리 메서드를 고려하라
        • 아이템2 생성자에 매개변수가 많다면 빌더를 고려하라
        • 아이템3 private 생성자나 열거 타입으로 싱글턴임을 보증하라
        • 아이템4 인스턴스화를 막으려거든 private 생성자를 사용하라
        • 아이템5 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라
        • 아이템6 불필요한 객체 생성을 피하라
        • 아이템7 다 쓴 객체 참조를 해제하라
        • 아이템8 finalizer와 cleaner 사용을 피하라
        • 아이템9 try-finally보다는 try-with-resources를 사용하라
      • 모든 객체의 공통 메서드
        • 아이템10 equals는 일반 규약을 지켜 재정의하라
        • 아이템11 equals를 재정의 하려거든 hashCode도 재정의 하라
        • 아이템12 toString을 항상 재정의하라
        • 아이템13 clone 재정의는 주의해서 진행해라
        • 아이템14 Comparable을 구현할지 고려하라
      • 클래스와 인터페이스
        • 아이템15 클래스와 멤버의 접근 권한을 최소화하라
        • 아이템16 public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라
        • 아이템17 변경 가능성을 최소화하라
        • 아이템18 상속보다는 컴포지션을 사용하라
        • 아이템19 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라
        • 아이템20 추상 클래스보다는 인터페이스를 우선하라
        • 아이템21 인터페이스는 구현하는 쪽을 생각해 설계하라
        • 아이템22 인터페이스 타입을 정의하는 용도로만 사용하라
        • 아이템23 태그 달린 클래스보다는 클래스 계층구조를 활용하라
        • 아이템24 멤버 클래스는 되도록 static으로 만들라
        • 아이템25 톱레벨 클래스는 한 파일에 하나만 담으라
      • 제네릭
        • 아이템26 로 타입은 사용하지 말라
        • 아이템27 비검사 경고를 제거하라
        • 아이템28 배열보다는 리스트를 사용하라
        • 아이템29 이왕이면 제네릭 타입으로 만들라
        • 아이템30 이왕이면 제네릭 메서드로 만들라
        • 아이템31 한정적 와일드카드를 사용해 API 유연성을 높이라
        • 아이템32 제네릭과 가변인수를 함께 쓸 때는 신중하라
        • 아이템33 타입 안전 이종 컨테이너를 고려하라
      • 열거 타입과 애너테이션
        • 아이템34 int 상수 대신 열거 타입을 사용하라
        • 아이템35 ordinal 메서드 대신 인스턴스 필드를 사용하라
        • 아이템36 비트 필드 대신 EnumSet을 사용하라
        • 아이템37 ordinal 인덱싱 대신 EnumMap을 사용하라
        • 아이템38 확장할 수 있는 열거 타입이 필요하면 인터페이스를 사용하라
        • 아이템 39 명명 패턴보다 애너테이션을 사용하라
        • 아이템40 @Override 애너테이션을 일관되게 사용하라
        • 아이템41 정의하려는 것이 타입이라면 마커 인터페이스를 사용하라
      • 람다와 스트림
        • 아이템46 스트림에는 부작용 없는 함수를 사용하라
        • 아이템47 반환 타입으로는 스트림보다 컬렉션이 낫다
        • 아이템48 스트림 병렬화는 주의해서 적용하라
      • 메서드
        • 아이템49 매개변수가 유효한지 검사하라
        • 아이템50 적시에 방어적 본사본을 만들라
        • 아이템53 가변인수는 신중히 사용하라
        • 아이템 54 null이 아닌, 빈 컬렉션이나 배열을 반환하라
        • 아이템56 공개된 API 요소에는 항상 문서화 주석을 작성하라
      • 일반적인 프로그래밍 원칙
        • 아이템56 공개된 API 요소에는 항상 문서화 주석을 작성하라
        • 아이템57 지역변수의 범위를 최소화하라
        • 아이템 60 정확한 답이 필요하다면 float와 double은 피하라
      • 예외
        • 아이템 73 추상화 수준에 맞는 예외를 던지라
        • 아이템 74 메서드가 던지는 모든 예외를 문서화하라
      • 동시성
        • 아이템78 공유 중인 가변 데이터는 동기화해 사용하라
        • 아이템79 과도한 동기화는 피하라
        • 아이템 80 스레드보다는 실행자, 태스크, 스트림을 애용하라
      • 직렬화
        • 아이템 87 커스텀 직렬화 형태를 고려해보라
    • Functional Programming in Java
      • Chap 01. 헬로, 람다 표현식
      • Chap 02. 컬렉션의 사용
      • Chap 03. String, Comparator, 그리고 filter
      • Chap 04. 람다 표현식을 이용한 설계
      • CHAP 05. 리소스를 사용한 작업
      • CHAP 06. 레이지
      • CHAP 07. 재귀 호출 최적화
      • CHAP 08. 람다 표현식의 조합
      • CHAP 09. 모든 것을 함께 사용해보자
      • 부록 1. 함수형 인터페이스의 집합
      • 부록 2. 신택스 오버뷰
    • 코틀린 쿡북
      • 2장 코틀린 기초
      • 3장 코틀린 객체지향 프로그래밍
      • 4장 함수형 프로그래밍
      • 5장 컬렉션
      • 6장 시퀀스
      • 7장 영역 함수
      • 9장 테스트
      • 10장 입력/출력
      • 11장 그 밖의 코틀린 기능
    • DDD START!
      • 1장 도메인 모델 시작
      • 2장 아키텍처 개요
      • 3장 애그리거트
      • 4장 리포지터리와 모델구현(JPA 중심)
      • 5장 리포지터리의 조회 기능(JPA 중심)
      • 6장 응용 서비스와 표현 영역
      • 7장 도메인 서비스
      • 8장 애그리거트 트랜잭션 관리
      • 9장 도메인 모델과 BOUNDED CONTEXT
      • 10장 이벤트
      • 11장 CQRS
    • JAVA 8 IN ACTION
      • 2장 동작 파라미터화 코드 전달하기
      • 3장 람다 표현식
      • 4장 스트림 소개
      • 5장 스트림 활용
      • 6장 스트림으로 데이터 수집
      • 7장 병렬 데이터 처리와 성능
      • 8장 리팩토링, 테스팅, 디버깅
      • 9장 디폴트 메서드
      • 10장 null 대신 Optional
      • 11장 CompletableFuture: 조합할 수 있는 비동기 프로그래밍
      • 12장 새로운 날짜와 시간 API
      • 13장 함수형 관점으로 생각하기
      • 14장 함수형 프로그래밍 기법
    • 객체지향과 디자인패턴
      • 객체 지향
      • 다형성과 추상 타입
      • 재사용: 상속보단 조립
      • 설계 원칙: SOLID
      • DI와 서비스 로케이터
      • 주요 디자인 패턴
        • 전략패턴
        • 템플릿 메서드 패턴
        • 상태 패턴
        • 데코레이터 패턴
        • 프록시 패턴
        • 어댑터 패턴
        • 옵저버 패턴
        • 파사드 패턴
        • 추상 팩토리 패턴
        • 컴포지트 패턴
    • NODE.JS
      • 1회차
      • 2회차
      • 3회차
      • 4회차
      • 6회차
      • 7회차
      • 8회차
      • 9회차
      • 10회차
      • 11회차
      • 12회차
      • mongoose
      • AWS란?
    • SRPING IN ACTION (5th)
      • Chap1. 스프링 시작하기
      • Chap 2. 웹 애플리케이션 개발하기
      • Chap 3. 데이터로 작업하기
      • Chap 4. 스프링 시큐리티
      • Chap 5. 구성 속성 사용하기
      • Chap 6. REST 서비스 생성하기
      • Chap 7. REST 서비스 사용하기
      • CHAP 8 비동기 메시지 전송하기
      • Chap 9. 스프링 통합하기
      • CHAP 10. 리액터 개요
      • CHAP 13. 서비스 탐구하기
      • CHAP 15. 실패와 지연 처리하기
      • CHAP 16. 스프링 부트 액추에이터 사용하기
    • 스프링부트 코딩 공작소
      • 스프링 부트를 왜 사용 해야 할까?
      • 첫 번째 스프링 부트 애플리케이션 개발하기
      • 구성을 사용자화 하기
      • 스프링부트 테스트하기
      • 액추에이터로 내부 들여다보기
    • ANGULAR 4
      • CHAPTER 1. A gentle introduction to ECMASCRIPT 6
      • CHAPTER 2. Diving into TypeScript
      • CHAPTER 3. The wonderful land of Web Components
      • CHAPTER 4. From zero to something
      • CHAPTER 5. The templating syntax
      • CHAPTER 6. Dependency injection
      • CHAPTER 7. Pipes
      • CHAPTER 8. Reactive Programming
      • CHAPTER 9. Building components and directives
      • CHAPTER 10. Styling components and encapsulation
      • CHAPTER 11. Services
      • CHAPTER 12. Testing your app
      • CHAPTER 13. Forms
      • CHAPTER 14. Send and receive data with Http
      • CHAPTER 15. Router
      • CHAPTER 16. Zones and the Angular magic
      • CHAPTER 17. This is the end
    • HTTP 완벽 가이드
      • 게이트웨이 vs 프록시
      • HTTP Header
      • REST API
      • HTTP Method 종류
        • HTTP Status Code
      • HTTP 2.x
  • REFERENCE
    • TECH BLOGS
      • 어썸데브블로그
      • NAVER D2
      • 우아한 형제들
      • 카카오
      • LINE
      • 스포카
      • 티몬
      • NHN
      • 마켓컬리
      • 쿠팡
      • 레진
      • 데일리 호텔
      • 지그재그
      • 스타일쉐어
      • 구글
      • 야놀자
    • ALGORITHM
      • 생활코딩
      • 프로그래머스
      • 백준
      • 알고스팟
      • 코딜리티
      • 구름
      • 릿코드
Powered by GitBook
On this page
  • 다대일
  • 다대일 단방향 [N:1]
  • 다대일 양방향 [N:1, 1:N]
  • 일대다
  • 일대다 양방향[1:N, N:1]
  • 일대일 [1:1]
  • 주 테이블에 외래 키
  • 대상 테이블에 외래 키
  • 다대다 [N:N]
  • 다대다: 단방향
  • 다대다: 양방향
  • 다대다: 매핑의 한계와 극복, 연결 엔티티 사용
  • 다대다 연관관계 정리
  • 참고

Was this helpful?

  1. STUDY
  2. JAVA ORM JPA

6장 다양한 연관관계 매핑

자바 ORM 표준 JPA 프로그래밍 6장을 요약한 내용 입니다.

Previous5장 연관관계 매핑 기초Next7장 고급 매핑

Last updated 5 years ago

Was this helpful?

엔티티의 연관관계를 매핑할 때는 다음 3가지를 고려해야 한다.

  • 다중성

    • 다대일(@ManyToOne)

    • 일대다(@OneToMany)

    • 일대일(@OneToOne)

    • 다대다(@ManyToMany)

  • 단방향, 양방향

    • 테이블은 외래 키 하나로 조인을 사용해서 양방향으로 쿼리가 가능하므로 사실상 방향이라는 개념이 없다.

    • 반연에 객체는 참조용 필드를 가지고 있는 객체만 연관된 객체를 조회할 수 있다.

  • 연관관계의 주인

    • JPA는 두 객체 연관관계 중 하나를 정해서 데이터 베이스 외래 키를 관리하는데 이것을 연간관계의 주인이라 한다. 주인이 아닌 방향은 외래 키를 변경할 수 없고 읽기만 가능하다.

    • 연관관계의 주인이 아니면 mappedBy 속성을 사용하고 연관관계의 주인 필드 이름을 값으로 입력해야 한다.

다대일

객체 양방향 관계에서 연관관계의 주인은 항상 다 쪽이다. 예를 들어 회원(N)과 팀(1)이 있으면 회원 쪽이 연관관계의 주인이다.

다대일 단방향 [N:1]

다대일 양방향 [N:1, 1:N]

  • 양방향은 외래 키가 있는 쪽이 연관관계의 주인이다.

    일대다와 다대일 연관 관계는 항상 다(N)에 외래 키가 있다. 여기서는 다 쪽인 MEMBER 테이블이 외래 키를 가지고 있으므로 Member.team이 연관관계의 주인이다. JPA는 외래 키를 관리할 때 연관관계의 주인만 사용한다.

  • 양방향 연관관계는 항상 서로를 참조해야 한다.

    항상 서로 참조하게 하려면 연관관계 편의 메소드를 작성하는 것이 좋은데 회원의 setTeam(), 팀의 addMember() 메소드가 이런 편의 메소드들이다.

    @Entity
    public class Member {
    	@Id @GeneratedValue
    	@Column(name = "MEMBER_ID")
    	private Long id;
    
    	...
    
    	public void setTeam(Team team) {
    		this.team = team;
    		
    		//무한루프에 빠지지 않도록 체크
    		if(!team.getMembers().contains(this)) {
    			team.getMembers().add(this);
    		}
    	}
    }
    @Entity
    public class Team {
    	...
    
    	public void addMember(Member member) {
    		this.members.add(member);
    		
    		//무한루프에 빠지지 않도록 체크
    		if (member.getTeam() != this) {
    			member.setTeam(this);
    		}
    	}
    }

일대다

일대다 관계는 엔티티를 하나 이상 참조할 수 있으므로 자바 컬렉션인 Collection, List, Set, Map 중에 하나를 사용해야 한다.

Q1. 다양한 컬렉션 구현체중에 어느것을 사용하는게 적절한가?

  • List는 순서를 가지고 있으며 중복이 허용된다. 또한 @Orderby 어노테이션을 이용하여 정렬이 가능하다. List에 add할 경우 기존 테이블을 날리고 insert하는 쿼리가 발생한다.

  • Set은 순서가 없으며 중복을 허용하지 않는다. @Orderby 어노테이션을 이용하여 정렬을 하여도 해쉬값에 의해 정렬되므로 원하는대로 정렬이 되지 않을 수 있다.

  • List를 사용할 경우 순서가 보장되지 않기 때문에 조인 테이블 전략을 사용할 경우 데이터를 삭제하고 다시 삽입하는 쿼리가 발생할 수 있다.

    • 전제조건

      • @OneToMany에서 @JoinTable 전략을 설정한다.

      • @OneToMany의 컬렉션을 List로 설정한다.

보통 자신이 매핑한 테이블의 외래 키를 관리하는데, 이 매핑은 반대쪽 테이블에 있는 외래 키를 관리한다.

@Entity
public class Team {
	...
	
	@OneToMany
	@JoinColumn(name = "TEAM_ID") // MEMEBER 테이블의 TEAM_ID (FK)
	private List<Member> members = new ArrayList<Member>();
	...
}

일대다 단방향 관계를 매핑할 때는 @JoinColumn을 명시해야 한다. 그렇지 않으면 JPA는 연결 테이블을 중간에 두고 연관관계를 관리하는 조인 테이블(JoinTable) 전략을 기본으로 사용해서 매핑한다.

Q2. 조인 테이블전략이란? @JoinColumn 어노테이션이 없을 경우에 A table 과 B table이 연결된 매핑 테이블이 생성된다. 업데이트 로직 삭제 로직에 매핑 테이블에 갯수 만큼 반영된다. @OneToMany 어노테이션에만 적용된다. @ManyToOne에서는 @JoinColumn을 선언하지 않아도 조인 테이블이 생성되지 않는다.

  • 일대다 단방향 매핑의 단점

    일대다 단방향 매핑의 단점은 매핑한 객체가 관리하는 외래 키가 다른 테이블에 있다는 점이다. 본인 테이블에 외래 키가 있으면 엔티티의 저장과 연관 관계 처리를 INSERT SQL 한 번으로 끝낼 수 있지만, 다른 테이블에 외래 키가 있으면 연관 관계 처리를 위한 UPDATE SQL을 추가로 실행해야 한다.

    public void testSave() {
    	Member member1 = new Member("member1");
    	Member member2 = new Member("member2");
    
    	Team teaml = new Team("teaml");
    	teaml.getMembers().add(memberl);
    	teaml.getMembers().add(member2);
    
    	em.persist(memberl); // INSERT-memberl
    	em.persist(member2); // INSERT-member2
    	em.persist(teaml);   // INSERT-teaml, UPDATE-memberl.fk,
    										   // UPDATE-meinber2. fk
    	transaction.commit();
    }

Q3. @JoinColumn 과 mappedBy 속성을 같이 사용하면 에러가 발생한다. 왜 그럴까? "Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn" 와 같은 에러가 발생한다. 그 이유는 mappedBy 옵션과 @JoinTable 이 같은 전략을 사용하기 때문에 충돌이 발생하기 때문이다. mappedBy를 사용해도 @JoinColumn 전략을 사용한다.

일대다 양방향[1:N, N:1]

다대일 관계는 항상 다 쪽에 외래 키가 있다. 이런 이유로 @ManyToOne에는 mappedBy 속성이 없다. 일대다 양방향 매핑이 완전히 불가능한 것은 아니다. 일대다 단방향 매핑 반대편에 같은 외래 키를 사용하는 다대일 단방향 매핑을 읽기 전용으로 하나 추가하면 된다.

@Entity
public class Member {
	@Id @GeneratedValue
	@Column(name = "MEMBER_ID")
	private Long id;

	private String username;

	@ManyToOne
	@JoinColumn(name = "TEAM_ID" , insertable = false, updatable = false)
	private Team team;

	//Getter, Setter ...
}

둘 다 같은 키를 관리하므로 문제가 발생할 수 있기 때문에 반대편인 다대일 쪽은 insetable,updatable 설정으로 읽기만 가능하게 했다. 이 방법은 일대다 양방향 매핑 이라기보다는 일대다 단방향 매핑 반대편을 읽기 전용으로 추가해서 일대다 앙방향처럼 보이도록 하는 방법이다.

일대일 [1:1]

일대일 관계는 주 테이블이나 대상 테이블 중에 누가 외래 키를 가질지 선택해야 한다.

  • 주 테이블에 외래 키

    주 객체가 대상 객체를 참조하는 것처럼 주 테이블에 외래 키를 두고 대상 테이블을 참조한다.

    이 방법의 장점은 주 테이블이 외래 키를 가지고 있으므로 주 테이블만 확인해도 대상 테이블과 연관관계가 있는지 알 수 있다.

  • 대상 테이블에 외래 키

    테이블 관계를 일대일에서 일대다로 변경할 때 테이블 구조를 그대로 유지할 수 있다.

주 테이블에 외래 키

단방향

@Entity
public class Member {
  @Id @GeneratedValue
  @Column(name = "MEMBER_ID")
  private Long id;

  private String username;

  @0neTo0ne
  @JoinColunm (name = "LOCKER_ID")
  private Locker locker;

  ...
}
@Entity
public class Locker {
  @Id @GeneratedValue
  @Column(name = "LOCKER_ID")
  private Long id;

  private String name;
  
  ...
}

양방향

@Entity
public class Member {
  @Id @GeneratedValue
  @Column(name = "MEMBER_ID")
  private Long id;

  private String username;

  @OneToOne
  @JoinColumn(name = "LOCKER_ID")
  private Locker locker;

}

@Entity
public class Locker {
  @Id @GeneratedValue
  @Column(name = "LOCKER_ID")
  private Long id;

  private String name;

  @OneToOne(mappedBy = "locker")
  private Member member;
  
  ... 
}

MEMBER 테이블이 외래 키를 가지고 있으므로 Member 엔티티에 있는 Member.locker가 연관관계의 주인이다. 따라서 반대 매핑인 사물함의 Locker.member는 mappedby를 선언해서 연관관계의 주인이 아니라고 설정했다.

대상 테이블에 외래 키

단방향

일대일 관계 중 대상 테이블에 외래 키가 있는 단방향 관계는 JPA에서 지원하지 않는다. 이때는 단방향 관계를 Locker에서 Member 방향으로 수정하거나 양방향 관계로 만들고 Locker를 연관관계의 주인으로 설정해야 한다.

양방향

@Entity
public class Member {
  @Id @GeneratedValue
  @Column(name = "MEMBER_ID")
  private Long id;

  private String username;

  @OneToOne(mappedBy = "member")
  private Locker locker;
  ...
}

@Entity
public class Locker {
  @Id @GeneratedValue
  @Column(name = "LOCKER_ID")
  private Long id;

  private String name;

  @OneToOne
  @JoinColumn(name = "MEMBER_ID")
  private Member member;

  ...
}

다대다 [N:N]

관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없다. 그래서 보통 다대다 관계를 일대다, 다대일 관계롤 풀어내는 연결 테이블을 사용한다.

다대다: 단방향

@Entity
public class Member {
  @Id @Column(name = "MEMBER_ID")
  private String id;

  private String username;

  @ManyToMany
  @JoinTable(name = "MEMBER_PRODUCT",
    joinColumns = @JoinColumn(name = "MEMBER_ID"),
    inverseJoinColumns = @JoinColumn(name ="PRODUCT_ID"))
  private List<Product> products = new ArrayList<Product>();
  ...

}

@Entity
public class Product {
  @Id @Column(name = "PRODUCT_ID")
  private String id;

  private String name;
  
  ...
}
  • @JoinTable.name : 연결 테이블을 지정한다.

  • @JoinTable.joinColumns : 현재 방향인 회원과 매핑할 조인 컬럼 정보를 지정한다.

  • @JoinTable.inverseJoinColums : 반대 방향인 상품과 매핑할 조인 컬럼 정보를 지정한다.

public void save() {
  Product productA = new Product();
  productA.setld("productA");
  productA.setName("상품A");
  em.persist(productA) ;

  Member member1 = new Member();
  member1.setld("member1");
  member1.setUsername("회원1");
  member1.getProducts().add(productA) //연관관계 설정
  em.persist(member1);
}

다대다: 양방향

다대다 매핑이므로 역방향도 @ManyToMany를 사용한다. 그리고 양쪽 중 원하는 곳에 mappedBy로 연관관계 주인을 지정한다.

@Entity
public class Product {
  @Id @Column(name = "PRODUCT_ID")
  private String id;

  private String name;
  
  @ManyToMany(mappedBy = "products") // 역방향 추가
	private List<Member> members;
	...
}
public void findinverse () {
  Product product = em.find(Product.class, "productA");
  List<Member> members = product.getMembers();
  for (Member member : members) {
	  System.out.printin("member = " + member.getUsername ());
	}
}

다대다: 매핑의 한계와 극복, 연결 엔티티 사용

@ManytoMany를 사용하면 연결 테이블을 자동으로 처리해 주므로 도메인 모델이 단순 해지고 여러 가지로 편리하다. 하지만 이 매핑을 실무에서 사용하기에는 한계가 있다.

연결 테이블에 주문 수량과 주문 날짜 컬럼을 추가했다. 이렇게 컬럼을 추가하면 더는 @ManyToMany를 사용할 수 없다. 왜냐하면 주문 엔티티나 상품 엔티티에는 추가한 컬럼 들을 매핑할 수 없기 때문이다.

결국 연결 테이블을 매핑하는 연결 엔티티를 만들고 이곳에 추가한 컬럼들을 매핑해야 한다.

@Entity
public class Member {
  @Id @Column(name = "MEMBER_ID")
  private String id;

  //역방향
  @OneToMany(mappedBy = "member")
  private List<MemberProduct> memberproducts;
}
@Entity
@IdClass(MemberProductId.class)
public class MemberProduct {
  @Id
  @ManyToOne
  @JoinColumn(name = "MEMBER_ID")
  private Member member; //MemberProductId.member와 연결

  @Id
  @ManyToOne
  @JoinColumn(name = "PRODUCT_ID")
  private Product product; //MemberProductId.product와 연결

  private int orderAmount;
}

public class MemberProductId implements Serializable {
  private String member; //MemerProduct.member 연결
  private String product; //MemberProduct.product 연결

  //hashCode and equals
  @Override
  public boolean equals(Object o) {...}

  @Override
  public int hashCode() {...}
  
}

회원상품(MemberProduct) 엔티티를 보면 기본 키를 매핑하는 @Id와 외래 키를 매핑하는 @JoinColumn을 동시에 사용해서 기본 키 + 외래 키를 한번에 매핑했다. 그리고 @IdClass를 사용해서 복합 기본 키를 매핑했다.

복합 기본 키란? JPA에서 복합 키를 사용하려면 별도의 식별자 클래스를 만들어야 한다. 그리고 엔티티에 @IdClass를 사용해서 식별자 클래스를 지정하면 된다.

복합 키를 위한 식별자 클래스 특징

  • 복합 키는 별도의 식별자 클래스로 만들어야 한다.

  • Serializable을 구현해야 한다.

  • equals와 hashCode 메소드를 구현해야 한다.

  • 기본 생성자가 있어야 한다.

  • 식별자 클래스는 public이어야 한다.

  • @IdClass를 사용하는 방법 외에 @EmbeddedId를 사용하는 방법도 있다.

저장 코드

public void save() {
  //회원 저장
  Member member1 = new Member();
  member1.setld("member1");
  member1.setUsername("회원 1");
  em.persist(memberl);

  //상품 저장
  Product productA = new Product();
  productA.setld("productA");
  productA.setName(”상품 1");
  em.persist(productA);

  //회원상품 저장
  MemberProduct memberProduct = new MemberProduct();
  memberProduct.setMember(memberl); //주문 회원 - 연관관계 설정
  memberProduct.setProduct(productA); //주문 상품 - 연관관계 설정
  memberProduct.setOrderAmount(2); //주문 수량
  em.persist(memberProduct);
}

조회 코드

public void find () {
  //기본 키 값 생성
  MemberProductld memberProductId = new MemberProductId();
  memberProductld.setMember("member1");
  memberProductld.setProduct("productA");

  MemberProduct memberProduct = em.find(MemberProduct.class,memberProductld);
  Member member = memberProduct.getMember();
  Product product = memberProduct.getProduct();

  System.out.println("member = " + member.getUsername());
  System.out.println("product = " + product.getName());
  System.out.printIn("orderAmount = " + memberProduct.getOrderAmount());
}

다대다 연관관계 정리

다대다 관계를 일대다 다대일 관계로 풀어내기 위해 연결 테이블을 만들 때 식별자를 어떻게 구서할지 선택해야 한다.

  • 식별 관계 : 받아온 식별자를 기본 키 + 외래 키로 사용한다.

  • 비식별 관계 : 받아온 식별자는 외래 키로만 사용하고 새로운 식별자를 추가한다.

객체 입장에서 보면 비식별 관계를 사용하는 것이 복합 키를 위한 식별자 클래스를 만들지 않아도 되므로 단순하고 편리하게 ORM 매핑을 할 수 있다.

참고

JPA OneToMany : List vs Set
JPA list or set 차이
JPA 컬렉션 - 머루의개발블로그
일대다 단방향 매핑 잘못하면 생기는