beadss / jpa-study

jpa슽터디입니다
1 stars 2 forks source link

6장 요약 #14

Open 2xel opened 5 years ago

2xel commented 5 years ago

5장 내용정리

엔티티 연관관계를 매핑할 때 고려사항

  1. 다중성 (두 엔티티가 일대일 관계, 일대다 관계인지)
  2. 단방향, 양방지(두 엔티티 중 한쪽만 참조하는 단방향 관계인지 서로 참조하는 양방향 관계인지)
  3. 연관관계의 주인(양방향 연관관계일시 연관관계의 주인)

다중성

단방향 양방향

연관관계의 주인


6장 다양한 연관관계 매핑

1. 다대일

1.1 다대일 단방향[N:1]

// 회원 엔티티
@Entity
public class Member {
    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    private String username;

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

    // Getter, Setter ...
}
// 팀 엔티티
@Entity
public class Team {
    @Id @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;

    private String name;

    // Getter, Setter ...
}

1.2 다대일 양방향(N:1, 1:N)

// 회원 엔티티
@Entity
public class Member {
    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    private String username;

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

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

        // 무한루프에 빠지지 않도록 체크
        if(!team.getMembers().contains(this)){
            team.getMembers().add(this);
        }
    }
    // Getter, Setter ...
}
// 팀 엔티티
@Entity
public class Team {
    @Id @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;

    private String name;

    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<Member>();

    public void addMember(Member member) {
        this.members.add(member);
        if(member.getTeam() != this) {
            // 무한루프에 빠지지 않도록 체크
            member.setTeam(this);
        }
    }
    // Getter, Setter ...
}

2. 일대다

2.1 일대다 단방향[1:N]

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

    private String name;

    @OneToMany
    @JoinColumn(name = "TEAM_ID")   // MEMBER 테이블의 TEAM_ID(FK)
    private List<Member> members = new ArrayList<Member>();

    // Getter, Setter ...
}
// 회원 엔티티
@Entity
public class Member {
    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    private String username;

    // Getter, Setter ...
}

일대다 단방향 매핑의 단점

// 일대다 단방향 매핑의 단점
public void testSave() {
    Member member1 = new Member("member1");
    Member member2 = new Member("member2");

    Team team1 = new Team("team1");
    team1.getMembers().add(member1);
    team1.getMembers().add(memeber2);

    em.persist(member1);    // INSERT-member1
    em.persist(member2);    // INSERT-member2
    em.persist(team1);      // INSERT-team1, UPDATE-member.fk, UPDATE-member2.fk

    transaction.commit();
}
insert into Member (MEMBER_ID, username) values (null, ?)
insert into Member (MEMBER_ID, username) values (null, ?)
insert into Team (TEAM_ID, name) values (null, ?)
update Member set TEAM_ID=? where MEMBER_ID=?
update Member set TEAM_ID=? where MEMBER_ID=?

일대다 단방향 매핑보다는 다대일 양방향 매핑을 사용하자

2.2 일대다 양방향[1:N, N:1]

// 일대다 양방향 팀 엔티티
@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>();

    // Getter, Setter ...
}
// 일대다 양방향 회원 엔티티
@Entity
public class Member {
    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;
    private String username;

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

    // Getter, Setter
}

3. 일대일[1:1]

주 테이블에 외래키

대상 테이블에 외래키

3.1 주 테이블에 외래키

단방향

// 일대일 주 테이블에 외래 키, 단방향 예제 코드
@Entity
public class Member {
    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    private String username;

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

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

    private String naame;
    ...
}

양방향

// 일대일 주 테이블에 외래 키, 양방향 예제 코드
@Entity
public class Member {
    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    private String username;

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

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

    private String naame;

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

3.2 대상 테이블에 외래키

단방향

양방향

// 일대일 주 테이블에 외래 키, 단방향 예제 코드
@Entity
public class Member {
    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    private String username;

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

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

    private String naame;

    @OneToOne
    @JoinColumn(name = "MEMBER_ID")
    private Member member;
    ...
}

4. 다대다[N:N]

4.1 다대다: 단방향

// 다대다 단방향 회원
@Entity
public class Member {
    @Id @Column(name = "MEMBER_ID")
    private String id;

    private String username;

    @ManyToMany
    @JoinTable(name = "MEMBER_PRODUCT"
                ,joinColumns = @JoinColumn(name = "MEMBER_ID")
                ,inverseJoinColumns = @JoinColumn(name = "PRODUCT_ID"))
    private List<Product> products = new ArrayList<Product>();
    ...
}
// 다대다 단방향 상품
@Entity
public class Product {
    @Id @Column(name = "PRODUCT_ID")
    private String id;

