다양한 연관관계 매핑

born_a·2022년 9월 1일
0

다대일

다대일을 가장 많이 쓰고, 다대다는 실무에서 쓰면 안된다.

단방향, 양방향

다대일 단방향

관계형 db에서는 '다'쪽에 외래키가 들어가줘야한다.
와래키 있는 쪽 기준으로 거기에다가 연관한 참조를 넣어줘서 매핑을 걸면 된다.

일대다

일대다 단방향

여기서는 '일'쪽이 연관관계의 주인이다.

실무에서 이 모델은 거의 가져가지 않는다.
기본 스펙에 있기 때문에 설명

db에선 무조건 '다'쪽에 외래키가 들어가야한다.
Team의 members를 변경하면 그게 MEMBER의 TEAM_ID를 업데이트 시켜 줘야한다.
Team.java

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

    private String name;

    @OneToMany
    @JoinColumn(name = "TEAM_ID")
    private List<Member> members = new ArrayList<>();

Member.java

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

    @Column(name = "USERNAME")
    private String username;


    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

JpaMain.java

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 {
            Member member = new Member();
            member.setUsername("member1");//실무에선 set 안쓰고 빌더패턴을 씀

            em.persist(member);

            Team team = new Team();
            team.setName("teamA");
            team.getMembers().add(member);

            em.persist(team);

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        }finally {
            em.close();
        }

        emf.close();
    }
}

Team, Member에 insert쿼리가 나가고 마지막에 update쿼리가 나간다.

테이블 수십개가 돌아가는 실무에선 잘쓰지 않는다.

일대다 단방향 정리

  • 일대다 단방향은 일대다(1:N)에서 일(1)이 연관관계의 주인
  • 테이블일대다관계는항상다(N)쪽에외래키가있음
  • 객체와 테이블의 차이 때문에 반대편 테이블의 외래 키를 관리하 는 특이한 구조
  • @JoinColumn을 꼭 사용해야 함. 그렇지 않으면 조인 테이블 방식을 사용함(중간에 테이블을 하나 추가함)
    :
@OneToMany
//@JoinColumn(name = "TEAM_ID")
private List<Member> members = new ArrayList<>();

@JoinColumn을 사용하지 않으면,

다음과 같은 TEAM_MEMBER가 생성된다.

일대다 양방향 정리

Member쪽에서도 team을 보고 싶어.
그럼

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

    @Column(name = "USERNAME")
    private String username;

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

매핑이나 설계는 단순해야한다.

일대일

JoinColumn 속성 중 name은 넣어줘야 한다. 안 넣으면 기본 값이 지저분 함.
Member.java

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

    @Column(name = "USERNAME")
    private String username;

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

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

Locker.java

@Entity
public class Locker {
    @Id
    @GeneratedValue
    private Long id;

    private String name;

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

일대일 : 대상 테이블에 외래키 양방향

일대일 관계는 '내 엔티티에 있는 외래키를 내가 관리!'
Locker에 member가 있으면 얘를 연관관계 주인으로 잡고, Locker locker는 읽기 전용으로 만들면 된다.
일대일 주 테이블에 외래 키 양방향과 매핑방향은 똑겉다.

일대일 정리

주 테이블 : 주로 액세스하는 테이블

다대다

실무에서는 쓰면 안된다.
디비는 중간 연결 테이블을 만들어서 풀어내야 한다.
객체는 다대다 관계가 가능하다.
ORM이 객체 그림과 테이블 그림을 매핑해준다.
테이블은 다대다가 안되니까 jointable을 집어넣어서 하는 매핑을 해준다.

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

    @Column(name = "USERNAME")
    private String username;

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

    @ManyToMany
    @JoinTable(name = "MEMBER_PRODUCT")
    private List<Product> products = new ArrayList<>();
    
    @OneToOne
    @JoinColumn(name = "LOCKER_ID")
    private Locker locker;


MEMBER_PRODUCT 테이블이 생성된것을 확인할 수 있다.

단방향을 양방향으로 만들고 싶으면 Product에 가서

@Entity
public class Product {
    @Id
    @GeneratedValue
    private Long id;
    private String name;

    @ManyToMany(mappedBy = "products")
    private List<Member> members = new ArrayList<>();

추가해주자.

다대다 매핑의 한계

중간 테이블엔 매핑 정보만 들어가고 추가 정보는 들어갈 수 없다.
또한 쿼리가 이상하게 나간다. -> 실무에서 쓰지 말자

다대다 한계 극복

  • @ManyToMany를 OneToMany, @ManyToOne으로 바꾸자.
  • 중간 테이블을 엔티티로 승격시키자

MemberProduct.java

@Entity
public class MemberProduct {

    @Id
    @GeneratedValue
    private Long id;

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

    @ManyToOne
    @JoinColumn(name = "PRODUCT_ID")
    private Product product;
}

Member.java

@OneToMany(mappedBy = "member")
    private List<MemberProduct> memberProducts = new ArrayList<>();

Product.java

 @OneToMany(mappedBy = "product")
    private List<MemberProduct> memberProducts = new ArrayList<>();

이제 내가 원하는 걸 마음대로 MemberProduct에 넣을 수 있음.
예제라 MemberProduct로 잡았지만,Orders 같은 의미있는 이름이 붙여지게 됨.
웬만하면 Pk는 비즈니스 적으로 의미없는 값을 써야한다.

실전 예제 3 - 다양한 연관관계 매핑

Category.java

@Entity
public class Category {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @ManyToOne
    @JoinColumn(name = "PARENT_ID")
    private Category parent;

    @OneToMany(mappedBy = "parent")
    private List<Category> child = new ArrayList<>();

    @ManyToMany
    @JoinTable(name = "CATEGORY_ITEM",
            joinColumns = @JoinColumn(name = "CATEGORY_ID"), //내가 조인하는건 얘고
            inverseJoinColumns = @JoinColumn(name = "ITEM_ID") //반대쪽인 조인하는건 얘야
    )
    private List<Item> items = new ArrayList<>();
}

Delivery.java

@Entity
public class Delivery {

    @Id@GeneratedValue
    private Long id;

    private String city;
    private String street;
    private String zipcode;
    private DeliveryStatus status;

    @OneToOne(mappedBy = "delivery")
    private Order order;

}

Order.java

@Entity
@Table(name = "ORDERS")
public class Order {

    @Id
    @GeneratedValue
    @Column(name = "ORDER_ID")
    private Long id;

    //@Column(name = "MEMBER_ID")
    //private Long memberId;

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

    @OneToOne
    @JoinColumn(name = "DELIVERY_ID")
    private Delivery delivery;

    @OneToMany(mappedBy = "order")
    private List<OrderItem> orderItems = new ArrayList<>();

    private LocalDateTime orderDate;

    @Enumerated(EnumType.STRING)
    private OrderStatus status;

Item.java

@Entity
public class Item {
    @Id @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long Id;

    private String name;

    @ManyToMany(mappedBy = "items")
    private List<Category> categories = new ArrayList<>();
    private int Price;
    private int stockQuality;

업로드중..
CATEGORY_ITEM 테이블이 제대로 생성되었다.
업로드중..
ORDERS 테이블에도 delivert가 제대로 들어있는걸 확인할 수 있다.

예시여서 다대다로 해봤는데 실전에선 이렇게 하면 안됨!

0개의 댓글

관련 채용 정보