ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 4. 연관관계 매핑 기초
    Spring-Boot/자바 ORM 표준 JPA 프로그래밍 - 기본편 2022. 12. 20. 17:12

    - 목표

        ㆍ객체와 테이블 연관관계의 차이를 이해

        ㆍ객체의 참조와 테이블의 외래 키를 매핑

        ㆍ용어 이해

              º 방향(Direction) : 단방향, 양방향

              º 다중성(Multiplicity) : 다대일(N:1). 일대다(1:N), 일대일(1:1), 다대다(N:M) 이해

              º 연관관계의 주인(Owner) : 객체 양방향 연관관계는 관리주인이 필요

    1. 단방향 연관관계

    - 객체를 테이블에 맞추어 데이터 중심으로 모델링하면, 협력 관계를 만들 수 없다.

        ㆍ테이블은 외래 키로 조인을 사용해서 연관된 테이블을 찾는다.

        ㆍ객체는 참조를 사용해서 연관된 객체를 찾는다

        ㆍ테이블과 객체 사이에는 이런 큰 간격이 있다.

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

     

    public class JpaMain {
        public static void main(String[] args) {
            EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
            EntityManager em = emf.createEntityManager();
    
            EntityTransaction tx = em.getTransaction();
            tx.begin();
    
            try {
    
                // 저장
                Team team = new Team();
                team.setName("TeamA");
                em.persist(team);
    
                Member2 member = new Member2();
                member.setUsername("member1");
    //            member.setTeamId(team.getId());
                member.setTeam(team);
                em.persist(member);
    
                em.flush();
                em.clear();	// sql문을 보고 싶을때 : 영속성 컨텍스트 캐시데이터 제거
    
                Member2 findMember = em.find(Member2.class, member.getId());
    //            Long findTeamId = findMember.getTeamId();
    //            Team findTeam = em.find(Team.class, findTeamId);
                Team findTeam = findMember.getTeam();
                System.out.println("findTeam = " + findTeam.getName());
    
                // team update
                Team newTeam = em.find(Team.class, 100L);
                findMember.setTeam(newTeam);
    
                tx.commit();
            } catch (Exception e) {
                tx.rollback();
            } finally {
                em.close();
            }
    
            // 웹어플리케이션이면 WAS가 내려갈때 엔티티매니저팩토리를 닫아줘야한다.
            emf.close();
    
    
        }
    }

    2. 양방향 연관관계와 연관관계의 주인

    @OneToMany(mappedBy = "team")       // Member2의 team 변수명과 연결되어 있다.
    private List<Member2> member2s = new ArrayList<>();
    Member2 findMember = em.find(Member2.class, member.getId());
    
    List<Member2> member2s = findMember.getTeam().getMember2s();    // 역방향조회
    for (Member2 m : member2s) {
        System.out.println("m.getUsername() = " + m.getUsername());
    }

     

    - 연관관계의 주인과 mappedBy

        ㆍmappedBy = JPA의 멘탈붕괴 난이도

        ㆍmappedBy는 처음에는 이해하기 어렵다

        ㆍ객체와 테이블간에 연관관계를 맺는 차이를 이해해야 한다.

     

    - 객체와 테이블이 관계를 맺는 차이

        ㆍ객체 연관관계 = 2개

              º 회원 → 팀 연관관계 1개(단방향)

              º 팀 → 회원 연관관계 1개(단방향)

        ㆍ테이블 연관관계 = 1개

              º 회원 ↔ 팀의 연관관계 1개(양방향)

    - 객체의 양방향 관계

        ㆍ객체의 양방향 관계는 사실 양방향 관계가 아니라 서로 다른 단방향 관계 2개다.

        ㆍ객체를 양방향으로 참조하려면 단방향 연관관계를 2개 만들어야 한다.

              º  A → B(a.getB())

              º  B  A(b.getA())

     

    - 테이블의 양방향 연관관계
        ㆍ테이블은 외래 키 하나로 두 테이블의 연관관계를 관리

        ㆍMEMBER.TEAM_ID 외래 키 하나로 양방향 연관관계 가짐 ( 양쪽으로 조인할 수 있다 )

    SELECT *
    FROM MEMBER M
    JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
    
    SELECT *
    FROM TEAM T
    JOIN MEMBER M ON T.TEAM_ID = M.TEAM_ID

     

    * 객체에서는 단방향 관계가 2개일 때 둘 중 하나로 외래 키를 관리해야 한다.

    - 연관관계의 주인(Owner)

    양방향 매핑 규칙

        ㆍ객체의 두 관계중 하나를 연관관계의 주인으로 지정

        ㆍ연관관계의 주인만이 외래 키를 관리(등록, 수정)

        ㆍ주인이 아닌쪽은 읽기만 가능

        ㆍ주인은 mappedBy 속성 사용X

        ㆍ주인이 아니면 mappedBy 속성으로 주인 지정

     

    - 누구를 주인으로?

        ㆍ외래 키가 있는 곳을 주인으로 정해라 - N:1 관계에서 외래키가 있는곳이 N이 된다.

        ㆍ여기서는 Member.team이 연관관계의 주인

     

    3. 양방향 연관관계와 연관관계의 주인2 - 주의점, 정리

    - 양방향 매핑시 가장 많이 하는 실수 (연관관계의 주인에 값을 입력하지 않음)

    // 저장
    Team team = new Team();
    team.setName("TeamA");
    em.persist(team);
    
    Member2 member = new Member2();
    member.setUsername("member1");
    
    // 역방향(주인이 아닌 방향)만 연관관계 설정 
    team.getMember2s().add(member); // mappedBy는 읽기 전용 // jpa에서 insert, update 할때 사용 X
    
    em.persist(member);

     

    - 양방향 매핑시 연관관계의 주인에 값을 입력해야 한다. ( 순수한 객체 관계를 고려하면 항상 양쪽다 값을 입력해야 한다 )

    Team team = new Team();
    team.setName("TeamA");
    em.persist(team);
    
    Member2 member = new Member2();
    member.setUsername("member1");
    
    team.getMember2s().add(member);
    // 연관관계의 주인에 값 설정
    member.setTeam(team);   //**
    em.persist(member);

        ㆍ양쪽 다 값을 입력안하고, flush, clear를 안해준다면 1차 캐시에서 값을 못찾을 수가 있기에 양쪽다 값을 입력한다

     

    - 양방향 연관관계 주의 - 실습

        ㆍ순수 객체 상태를 고려해서 항상 양쪽에 값을 설정하자

        ㆍ연관관계 편의 메소드를 생성하자

              º getter, setter는 로직이 없을때, 로직이 있다면 이름을 만들어줄 것!

    public void changeTeam(Team team) {
        this.team = team;
        team.getMember2s().add(this);
    }

        ㆍ양방향 매핑시에 무한 루프를 조심하자

              º 예: toString(), lombok, JSON 생성라이브러리

              º toString으로 서로서로를 호출 - lombok에서 toString 만드는 것은 쓰지마라! : StackOverflowError

              º controller에서 Entity를 절대 반환하지마라 - 무한루프, Entity 변경 문제

     

    - 양방향 매핑 정리

        ㆍ단방향 매핑만으로도 이미 연관관계 매핑은 완료

              º 처음에는 단방향 매핑으로   설계할 것

        ㆍ양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것 뿐

        ㆍJPQL에서 역방향으로 탐색할 일이 많음

        ㆍ단방향 매핑을 잘 하고 양방향은 필요할 때 추가해도 됨 ( 테이블에 영향을 주지 않음 )

     

    - 연관관계의 주인을 정하는 기준

        ㆍ비즈니스 로직을 기준으로 연관관계의 주인을 선택하면 안됨

        ㆍ연관관계의 주인은 외래 키의 위치를 기준으로 정해야함

     

    댓글

Designed by Tistory.