    private String name;
    ...
}

연결 테이블을 매핑하는 @JoinTable 속성 정리

// 저장
public void save() {
    Product productA = new Product();
    productA.setId("productA");
    productA.setName("상품A");
    em.persist(productA);

    Member member1 = new Member();
    member1.setId("member1");
    member1.setUsername("회원1");
    member1.getProducts().add(ProductA);    // 연관관계 설정
    em.persist(member1);
}
INSERT INTO PRODUCT ...
INSERT INTO MEMBER ...
INSERT INTO MEMBER_PRODUCT ...
// 탐색
public void find() {
    Member member = em.find(Member.class, "member1");
    List<Product> products = member.getProducts();  // 객체 그래프 탐색
    for(Product product : products) {
        System.out.println("product.name = " + product.getName());
    }
}
-- member.getProducts()를 호출해서 상품 이름을 출력 SQL

SELECT * FROM MEMBER_PRODUCT MP
INNER JOIN PRODUCT P ON MP.PRODUCT_ID=P.PRODUCT_ID
WHERE MP.MEMBER_ID=?

4.2 다대다: 양방향

// 역방향 추가
@Entity
public class Product {
    @Id
    private String id;

    @ManyToMany(mappedBy = "products")  // 역방향 추가
    private List<Member> members;
    ...
}

다대다의 양방향 연관관계 설정

member.getProducts().add(product);
product.getMembers().add(member);

양방향 연관관계 편의 메소드 추가

public void addProduct(Product product) {
    ...
    products.add(product);
    product.getmembers().add(this);
}

양방향 연관관계로 설정후 역방향으로 객체 그래프 탐색

// 역방향 탐색
public void findInverse() {
    Products product = em.find(Product.class, "productA");
    List<Member> members = products.getMembers();
    for(Member member : members) {
        System.out.println("member = " + member.getUsername());
    }
}

4.3 다대다: 매핑의 한게와 극복, 연결 엔티티 사용

// 회원 코드
@Entity
public class Member {
    @Id @Column(name = "MEMBER_ID")
    private String id;

    // 역방향
    @OneToMany(mappedBy = "member")
    private List<MemberProduct> memberProducts;
    ...
}
// 상품 코드(객체 그래프 탐색이 필요하지 않다고 판단해 연관관계를 만들지 않음)
@Entity
public class Product {
    @Id @Column(name = "PRODUCT_ID")
    private String id;

    private String name;
    ...
}
// 회원상품 엔티티 코드
@Entity
@IdClass(MemberProductId.class)
public class MemberProduct {
    @Id
    @ManyToOne
    @JoinColumn(name = "MEMBER_ID")
    private Member member;  // MemberProductId.member와 연결

    @Id
    @ManyToOne
    @JoinColumn(name = "PRODUCT_ID")
    private Product product;    // MemberProductsId.product와 연결

    private int orderAmount;
    ...
}
// 회원상품 식별자 클래스
public class MemberProductId implements Serializable {
    private String member;  // MemberProduct.member와 연결
    private String product; // MemberProduct.product와 연결

    // hashCode and equals

    @Override
    public boolean equals(Object o) {...}

    @Override
    public int hasCode() {...}
}

복합 기본키

식별관계

// 저장하는 코드
public void save() {
    // 회원저장
    Member member1 = new Member();
    member1.setId("member1");
    member1.setUsername("회원1");
    em.persist(member1);

    //상품저장
    Product productA = new Product();
    productA.setId("productA");
    productA.setName("상품1");
    em.persist(productA);

    //회원상품 저장
    MemberProduct memberProduct = new MemberProduct();
    memberProduct.setMember(member1);   // 주문 회원 - 연관관계 설정
    memberProduct.setProduct(productA); // 주문 상품 - 연관관계 설정
    memberProduct.setOrderAmount(2);    // 주문수량
    em.persist(memberProduct);
}
// 조회코드
public void find() {
    // 기본키 값 생성
    MemberProductId memberProductId = new MemberProductId();
    memberProductId.setMember("member1");
    memberProductId.setProduct("productA");

    MemberProduct memberProduct = em.find(MemberProduct.class, memberProductId);

    Member member = memberProduct.getMember();
    Product product = memberProduct.getProduct();

    System.out.println("member = " + member.getUsername());
    System.out.println("product = " + product.getName());
    System.out.println("orderAmount = " + memberProduct.getOrderAmount());
}

4.4 다대다 : 새로운 기본키 사용

// 주문코드
@Entity
public class Order {
    @Id @GeneratedValue
    @Column(name = "ORDER_ID")
    private Long id;

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

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

    private int orderAmount;
    ...
}
// 저장 코드
public void save() {
    // 회원 저장
    Member member1 = new Member();
    member1.setId("member1");
    member1.setUsername("회원1");
    em.persist(member1);

    // 상품 저장
    Product productA = new Product();
    productA.setId("productA");
    productA.setName("상품1");
    em.persist(productA);

    // 주문 저장
    Order order = new Order();
    order.setMember(member1);   // 주문 회원 - 연관관계 설정
    order.setProduct(productA); // 주문 상품 - 연관관계 설정
    order.setOrderAmount(2);    // 주문 수량
    em.persist(order);
}
// 조회 코드
public void find() {
    Long orderId = 1L;
    Order order = em.find(Order.class, orderId);

    Member member = order.getMember();
    Product Product = order.getProduct();

    System.out.println("member = " + member.getUsername());
    System.out.println("product = " + product.getName());
    System.out.println("orderAmount = " + order.getOrderAmount());
}

4.5 다대다 연관관계 정리

joont92 commented 5 years ago

역시!