Junjangsee's Blog

JPA - Relation Mapping(관계 매핑)

2019-05-23

images

관계 매핑

관계 매핑에선 항상 두 엔티티가 존재해야합니다. 주종관계를 가지고 있으며 반대쪽 레퍼런스를 가진쪽이 주인 관계가 됩니다.

두 엔티티 클래스가 필요하기 때문에 기존 Account 클래스, 추가적으로 Study 클래스를 생성합니다.
여기서 Study는 Account가 Study를 다수로 생성할 수 있는 개념입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Entity
public class Study {

@Id
@GeneratedValue
private Long id;

private String name;

public Long getId() {
return id;
}

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

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

Entity

단방향

  • 관계를 정의한 쪽이 그 관계의 주인입니다.(@ManyToOne)
  • 기본값은 FK가 생성됩니다.
  • 기본값은 조인 테이블을 생성합니다.
  • @OneToMany로 매핑시 JOIN 테이블로 매핑됩니다.


@ManyToOne

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Entity
public class Study {

@Id
@GeneratedValue
private Long id;

private String name;

@ManyToOne
private Account owner;

public Account getOwner() {
return owner;
}

public void setOwner(Account owner) {
this.owner = owner;
}

public Long getId() {
return id;
}

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

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

Entity 스터디는 하나의 계정이 복수로 만들 수 있습니다. 그러므로 @ManyToOne을 사용합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Component
@Transactional
public class JpaRunner implements ApplicationRunner {

@PersistenceContext
EntityManager entityManager;

@Override
public void run(ApplicationArguments args) throws Exception {
Account account = new Account();
account.setUsername("kimjunhyeung");
account.setPassword("1234");

Study study = new Study();
study.setName("Spring Data JPA");
study.setOwner(account);

Session session = entityManager.unwrap(Session.class);

session.save(account);
session.save(study);
}
}

Study를 Account에 추가하는 코드를 작성합니다.
Entity owner라고 값을 주었지만 Account의 PK를 참고하는 owner_id라는 FK가 생성됩니다.



@OneToMany

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Entity
public class Account {

@Id
@GeneratedValue
private Long id;

@Column(nullable = false, unique = true)
private String username;

private String password;

@OneToMany
private Set<Study> studies = new HashSet<>();
}

Entity Account는 여러개의 Study를 가질 수 있기 때문에 Set에 담아서 @OneToMany를 선언합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Component
@Transactional
public class JpaRunner implements ApplicationRunner {

@PersistenceContext
EntityManager entityManager;

@Override
public void run(ApplicationArguments args) throws Exception {
Account account = new Account();
account.setUsername("kimjunhyeung");
account.setPassword("1234");

Study study = new Study();
study.setName("Spring Data JPA");

account.getStudies().add(study);

Session session = entityManager.unwrap(Session.class);

session.save(account);
session.save(study);
}
}

Set에 study를 add해줍니다.
Entity 2개가 아닌 3개의 테이블이 생겼는데 account_studies는 JOIN 테이블로서 각 테이블의 PK를 가지고 있게 됩니다.



양방향

  • FK를 가지고 있는 쪽이 주인입니다. 따라서 기본값은 @ManyToOne 가지고 있는 쪽이 주인입니다.
  • 주인이 아닌쪽(@OneToMany쪽)에서 mappedBy 사용해서 관계를 맺고 있는 필드를 설정해야 합니다.


Study 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Entity
public class Study {

@Id @GeneratedValue
private Long id;

private String name;

@ManyToOne
private Account owner;

}

`

Account를 종속시킨 주인 클래스로서 @ManyToOne을 선언합니다.

Account 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Entity
public class Account {

@Id
@GeneratedValue
private Long id;

@Column(nullable = false, unique = true)
private String username;

private String password;

@OneToMany(mappedBy = "owner")
private Set<Study> studies = new HashSet<>();
}
`

Entity mappedBy를 통해서 종속관계를 설정해주어야 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Component
@Transactional
public class JpaRunner implements ApplicationRunner {

@PersistenceContext
EntityManager entityManager;

@Override
public void run(ApplicationArguments args) throws Exception {
Account account = new Account();
account.setUsername("kimjunhyeung");
account.setPassword("1234");

Study study = new Study();
study.setName("Spring Data JPA");

account.getStudies().add(study); // 종속관계 관계 설정
study.setOwner(account); //주인이 되는 쪽 관계 설정

Session session = entityManager.unwrap(Session.class);

session.save(account);
session.save(study);
}
}

Entity 주인 관계를 설정하는 로직의 작성은 필수이며 종속관계의 로직은 필수는 아닙니다.
그러나 객체지향적으로 활용하기 위해 주종 모두 관계를 설정해주는 것이 좋습니다.


관계 설정 로직

1
2
account.getStudies().add(study);
study.setOwner(account);

관계를 설정할 때 사용했던 로직입니다. 결과만 놓고 봤을 때는 주인쪽에 관계를 설정하면 이상 없지만 객체지향이기 때문에 양방향 관계를 설정해주어야합니다. 그래서 항상 같이 다니기 때문에 하나의 메소드로 묶어주면 편리합니다. 이 메소드를 컨비니언스(convinience) 메소드 라고 합니다.


addStudy 메소드 생성

1
2
3
4
public void addStudy(Study study) {
this.getStudies().add(study);
study.setOwner(this);
}

Account 클래스에서 Study를 추가할 때마다 관계를 설정하는 메소드를 생성합니다.
여기서 this는 account를 가르킵니다.


메소드 활용

1
account.addStudy(study);

애플리케이션 구동시 생성한 메소드만 호출해주면 관계 설정 로직이 구동됩니다.