Flamme Dev
  • IntroduceMyself
  • GOAL
    • 2021년 목표
    • 회고록
      • 2020년
  • COMMON
    • 자료구조
      • 시간복잡도
      • 스택/큐
    • 코드리뷰/스터디/세미나
      • Semina
        • Version control -1 (SVN/GIT)
        • Version control - 2 (GIT)
        • PostMan
        • JMeter
      • 스터디
        • 2021년
          • EffectiveKotlin
          • 이펙티브자바
        • 2020년
          • JPA Study
            • Chap1
            • Chap3
            • Chap4
            • Chap5
            • Chap6
            • Chap7
            • Chap8
            • Chap9
            • Chap10 -1
            • Chap10-2
            • Chap10-3
            • Chap12
            • Chap 13. 웹 애플리케이션과 영속성 관리
            • Chap14
      • 코드리뷰
        • Page 1
        • 개발 생각
        • APNs 라이브러리 교체
        • 파일 삭제 분투기
        • 신입 코드리뷰
          • 2020-09-02
          • 2020-09-03
          • 2020-09-10
        • Repository
          • Repositroy(20201124)
          • Repositroy(20201123)
          • Repositroy(20201120)
          • Repositroy(20201119)
          • Repositroy(20201117)
    • 개발 서적 / 동영상 강의
      • 동영상 강의
        • 진행 중
        • 완주한 것
      • 개발 서적
        • 읽은 것
          • 손에 잡히는 10분 SQL
            • 인덱스
            • 정리 본
        • 진행 중
          • 폴리글랏 프로그래밍
          • DDD-START
            • Chapter5 리포지터리의 조회 기능
            • Chapter4 리포지터리와 모델 구현
            • Chapter3 애그리거트
            • Chapter2 아키텍쳐 개요
            • Chapter1 도메인 모델의 시작
  • INFO
    • 유스콘 준비
    • intellij
      • Command line is too long.
    • 프로그래밍의 종류
      • Async Await
      • Reactive Programming
      • Imperative Programming
      • Functional Programming
        • Naver D2 함수형 프로그래밍 강의
      • Object Oriented Programing
        • 객체지향에 대해서 알아보는 영상
      • ProcedureOriented Programming
      • RX(ReactiveX)
      • OOP VS FP
      • Declarative Programming
    • Language
      • Kotlin
        • 박재성님 강의
        • Kotlin Spring CGLIB Error
        • Kotlin의 사용
        • coroutine
        • Kotlin JVM
      • JavaScript
      • JAVA
        • DefaultNonnull
        • effective78
        • Exception
        • JsonUnWrappers
        • 명명짓기
        • @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
        • Enc Key
        • ServiceLoader
        • Annotation
        • JVM(Java Virtual Machine)
        • Java8
          • Functional 사용
          • Optional
          • Invalid Key size
        • Java13
          • Text Block
        • Basic
          • Generic
          • JVM
          • Interface, Implements
    • Framework
      • NodeJs
      • Xamarin
      • Vert.x
        • vert.x in action
        • eventbus
      • blockedthreadchecker
      • Why.. not root run..
      • Spring
        • Spring Test
        • Kotlin
        • ControllerAdvice 정리
        • JsonAnnotation
        • Spring Data R2DBC
        • SpringWebFlux
          • login
          • SpringWebFlux란?
        • JPA
          • QueryDSL 객체 주소값 확인할 것
          • SpringData-JPA에서 Save, SaveAll
          • @CompositeKey에 ManyToOne JoinColumn이 있을 경우
          • QueryDSL N+1 문제(@OneToOne)
          • QueryDSL설정
          • CreationTimestamp
          • Enum(@enumerated vs @convert)
        • SpringSecurity
          • aopAlliance
          • csrf 방어
          • antMatcher vs mvcMatcher
          • SpringSecurity를 왜 써야할까?
          • CoreSpringSecurity
            • AuthenticationProvider
            • AuthenticationManager
            • Authentication Flow
            • SecurityContextPersistenceFilter
            • SecurityContextHolder, SecurityContext
            • Authentication
            • 필터 초기화와 다중 보안 설정
            • DelegatingProxyChain, FilterChainProxy
            • 사이트 간 요청 위조 - CSRF, CsrfFilter
            • 예외 처리 및 요청 캐시 필터 : ExceptionTranslationFilter, RequestCacheAwareFilter
            • 인가 API - 권한 설정 및 표현식
            • SessionManagementFilter, ConcurrentSessionFilter
            • 동시 세션 제어 / 세션고정보호/ 세션 정책
            • AnonymousAuthenticationFilter
            • RememberMeAutheticationFilter
            • Logout,LogoutFilter
            • UsernamePasswordAuthenticationFilter
            • Form인증
            • 인증 API - 사용자 정의 보안 기능 구현
            • 인증 API - 스프링 시큐리티 의존성 추가
        • SpringMVC
          • Spring Bean Q&A
          • HttpServletRequest
          • @ControllerAdvice
          • Service와 ServiceImpl은 꼭 있어야할까?
          • SpringMVC-1
          • SpringMVC - validator와 Errors
          • SpringMVC - DispatcherServlet의 URL
          • Spring MVC - 기본설정(2) 및 HandlerInterceptor
          • SpringMVC-3
          • Spring MVC - ViewResolver
          • Spring MVC - XML/JSON
          • Spring MVC - Field Injection & Constructor Injection
        • SpringEtc
          • Transactional
          • @Atowired NullPointer
          • CircleReference
          • Mybatis
            • IBatis - sql에 Map
            • 1:1 Mapping시 Null
            • auto_Increment, selectKey
          • @Transactional
          • JacksonAnnotation
        • SpringAOP
        • SpringDI
    • Server
      • PM2
      • Tomcat
        • jks
        • Virtual Host(Port)
    • OS
      • Window
        • Active directory
      • IOS
      • Linux
        • samba/keroberos
        • Linux
          • unlimit / socket backLog
          • nohup
          • ; , & , &&, {}
          • 쉘
          • rc.local
          • ps aux | grep 프로세스이름
          • Vim
          • Su, Sudo
          • File Directory
        • IdConfig
        • 유용한 커맨드
        • 리눅스 대소문자
        • 소프트링크와 하드링크의 차이
        • ln -Tfs
        • //와 / 의 차이
        • Linux(202003)
          • rm
          • diff
          • cp
          • mv
          • tar 압축 시 어떻게 심볼릭 링크도 같이 압축할까?
          • tar 명령어
          • ln -s 심볼릭 링크
    • Network
      • API Architectural Styles
      • 포트 확인
      • HeartBeat Protocol
      • SSO(Single Sign On)
      • Cookie and Session
      • Tcp HandShake
      • WireShark
        • WireShark 옵션
        • WireShark 패킷분석
    • DataBase
      • DB락에 대해서 알아보기
      • Redis
      • MYSQL
        • Mysql TimeOut 설정
        • MysqlLog
        • SlowQuery Analyzing
        • PushNotification Stress Test
      • MSSQL
      • 데이터베이스 식별자 선택 전략
    • ETC
      • 이름 짓기
      • FFProbe
      • Talk
      • Untitled
