JPA 연관관계 매핑 - 단방향매핑(@ManyToOne, @OneToMany, @OneToOne)


JPA 연관관계 매핑

엔티티들은 어떠한 키를 기준으로 여러 엔티티와 연관 관계를 가지고 있다. JPA 의 객체 관계 매핑은 이해 하지 못하면 매우 어렵다.
JPA 에서의 연관관계는 크게 아래와 같다.

1) 방향성(Direction)

  • 단방향 : 회원정보 -> 강사 또는 강사 -> 회원정보 중 한 쪽만 참조되는 경우
  • 양방향 : 회원정보 -> 강사, 강사 -> 회원정보 으로 양쪽으로 참조되는 경우

2) 다중성(Multiplicity)

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

3) 연관관계의 주인(Owner)

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

방향성(Direction)

JPA 객체 관계 매핑은 방향성을 가지고 있다. 이 방향성은 엔티티의 관계에 따라서 달라진다.
@ManyToOne(N:1) 매핑 예제를 통해서 JPA의 방향성 중 단방향에 대해서 알아보자.

  • 회원 엔티티의 속성은 아래와 같다.
    • 회원은 한명의 강사에 속할 수 있다.
    • 회원과 강사는 다대일(N:1) 관계이다.

    맴버ERD

  • 테이블 스크립트
CREATE TABLE MEMBER(
      ID VARCHAR(255) NOT NULL, --아이디(기본 키)
      NAME VARCHAR(255),             --이름
      AGE INTEGER NOT NULL,       --나이
      ADDRESS VARCHAR(255),      --주소
      TEACHER_ID VARCHAR(255), --강사ID
      PRIMARY KEY (ID)
  )

CREATE TABLE TEACHER (
      TEACHER_ID VARCHAR(255) NOT NULL, --강사ID(기본 키)
      NAME VARCHAR(255),                --이름
      PRIMARY KEY (TEACHER_ID)
  )

1) 관계매핑 및 회원 등록

  • 단방향 Annotation 정보

    • @ManyToOne : 다대일(N:1) 매핑
    • @OneToMany : 일대다(1:N) 매핑
    • @OneToOne : 일대일(1:1) 매핑
    • @JoinColumn(생략가능) : 조인 컬럼은 외래 키를 맵핑 할때 사용 됨.
  • Member Entity

      @Entity
      @Table(name = "MEMBER")
      public class Member {
          @Id
          private String id;
            
          private String name;
            
          private int age;
            
          private String address;
            
          @ManyToOne
          @JoinColumn(name = "TEACHER_ID")
          private Teacher teacher;
            
          public Member(String id, String name, String address) {
              super();
              this.id = id;
              this.name = name;
              this.address = address;
          }
          public void setTeacher(Teacher teacher) {
              this.teacher = teacher;
          }
      }
    
  • Teacher Entity

      @Entity
      public class Teacher {
          @Id
          @Column(name = "TEACHER_ID")
          private String id;
            
          private String name;
            
          public Teacher(String id, String name) {
              this.id = id;
              this.name = name;
          }
      }
    
  • Main Class

      public class Main 
      {
          public static void main( String[] args )
          {
              //엔티티 매니저 팩토리 생성 
              EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpaExam");
              //엔티티 매니저 생성
              EntityManager em = emf.createEntityManager();
              //엔티티 트렌젝션 생성
              EntityTransaction tx = em.getTransaction();
              try {
                  //트랜잭션 시작
                  tx.begin(); 
                    
                  //강사 생성
                  Teacher teacher1 = new Teacher("1", "이일타");
                  Teacher teacher2 = new Teacher("2", "김이타");
                    
                  //등록
                  em.persist(teacher1);
                  em.persist(teacher2);
                    
                  //맴버 생성
                  Member member1 = new Member("1", "김철수", 23, "서울특별시");
                  member1.setTeacher(teacher1);
                  em.persist(member1);
                    
                  Member member2 = new Member("2", "이영희", 21,"부산광역시");
                  member2.setTeacher(teacher1);
                  em.persist(member2);
                    
                  Member member3 = new Member("3", "박수지", 22, "광주광역시");
                  member3.setTeacher(teacher2);
                  em.persist(member3);
                    
                  tx.commit();//트랜잭션 커밋
              } catch (Exception e) {
                  e.printStackTrace();
                  tx.rollback(); //트랜잭션 롤백
              } finally {
                  em.close(); //엔티티 매니저 종료
              }
              emf.close(); //엔티티 매니저 팩토리 종료
          }
      }
    
  • 회원 등록 수행 결과

    • 회원 엔티티는 강사 엔티티에 @ManyToOne 을 사용하여 다대일(N:1) 매핑이 되어있고 이때 @JoinColumn(name = “TEACHER_ID”) 외래키 설정을 통해 “TEACHER_ID” 로 연관 관계가 맺어 졌다.

    • 회원 데이터

      맴버 데이터

    • 강사 데이터

      강사 데이터

2) 조회

    //조회
    Member findMember1 = em.find(Member.class, "1");
    System.out.println("member1 : " + findMember1);

3) 수정

  • memeber1 의 강사를 teacher1 에서 teacher2로 수정 해보자.
    //조회
    Member findMember1 = em.find(Member.class, "1");
    System.out.println("member1 : " + findMember1);    
    //수정
    findMember1.setTeacher(teacher2);
    System.out.println("member1 : " + findMember1);

4) 연관관계 제거

    //연관관계 제거
    findMember1.setTeacher(null);

5) 회원 삭제

  • 회원 정보를 삭제 하기 위해서는 기존의 연관 관계를 먼저 제거하고 삭제 해야 한다. 그렇지 않으면 외래키 오류로 DB Exception 이 발생 되게 된다.
    //연관관계 제거
    findMember1.setTeacher(null);
    //삭제
    em.remove(findMember1);

마무리

이번 포스팅에서는 @ManyToOne 예제를 통해서 JPA 의 방향성 중 단방향 매핑에 대해서 알아보았다. 다음 포스팅에서는 양방향 매핑에 대해서 정리해보자.

Back to blog