jpa는 관계형 DB의 table을 객체처럼 관리한다. 따라서 jpa로 DB를 설계할때 table의 연관 관계와는 다르게 관계를 고려해야한다.
jpa를 사용하면 데이터를 객체에 관점에서 접근할 수 있다.
jpa에서 테이블과 객체와의 N:1 매핑을 할때 @ManyToOne
어노테이션을 사용해 매핑을 한다.
@ManyToOne
으로 N:1관계를 명시하고 JoinColumn(name = "TEAM_ID")
서 해당 객체가 TEAM_ID 외래키와 매핑관계임을 입력@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
@Entity
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
앞선 코드에서 Member와 Team 서로 양방향으로 접근하게 하려면 Team class코드에서 칼럼만 추가해주면 된다
@OneToMany(mappedBy = "team")
해당 어노테이션을 통해 One에서 Many 방향임을 명시하고 team(Member에 team변수)에 의해 매핑된다는 정보를 파라미터로 전달, 여기서 Team Class는 Member와 1대 N 관계를 갖기 때문에 members 변수를 이용해 N 개의 Member 객체를 포함한다.@Entity
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
private String name;
.
.
.
객체를 설계하다 보면 Team을 통해서 Member 방향으로만 접근하는 설계를 자주 볼 수 있다. 그러나 DB의 입장에서는 무조건 N 쪽에 외래키가 존재해야하기 때문에 이러한 설계는 나올 수가 없다.
주의: 해당 관계는 Team이 주인이지만 엔티티가 관리하는 외래키가 Team아닌 Member table에 존재한다. 따라서 어플리케이션에서 Team을 이용해 데이터를 변경하거나 추가하는 경우 Team에 대한 query가 DB에 전달 되는 것이 아닌 Member에 대한 query가 전달 된다. 실무에서는 수많은 table 수십개가 연관관계를 갖고 돌아가는 상황해서 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<>();
JoinColumn을 사용하지 않으면 Join Table 방식으로 동작 (중간 테이블을 이용해서 관리)
@Entity
public class Locker {
@Id
@GeneratedValue
@Column(name = "LOCKER_ID")
private Long id;
private String name;
// 양방향 매핑
//@OneToOne(mappedBy = "locker")
//private Member member;
}
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
@OneToOne
@JoinColumn(name = "LOCKER_ID")
private Locker locker;
'
'
'
관계형 데이터베이스는 정규화된 테이블 2개로 N:M 관계를 표현할 수 없다. 따라서 중간 테이블을 추가해 1:N, N:1 관계로 풀어야함.
객체는 컬렉션을 사용해 객체 2개로 N:M 관계를 표현 가능하다.
@Entity
public class Product {
@Id @GeneratedValue
private Long id;
private String name;
//양방향
//@ManyToMany(mappedBy = "products")
//private List(Member) members = new ArrayList<>();
}
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
@ManyToMany
@JoinTable(name = "MEMBER_PRODUCT") //MEMBER_PRODUCT란 이름으로 조인 테이블을 만들어 N:M 관계 관리
private List<Product> products = new ArrayList<>();
.
.
.
주의: 해당
@ManyToMany
어노테이션을 이용하여 N:M 관계를 표현하는 것은 실무에 적합하지 않다. 실무에서 사용하는 연결 테이블은 단순히 연결만 하고 끝나는 것이 아니라 다양한 추가 데이터가 들어올 수 있기 때문에 더 복잡한 중간 테이블 설계가 필요하기 때문
@ManyToMany
한계 극복중간 테이블인 MemberProduct를 만들어 극복
@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;
}
@Entity
public class Product {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "product")
private List<MemberProduct> memberProducts = new ArrayList<>();
}
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
@OneToMany(mappedBy = "member")
private List<MemberProduct> memberProducts = new ArrayList<>();
}