이 글은 김영한의 [자바 ORM 표준 JPA 프로그래밍 - 기본편]을 수강하며 정리한 글입니다.
👉 기본 환경
- Language: Java
- DB: H2 Database
- IDE: IntelliJ
1. Entity 상태
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
37
38
39
40
41
42
|
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String name;
@ManyToOne // Member : Team = Many : One
@JoinColumn(name = "TEAM_ID") // JoinColum = FK
private Team team;
// FK가 있는 곳(Many to One에서 Many 부분)이 연관 관계의 주인
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;
}
public Team getTeam() {
return team;
}
public void setTeam(Team team) {
this.team = team;
}
}
|
Member.java
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
37
38
39
|
@Entity
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "team") // mappedBy: 상대 Entity의 FK field 값
private List<Member> members = new ArrayList<>();
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;
}
public List<Member> getMembers() {
return members;
}
public void setMembers(List<Member> members) {
this.members = members;
}
}
|
Team.java
Member entity와 Team entity가 양방향으로 매핑되어있음
Member : Team = 다 : 1 관계
2-1. 데이터 저장
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
37
38
|
public class Main {
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.setName("Member1");
em.persist(member);
Team team = new Team();
team.setName("TeamA");
team.getMembers().add(member);
em.persist(team);
em.flush();
em.clear();
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
|
- Member 객체 생성 후, persist
- Team 객체 생성 후, getter로 member list 호출 후 member 객체 추가
Team 객체에 Member를 매핑하였지만, Member 객체의 Team_id가 null값으로 입력됨
⭐
@OneToMany(mappedBy = "")
양방향 매핑에서 mappedBy 속성은 단순히 매핑되어있음을 안내해주는 역할일 뿐, 실제로 데이터를 매핑시켜주지 않음
2-2. 데이터 저장 - 수정
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
37
|
public class Main {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("Member1");
member.setTeam(team);
em.persist(member);
em.flush();
em.clear();
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
|
- Team 객체 생성 후, persist
- Member 객체 생성 및 team 객체 주입 후, persist
Member 객체에 매핑한 Team 객체의 Team_id가 입력됨
⭐
@ManyToOne
@JoinColumn(name = "TEAM_ID")
양방향 매핑에서 실제 데이터 입력을 담당하는 필드는 @Joincolumn이 선언되어있는 필드
3. 데이터 조회
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
37
38
39
40
41
42
43
44
45
46
47
48
|
public class Main {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("Member1");
member.setTeam(team);
em.persist(member);
// em.flush();
// em.clear();
Team findTeam = em.find(Team.class, team.getId());
List<Member> members = findTeam.getMembers();
System.out.println("==========");
for(Member m : members){
System.out.println("member: " + m.getName());
}
System.out.println("==========");
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
|
- flush(), clear()를 제거하고, 같은 트랜잭션 내에서 1차 캐시에 저장된 team과 member를 바로 조회할 경우,
member 객체에 매핑된 team 객체를 조회할 수 있지만, team 객체에서 매핑된 member 조회할 수 없음
- 1차 캐시 상태에서는 Team 객체와 Member 객체의 관계가 명시적으로 코드로 작성한 부분을 제외하고는 인식되지 않음
▶ 양방향으로 객체 주입
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
public class Main {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("Member1");
member.setTeam(team);
em.persist(member);
team.getMembers().add(member);
// em.flush();
// em.clear();
Team findTeam = em.find(Team.class, team.getId());
List<Member> members = findTeam.getMembers();
System.out.println("==========");
for(Member m : members){
System.out.println("member: " + m.getName());
}
System.out.println("==========");
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
|
4. 코드 리팩토링
양방향으로 값을 세팅해주는 과정(setTeam, addMember)에서 일부 값만 세팅하는 실수가 발생할 수 있음
▶ 연관관계 편의 메서드를 생성하여, 하나의 메서드로 2개의 값을 세팅
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
37
38
39
40
41
42
43
|
public class Main {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("Member1");
member.changeTeam(team);
em.persist(member);
Team findTeam = em.find(Team.class, team.getId());
List<Member> members = findTeam.getMembers();
System.out.println("==========");
for(Member m : members){
System.out.println("member: " + m.getName());
}
System.out.println("==========");
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
|
- setTeam() 대신 changeTeam을 활용하여, setTeam과 addMember 수행
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
37
38
39
40
41
42
43
44
45
46
47
|
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String name;
@ManyToOne // Member : Team = Many : One
@JoinColumn(name = "TEAM_ID") // JoinColum = FK
private Team team;
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;
}
public Team getTeam() {
return team;
}
public void setTeam(Team team) {
this.team = team;
}
public void changeTeam(Team team) {
this.team = team;
team.getMembers().add(this);
}
}
|
cf. changeTeam() 대신 addMember() 사용 가능
⭐ 단, 연관관계 편의 메서드는 동일한 역할을 수행하므로 하나의 메서드만 생성
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
37
38
39
40
41
42
43
|
@Entity
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "team") // mappedBy: 상대 Entity의 FK field 값
private List<Member> members = new ArrayList<>();
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;
}
public List<Member> getMembers() {
return members;
}
public void setMembers(List<Member> members) {
this.members = members;
}
public void addMember(Member member) {
member.setTeam(this);
this.getMembers().add(member);
}
}
|
'Java > JPA' 카테고리의 다른 글
[JPA_Basic] 다대다 매핑 (0) | 2023.08.16 |
---|---|
[JPA_Basic] 일대다 매핑 시, ArrayList를 사용하는 이유 (0) | 2023.08.15 |
[JPA_Basic] 객체 지향 모델링 (0) | 2023.08.08 |
[JPA_Basic] 기본키 매핑 (0) | 2023.08.05 |
[JPA_Basic] 데이터베이스 스키마 자동 생성 (0) | 2023.07.31 |