Powered by GitBook
On this page
  • JPA-Cha5 연관관계매핑기초
  • 단방향 연관관계
  • 연관관계 사용
  • 연관관계의 주인
  • 양방향 연관관계 저장
  • 양방향 연관관계의 주의점

Was this helpful?

  1. COMMON
  2. 코드리뷰/스터디/세미나
  3. 스터디
  4. 2020년
  5. JPA Study

Chap5

이 글은 김영한님의 jpa책을 보고 공부한 흔적입니다.

JPA-Cha5 연관관계매핑기초

객체는 참조(주소)를 사용해서 관계를 맺고 테이블은 외래 키를 사용해서 관계를 맺는다.

객체 관계 매핑(ORM)에서 가장 어려운 부분이 바로 객체 연관관계와 테이블 연관관계를 매핑하는 일이다.

객체의 참조와 테이블의 외래 키를 매핑하는 것이 이 장의 목표이다.

연관관계 매핑 핵심 키워드

  • 방향

    • 단방향

      • 회원 -> 팀 or 팀 -> 회원 둘 중 하나만 참고 하는 것

    • 양방향

      • 회원 -> 팀, 팀 -> 회원 모두 서로 참조하는 것

  • 다중성

    • 다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다(N:M)

    • 회원과 팀의 관계는 다대일

    • 팀과 회원은 일대다 관계

  • 연관관계의 주인

    • 객체를 양방향 연관관계로 만들면 연관관계의 주인을 정해야 한다.

