자바 ORM 표준 JPA 프로그래밍 학습 내용 정리한 포스팅 입니다.
- 객체는 참조(주소)를 사용해서 관계를 맺고 테이블을 외래 키를 사용해서 관계를 맺는다.
- 방향(
Direction
) : 단방향과 양방향이 있으며 방향은 객체관계에만 존재하고 테이블 관계는 항상 양방향 이다.
- 다중성(
Multiplicity
) : 다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다 (N:N)
- 연관관계 주인 (
owner
) : 객체를 양방향 연관관계로 만들면 연관관계의 주인을 정해야 한다.
단방향 연관관계
객체 연관관계
- 회원 객체와 팀 객체는 단방향 관계 다.
- 회원은
Member.team
필드를 통해서 팀을 알 수 있지만 반대로 팀은 회원을 알 수 없다.
테이블 연관관계
- 회원 테이블과 팀 테이블은 양방향 관계 다.
- 회원 테이블의 TEAM_ID 외래키를 통해 회원팀과 조인 할 수 있고 반대로 팀과 회원도 조인할 수 있다.
1
2
|
SELECT * FROM MEMBER M JOIN TEAM T ON M.TEAM_ID = T.ID;
SELECT * FROM TEAM T JOIN MEMBER M ON T.ID = M.TEAM_ID;
|
정리
- 참조를 사용하는 객체의 연관관계는 단방향이다.
A -> B (a.b)
- 외래키를 사용하는 테이블의 연관관계는 양방향이다.
A JOIN B
도 가능 하고 B JOIN A
도 가능하다.
- 객체를 양항뱡으로 참조하려면 단방향 연관관계를 2개 만들어야 한다.
순수한 객체 연관관계
객체는 참조를 사용해서 연관관계를 탐색할 수 있고 이것을 객체 그래프 탐색이라고 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// 객체그래프 탐색 동작코드
public static void main(String[] args) {
Member member1 = new Member ("member1", "회원1");
Member member2 = new Member ("member2", "회원2");
Team team1 = new Team ("team1", "팀1");
member1.setTeam(team1);
member2.setTeam(team1);
// 참조를 사용하여 연관관계를 탐색한다.
Team findTeam = member1.getTeam();
}
|
테이블 연관관계
데이터베이스는 외래 키를 사용하여 연관관계를 탐색할 수 있으며 이것을 조인이라고 한다.
테이블 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
-- MEMBER
CREATE TABLE MEMBER (
MEMBER_ID VARCHAR(255) NOT NULL,
TEAM_ID VARCHAR(255),
USERNAME VARCHAR(255),
PRIMARY KEY (MEMBER_ID)
);
-- TEAM
CREATE TABLE TEAM(
TEAM_ID VARCHAR(255) NOT NULL,
NAME VARCHAR(255),
PRIMARY KEY (TEAM_ID)
);
ALTER TABLE MEMBER ADD CONSTRAINT FK_MEMBER_TEAM
FOREIGN KEY (TEAM_ID)
REFERENCES TEAM
;
|
SQL 을 통하여 연관관계를 정의하기
1
2
3
|
INSERT INTO TEAM (TEAM_ID, NAME) VALUES ('team1', '팀1');
INSERT INTO MEMBER (MEMBER_ID, TEAM_ID, USERNAME) VALUSE ('member1', 'team1', '회원1');
INSERT INTO MEMBER (MEMBER_ID, TEAM_ID, USERNAME) VALUSE ('member2', 'team1', '회원2');
|
객체 관계 매핑
회원 Entity(Member.java)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@Entity
public class Member {
@Id
@Column(name = "MEMBER_ID")
private String id;
private String username;
// 연관관계 매핑
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
// 연관관계 설정
public void setTeam(Team team) {
this.team = team;
}
// Getter, Setter...
}
|
매핑한 팀 엔티티(Team.java)
1
2
3
4
5
6
7
8
9
10
|
@Entity
public class Team {
@Id
@Column(name = "TEAM_ID")
private String id;
private String name;
// Getter, Setter ...
}
|
@ManyToOne
: 다대일(N:1) 관계 매핑 정보를 나타낸다. 회원객체를 기준으로 팀객체와는 다대일 관계.
@JoinColumn(name = "TEAM_ID")
: 조인 컬럼은 외래키를 매핑할 때 사용.
@JoinColumn
속성 |
기능 |
기본값 |
name |
매핑할 외래 키 이름 |
필드명 + _ + 참조하는 테이블의 기본키 컬럼명 |
referencedColumnName |
외래 키가 참조하는 대상 테이블의 컬럼명 |
참조하는 테이블의 키 컬럼명 |
foreignKey(DDL) |
테이블 생성시 사용. 외래키를 직접 지정 가능 |
|
unique, nullable, inserable, updateable, columnDefinition, table |
@Column 속성과 동일 |
|
@ManyToOne
속성 |
기능 |
기본값 |
optional |
false 로 설정시 연관된 엔티티가 항상 있어야 한다. |
true |
fetch |
글로벌 페치 전략 |
@ManytoOne = FetchType.EAGER, @OneToMany=FetchType.LAZY |
cascade |
영속성 전이 기능 사용 |
|
targetEntity |
연관된 엔티티 타입 정보를 설정. (이 기능은 거의 사용 안함) |
|
1
2
|
@OneToMany
private List<Member> members; // 제네릭타입으로 정보를 알 수 있다.
|
연관관계 사용
저장
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public static void testSave(EntityManager em) {
// 팀1 저장
Team team1 = new Team("team1", "팀1");
em.persist(team1);
// 회원1 저장
Member member1 = new Member("member1", "회원1");
member1.setTeam(team1); // 연관관계 설정 member1 -> team1
em.persist(member1);
// 회원2 저장
Member member2 = new Member("member2", "회원2");
member2.setTeam(team1);
em.persist(member2);
}
|
JPA 는 참조한 팀의 식별자 (Team.id
) 를 외래 키로 사용해서 적절한 등록 쿼리를 생성
조회
연관관계가 있는 엔티티를 조회하는 방법
- 객체 그래프 탐색 (객체 연관관계를 사용한 조회)
- 객체 지향 쿼리 사용 (
JPQL
)
객체그래프 탐색
member.getTeam()
을 사용하여 Member 와 연관된 Team 객체를 조회할 수 있다.
1
2
|
Member member = em.find(Member.class, "member1");
Team team = member.getTeam(); // 객체 그래프 탐색
|
객체지향 쿼리 사용
1
2
3
4
5
6
7
8
9
10
11
|
public static void queryLogicJoin(EntityManager em) {
String jpql = " SELECT m from Member m JOIN m.team t WHERE t.name = :teamName";
List<Member> resultList = em.createQuery(jpql, Member.class)
.setParameter("teamName", "팀1")
.getResultList();
for (Member member : resultList) {
System.out.println("[query] member.username = " + member.getUsername());
}
}
|
수정
- 트랜잭션을 커밋할 때 플러시가 일어나면서 변경 감지 기능이 작동한다. 이것은 연관관계를 수정할 때도 동일하며, 참조하는 대상만 변경하면 나머지는 JPA 에서 자동으로 처리한다.
1
2
3
4
5
6
7
8
9
|
public static void updateRelation(EntityManager em) {
// 새로운 팀
Team team2 = new Team("team2", "팀2");
em.persist(team2);
// 회원1 을 팀2로 설정한다.
Member member = em.find(Member.class, "member1");
member.setTeam(team2); // 플러시 발생시 자동으로 업데이트
}
|
연관관계 제거
1
2
3
4
|
private static void deleteRelation(EntityManager em) {
Member member1 = em.find(Member.class, "member1");
member1.setTeam(null);
}
|
연관된 엔티티 삭제
- 연관된 엔티티를 삭제하려면 기존에 있던 연관관계를 먼저 제거하고 삭제해야 한다.
1
2
3
|
member1.setTeam(null); // 회원1 연관관계 제거
member2.setTeam(null); // 회원2 연관관계 제거
em.remove(team); // 팀 삭제
|