ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring] 연관 관계 매핑
    Backend/Spring 2022. 8. 25. 14:51

    단일 엔티티 매핑

    https://miraekwak.tistory.com/124

     

    [Spring] 단일 엔티티 매핑

    단일 엔티티 매핑 JPA는 엔티티 객체를 생성할 때, 기본 생성자 (Default Constructor)를 사용한다. @Entity : 영속성 컨텍스트에서 관리하는 객체임을 알림 속성 설명 name JPA에서 사용할 엔티티 이름을 지

    miraekwak.tistory.com

    단일 엔티티 매핑을 사용할 경우 데이터 중심 설계로 인해 잘못된 JPA 엔티티 객체를 사용하는 문제점이 발생한다. 이를 해결하는 것이 연관 관계 매핑!

     

     

    연관 관계 매핑

     

    객체 연관 관계 VS 테이블 연관 관계

    테이블 연관 관계

    create table member (id bigint not null, address varchar(255) not null, age integer not null, description varchar(255), name varchar(30) not null, nickName varchar(30) not null, primary key (id))
    create table orders (id varchar(255) not null, memo clob, order_datetime TIMESTAMP, orderStatus varchar(255), member_id bigint, primary key (id))
    
    ## 외래키를 통해 관계를 맺는다.
    alter table orders 
    add constraint fk_order_member_id foreign key (member_id) references member
    • 테이블은 외래키로 연관 관계를 맺는다.

     

    객체 연관 관계

    @Entity
    @Table(name = "orders")
    @Getter
    @Setter
    public class Order {
        @Id
        @Column(name = "id")
        private String uuid;
    
        @Column(name = "order_datetime", columnDefinition = "TIMESTAMP")
        private LocalDateTime orderDatetime;
    
        @Enumerated(EnumType.STRING)
        private OrderStatus orderStatus;
    
        @Lob
        private String memo;
    
    
    		//private Long memberId;
        @ManyToOne
        private Member member; // 참조를 통해서 관계를 맺는다.
    }
    • 객체 연관 관계의 경우 참조(주소)로 연관 관계를 맺는다.

     

    연관 관계

    방향(단방향, 양방향)

    • 회원 -> 주문 또는 주문 -> 회원 둘 중 한쪽만 참조하는 것을 단방향 관계라고 한다.
    class Member {
    	private long id;
    	private List<Order> orders; // 회원 -> 주문
    }
    
    class Order {
    	private String id;
    }
    
    Member member = new Member();
    Order order = meber.getOrders().get(0); // 회원 -> 주문에 참조가 가능하다.
    
    order.getMember() // (X)
    class Member {
    	private long id;
    }
    
    class Order {
    	private String id;
    	private Member member; // 주문 -> 회원
    }
    
    Order order = new Order();
    Member member = order.getMember(); // 주문 -> 회원에 참조가 가능하다.
    
    member.getOrders() // (X)

     

    • 회원 -> 주문, 주문 -> 회원 양쪽 모두 서로 참조하는 것을 양방향 관계라고 한다.
    class Member {
    	private long id;
    	private List<Order> orders; // 회원 -> 주문
    }
    
    class Order {
    	private String id;
    	private Member member; // 주문 -> 회원
    }
    
    Member member = new Member();
    Order order = member.getOrders().get(0); // 회원 -> 주문 참조 (O)
    order.getMember(); // 주문 -> 회원 참조 (O)

     

    • 테이블에서의 관계는 항상 양방향이다.
      • 외래키를 이용해서 양방향으로 조인이 가능하다.
    SELECT * FROM member AS m JOIN orders AS o 
    ON m.id = o.member_id;
    
    SELECT * FROM orders AS o JOIN member AS m
    ON o.member_id = m.id

     

    다중성(다대일, 일대다, 다대다)

    • 회원은 여러 주문을 할 수 있기 때문에, 회원(1)은 주문(N)은 일대다 관계이다.
    • 주문은 여러 회원에 의해 발생할 수 있기 때문에, 주문(N)과 회원(1)은 다대일 관계이다.

     

    연관관계 주인(mappedBy)

    • 객체를 양방향 연관관계로 만들면, 연관관계의 주인을 정해야 한다.
    • 외래키를 관리할 객체를 지정한다. (INSERT, UPDATE, DELETE)
    • 연관관계 주인만이, 외래키를 등록/수정/삭제 할 수 있다.
      • 주인이 아닌 쪽은 읽기만 가능하다. (SELECT)
    • 테이블 중 FK가 있는 쪽이 연관관계 주인이 된다. 
      • 회원(1) - 주문(N) 에서는 주문 엔티티가 연관관계의 주인이 된다. 

     

    객체 그래프 탐색

    class Member {
    	private long id;
    	private List<Order> orders;
    }
    
    class Order {
    	private String id;
    	private Member member;
    }
    
    ... 
    @Test
    void graph() {
    	Member member1 = new Mebmer(1);
    	Order order1 = new Order(1)
    
    	member1.setOrders(Lists.newArrayList(order1));
    
    	Order findOrder= member1.getOrders().get(o); // 객체 그래프 탐색이라 한다.
    	findOrder.getMember();
    }
    • 객체의 참조를 통해서 서로를 가져오는 것을 객체 그래프 탐색이라고 한다.

     

    JPA 엔티티 객체 관계 매핑

    단일 엔티티 매핑

    단방향 연관관계 매핑

    @Entity
    @Table(name = "orders")
    @Getter
    @Setter
    public class Order {
        @Id
        @Column(name = "id")
        private String uuid;
    
        @Column(name = "order_datetime", columnDefinition = "TIMESTAMP")
        private LocalDateTime orderDatetime;
    
        @Enumerated(EnumType.STRING)
        private OrderStatus orderStatus;
    
        @Lob
        private String memo;
        
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "member_id", referencedColumnName = "id")
        private Member member;
    }
    • @ManyToOne 을 사용하여 단방향 연관관계 설정
    • @JoinColumn
      • name의 디폴트 값은 명시한 필드명에 언더바를 붙이고 pk값을 따라가는게 fk값이 된다.
      • referencedColumnNamed의 디폴트 값은 명시한 엔티티의 Id를 찾는다.

     

    양방향 연관관계 매핑

    @Entity
    @Table(name = "member")
    @Getter
    @Setter
    public class Member {
        @Id
        @GeneratedValue(strategy = GenerationType.SEQUENCE)
        private Long id;
    
        @Column(name = "name", nullable = false, length = 30)
        private String name;
    
        @Column(nullable = false, length = 30, unique = true)
        private String nickName;
    
        private int age;
    
        @Column(name = "address", nullable = false)
        private String address;
    
        @Column(name = "description", nullable = true)
        private String description;
    
        @OneToMany(mappedBy = "member") // 연관관계 주인은 order.member
        private List<Order> orders;
    
        // 연관관계 편의 메소드
        public void addOrder(Order order) {
        order.setMember(this);
        }
    }
    • mappedBy에서는 연관관계 주인인 Order의 Member 객체 명을 적는다.
    @Entity
    @Table(name = "orders")
    @Getter
    @Setter
    public class Order {
        @Id
        @Column(name = "id")
        private String uuid;
    
        @Column(name = "order_datetime", columnDefinition = "TIMESTAMP")
        private LocalDateTime orderDatetime;
    
        @Enumerated(EnumType.STRING)
        private OrderStatus orderStatus;
    
        @Lob
        private String memo;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "member_id", referencedColumnName = "id")
        private Member member;
    
    
        // 연관관계 편의 메소드
        public void setMember(Member member) {
            if(Objects.nonNull(this.member)) {
                this.member.getOrders().remove(this);
            }
    
            this.member = member;
            member.getOrders().add(this);
        }
    }
    • 연관관계 편의 메소드는 양방향의 경우 서로 설정이 필요하기 때문에 번거로움을 해결한다.
    • 한 쪽에서 설정을 할 경우 다른 쪽에서 알아서 설정되도록 한다.

     

    애노테이션 정리

     

    @ManyToOne()

    속성 설명
    fetch FetchType을 설정하는 것으로 LAZY, EAGER가 존재한다.
    LAZY : 필요할 때 가져옴
    EAGER : 바로 가져옴

     

    @JoinColumn

    속성 설명 디폴트 값
    name join을 위한 컬럼명 명시한 필드명_pk값
    referencedColumnName 참조 컬럼 명 명시한 엔티티의 Id

     

    @OneToMany

    속성 설명
    mappedBy 연관관계 주인의 참조 객체 명

     

    댓글

Designed by Tistory.