단방향 연관관계

연관관계 중에선 다대일(N:1) 단방향 관계를 우선 이해해야 한다.

예.

  • 회원과 팀이 있다.

  • 회원은 하나의 팀에만 소속될 수 있다.

  • 회원과 팀은 다대일 관계다.

객체 연관관계

  • 회원 객체로 팀 객체와 연관관계를 맺는다.

  • 회원 객체와 팀 객체는 단방향 관계다.

    • 회원은 팀을 알 수 있지만, 팀은 회원을 알 수 없다.

테이블 연관관계

  • 회원 테이블은 Team_ID를 외래키로 팀 테이블과 연관관계를 맺는다.

  • 회원 테이블과 팀 테이블은 양방향 관계다.

객체 연관관계 vs 테이블 연관관계 정리

  • 테이블은 외래 키로 연관관계를 맺는다.

즉 객체는 참조를 사용하지만 테이블은 조인을 사용한다.

  • 참조를 사용하면 연관관계는 단방향이다.

  • 외래 키를 사용하는 테이블의 연관관계는 양방향이다.

    순수한 객체 연관관계: 168페이지 참고

    테이블 연관관계: 169페이지 참고

    객체 관계 매핑

@Entity 
public class Member {
    @Id 
    @Column(name= "MEMBER_ID") 
    private String id;
    private String username;
    
    //연관관계 매핑 
    @ManyToOne // 다대일 관계라는 매핑정보 
    @JoinColumn(name="TEAM_ID") // 조인 컬럼은 외래 키를 매핑할 떄 사용한다. 
    private Team team;

public void setTeam(Team team) { this.team = team; } 
}

@Entity 
public class Team { @Id @Column(name = "TEAM_ID") private String id;
    private String name;
}
  • 객체 연관관계 : 회원 객체의 Member.team 필드 사용

  • 테이블 연관관계 : 회원 테이브르이 MEMBER.TEAM_ID 외래 키 컬럼을 사용

Member.team과 MEMBER.TEAM_ID를 매핑하는 것이 연관관계 매핑이다.

@JoinColumn

  • name : 매핑할 외래 키 이름

  • referencedColumnName : 외래 키가 참조하는 대상 테이블의 컬럼명

  • foreignKey(DDL) : 외래 키 제약조건을 직접 지정할 수 있다.

  • unique, nullable, isertable, updateble, columnDefinition, table

@ManyToOne

  • optional : false로 설정하면 연관된 엔티티가 항상 있어야 한다.

  • fetch : 글로벌 패치 전략

  • cascade : 영속성 전이 기능

  • targetEntity : 연관된 엔티티의 타입 정보를 설정한다.

targetEntity속성 : 제네릭으로 타입 정보를 알 수 있다. targetEntity=Member.class인 경우 List에 <>를 안써도 된다.

연관관계 사용

연관관계를 등록, 조회, 수정, 삭제 하는 예제 P173 참고

  • 등록

  • 조회

    • 객체 그래프 탐색(객체 연관관계를 사용한 조회)

      • member.getTeam()을 사용해서 member와 관련된 team 엔티티 조회

    • 객체지향 쿼리 사용(JPQL)

      • SQL과 JPQL을 비교하면 JQL은 객체(엔티티)를 대상으로 하고 SQL보다 간결하다.

      • 10장에서 다룬다.

  • 수정

  • 삭제

    • setTeam(null); //연관관계 제거

연관된 엔티티 삭제

연관된 엔티티를 삭제하려면 기존에 있던 연관관계를 먼저 제거하고 삭제해야 한다. 그렇지 않으면 외래 키 제약조건으로 인해 데이터베이스에서 오류가 발생한다.

Team team = member.getTeam(); // 객체 그래프 탐색

member1.setTeam(null); //회원1 연관관계 제거
member2.setTeam(null); //회원2 연관관계 제거
em.remove(team); //팀삭제

양방향 연관관계

회원에서 팀으로 접근하고 반대 방향인 팀에서 회원으로 접근할 수 있도록 양방향 연관관계에 대해서 말해보자.

회원과 팀은 다대일, 팀에서 회원은 일대다 관계다.

객체 연관관계 :

  • 회원 -> 팀 (Member.team)

  • 팀 -> 회원 (Team.members)

테이블의 관계 :

  • 외래 키 하나로 양방향으로 조회

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

    private String username;

    @ManyToOne
    @JoinColumn(name="TEAM_ID") 
    private Team team;

    public void setTeam(Team team) {
        this.team = team;
    }
}

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

    private String name;

    @OneToMany(mappedBy = "team")
    //mappedBy 속성 양방향 매핑일 떄 사용하는데 반대쪽 매핑의 필드 이름을 갑으로 주면 된다.
    //Member.team이므로 team을 값으로 주었다.
    private List<Member> members = new ArrayList<Member>();


}

일대다 컬렉션 조회

public void biDirection() {
    Team team = em.find(Team.class,"team1");
    List<Member> members = team.getMember();

    for (Member member : members) {
        System.out.println("member.username = " + member.getUsername()); 
    }   

}

연관관계의 주인

객체에는 양방향 연관관계라는 것이 없다.

서로 다른 단방향 연관관계 2개를 애플리케이션 로직으로 잘 묶어서 양방향 인것처럼 보이게 할뿐이다.

  • 테이블은 외래 키 하나로 두 테이블의 연관관계를 관리한다.

  • 엔티티를 단방향으로 매핑하면 참조를 하나만 사용

  • 엔티티를 양방향 연관관계로 설정하면 객체의 참조는 둘인데 외래 키는 하나다.

    따라서 둘 사이에 차이가 발생한다. -> 이러한 차이로 JPA에서는 두 객체 연관관계 중 하나를 정해서 테이블의 외래키를 관리해야 하는데 이것을 연관관계의 주인(Owner)이라고 한다.

양방향 매핑의 규칙 : 연관관계의 주인

양방향 연관관계에서 두 연관관계 중 하나를 연관관계의 주인으로 정해야 한다.

연관관계의 주인만이 데이터베이스 연관관계와 매핑되고 외래 키를 관리(등록, 수정, 삭제) 할 수 있다.

반면에 주인이 아닌 쪽은 읽기만 할 수 있다.

여기서 사용되는 개념이 바로 mappedBy 속성이다.

mappedBy : 주인을 결정하는 방법 mappedBy를 사용하면 주인이 아님을 설정한다.

  • 주인은 mappedBy 속성을 사용하지 않는다.

  • 주인이 아니면 mappedBy 속성을 사용해서 속성의 값으로 연관관계의 주인을 지정해야 한다.

연관관계의 주인을 정한다는 것은 사실 외래 키 관리자를 선택하는 것이다.

연관관계의 주인은 외래 키가 있는 곳

연관관계의 주인은 테이블에 외래 키가 있는 곳으로 정해야 한다.

예제에서 회원테이블이 외래 키를 가지고 있으므로 Member.team이 주인이 된다.

주인이 아닌 Team.member에는 mappedBy="team" 속성을 사용해서 주인이 아님을 설정한다.

즉 연관관계의 주인만 데이터베이스 연관관계와 매핑되고 외래 키를 관리 할 수 있다.

주인이 아닌 반대편은 읽기만 가능하고 외래키를 변경하지 못한다.

데이터베이스 테이블의 다대일, 일대다 관계에서는 항상 다 쪽이 외래 키를 가진다.

다 쪽인 @ManyToOne은 항상 연관관계의 주인이 되므로 mappedBy를 설정할 수 없다. 따라서 @ManyToOne에는 mappedBy 속성이 없다.

양방향 연관관계 저장

public void testSave() {

    Team team1 = new Team("team", "팀1");
    em.persist(team1);

    Member member1 = new Member("member1", "회원1");
    member1.setTeam(team1);
    em.persist(member1);

    Member member2 = new Member("member2", "회원2");
    member2.setTeam(team1);
    em.persist(member2);




}

양방향 연관관계의 주의점

연관관계의 주인에는 값을 입력하지 않고, 주인이 아닌 곳에만 값을 입력하는 것

순수한 객체까지 고려한 양방향 연관관계

객체 관점에서 양쪽 방향에 모두 값을 입력해주는 것이 가장 안전한다.

public void test순수한객체_양방향() {
    Team team1 = new Team("team1", "팀1");
    em.persist(team1);

    Member member1 = new Member("member1", "회원1");
    member1.setTeam(team1);
    team1.getMembers().add(member1); 
    em.persist(member1);

    Member member2 = new Member("member2", "회원2");
    member2.setTeam(team1);
    team1.getMember().add(member2);
    em.persist(member2);

    List<Member> members = team1.getMembers();
    System.out.println("members.size = " + members.size());
}

member1.setTeam(team1); // 연관관계의 주인 team1.getMembers().add(member1); // 주인이 아니다. 저장 시 사용되지 않는다.

-> 객체까지 고려해서 주인이 아닌 곳에도 값을 입력하자.

결론 : 객체의 양방향 연관관계는 양쪽 모두 관계를 맺어주자.

public class Member { 
    private Team team;


    public void setTeam(Team team) {
        //삭제되지 않는 관계가 있기 때문에 연관관계를 변경할 떄는 기존 팀이 있으면 기존 팀과 회원의 연관관계를 삭제하는 코드를 추가해야 한다.
        if (this.team != null) {
            this.team.getMembers().remove(this);
        }
        this.team = team;
        team.getMember().add(this);
    }   
}

public void test순수한객체_양방향2() {
    Team team1 = new Team("team1", "팀1");
    em.persist(team1);

    Member member1 = new Member("member1", "회원1");
    member1.setTeam(team1);
    em.persist(member1);

    Member member2 = new Member("member2", "회원2");
    member2.setTeam(team1);
    em.persist(member2);

    List<Member> members = team1.getMembers();
    System.out.println("members.size = " + members.size());
}

객체에서 양방향 연관관계를 사용하려면 로직을 견고하게 작성해야 한다.

양방향의 장점 : 반대방향으로 객체 그래프 탐색 기능이 추가된 것.

주인의 반대편은 항상 mappedBy로 주인을 지정해야 한다.

  • 단방향 매핑만으로 테이블과 객체의 연관관계 매핑은 이미 완료되었다.

  • 단방향을 양방향으로 만들면 반대방향으로 객체 그래프 기능이 추가된다.

  • 양방향 연관관계를 매핑하려면 객체에서 양쪽 방향을 모두 관리해야 한다.

연관관계의 주인 : 외래 키의 위치와 관련해서 정해야지 비즈니스 중요도로 접근하면 안 된다.

PreviousChap4NextChap6

Last updated 4 years ago

Was this helpful?

객체는 참조(주소)로 연관관계를 맺는다. //참조 :

https://opentutorials.org/course/2517/14152