Skip to content

Commit

Permalink
add Spring Boot JPA :: ch.14 part1
Browse files Browse the repository at this point in the history
  • Loading branch information
now-water committed Jun 26, 2021
1 parent ee2eed1 commit be60c8a
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 42 deletions.
11 changes: 7 additions & 4 deletions content/jpa/ch14.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ date: '2021-01-28'

## 🤸‍♂️목차

- 컬렉션
- **컬렉션** - 다양한 컬렉션과 특징을 설명한다.

* Converter
* **컨버터** - 엔티티의 데이터를 변환해서 데이터베이스에 저장한다.

- 리스너
- **리스너** - 엔티티에서 발생한 이벤트를 처리한다.

* **엔티티 그래프** - 엔티티를 조회할 때 연관된 엔티티들을 선택해서 함께 조회한다.

---

* 엔티티 그래프
78 changes: 40 additions & 38 deletions content/jpa/ch14/14-1.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,57 +6,58 @@ tags: ['Spring Boot JPA']
date: '2021-01-28'
---

- 컬렉션 : 다양한 컬렉션과 특징을 설명한다.

* 컨버터 : 엔티티의 데이터를 변환해서 데이터베이스에 저장한다.

- 리스너 : 엔티티에서 발생한 이벤트를 처리한다.

* 엔티티 그래프 : 엔티티를 조회할 때 연관된 엔티티들을 선택해서 함께 조회한다.

## 컬렉션
## 14.1.1 JPA와 컬렉션

- `@OneToMany`, `@ManyToMany` 를 사용해서 일대다나 다대다 엔티티 관계를 매핑할 때 사용

* `@ElementCollection`을 사용해서 값 타입을 하나 이상 보관할 때 사용

하이버네이트는 엔티티를 영속 상태로 만들 때 **컬렉션 필드를 하이버네이트에서 준비한 컬렉션(PersistentBag)으로 감싸서 사용**한다.
하이버네이트는 엔티티를 영속 상태로 만들 때 **컬렉션 필드를 하이버네이트에서 제공하는 컬렉션(PersistentBag)으로 감싸서 사용**한다.

> 컬렉션을 효율적으로 관리하기 위해 엔티티를 영속 상태로 만들 때 원본 컬렉션을 감싸는 내장 컬렉션을 생성해서 사용.
>
> 래퍼 컬렉션이라고도 부른다.
> 이를 **래퍼 컬렉션**이라고도 부른다.
>
> 따라서 컬렉션 사용 시 즉시 초기화해서 사용하는 것을 권장한다.
>
> `Collection<Member> members = new ArrayList<Member>();`
---
### 하이버네이트 내장 컬렉션과 특징

| 컬렉션 인터페이스 | 내장 컬렉션 | 중복 허용 | 순서 보관 |
| :-----------------: | :------------: | :-------: | :-------: |
| Collection, List | PersistenceBag | O | X |
| Set | PersistenceSet | X | X |
| List + @OrderColumn | PersistentList | O | O |

### Collection, List
---

- 중복을 허용하는 컬렉션. `PersistentBag` 을 래퍼 컬렉션으로 사용
## 14.1.2 Collection, List

* `ArrayList` 로 초기화한다.
- 중복을 허용하는 컬렉션. `PersistentBag` 을 래퍼 컬렉션으로 사용 -> `ArrayList` 로 초기화한다.

- 엔티티를 추가할 때 중복된 엔티티가 있는지 비교하지 않고 단순히 저장만 한다.

* **엔티티를 추가해도 지연 로딩된 컬렉션을 초기화하지 않는다.**

### Set
- 같은 엔티티가 있는지 찾거나 삭제 시 `equals()` 메소드를 사용한다. => `.contains(인스턴스)`, `.remove(인스턴스)`

---

## 14.1.3 Set

- 중복을 허용하지 않는 컬렉션. `PersistentSet`을 래퍼 컬렉션로 사용
- 중복을 허용하지 않는 컬렉션. `PersistentSet`을 래퍼 컬렉션로 사용 -> `HashSet` 으로 초기화한다.

* `HashSet` 으로 초기화한다.

- 중복을 허용하지 않으므로 `add()` 메소드로 객체를 추가할 때 마다 `equals()` 메소드와 `hashcode()` 로 같은 객체가 있는지 비교한다.

* 같은 객체가 없으면 객체를 추가하고 `true` 반환, 같은 객체가 있으면 추가에 실패하고 `false` 반환
* 같은 객체가 없으면 객체를 추가하고 `true` 반환, 같은 객체가 있으면 추가에 실패하고 `false` 반환

- **중복된 엔티티가 있는지 비교하기 때문에 엔티티를 추가할 때 지연 로딩된 컬렉션을 초기화한다.**

- **중복된 엔티티가 있는지 비교하기 때문에 엔티티를 추가할 때 지연 로딩된 컬렉션을 초기화한다.**
---

### List + @OrderColumn
## 14.1.4 List + @OrderColumn

- `List` 인터페이스에 `@OrderColumn`을 추가하면 순서가 있는 특수한 컬렉션으로 인식한다. -> DB에 순서 값을 저장해서 조회할 때 사용한다.

Expand Down Expand Up @@ -98,7 +99,7 @@ public class Comment {
}
```

<br/>
### @OrderColumn 사용 코드

```java

Expand All @@ -112,47 +113,45 @@ em.persist(comment1);

Comment comment2 = new Comment("댓글2");
comment2.setBoard(board);
board.getComments().add(comment2); // POSITION 0
board.getComments().add(comment2); // POSITION 1
em.persist(comment2);

Comment comment3 = new Comment("댓글3");
comment3.setBoard(board);
board.getComments().add(comment3); // POSITION 0
board.getComments().add(comment3); // POSITION 2
em.persist(comment3);

Comment comment4 = new Comment("댓글4");
comment4.setBoard(board);
board.getComments().add(comment4); // POSITION 0
board.getComments().add(comment4); // POSITION 3
em.persist(comment4);

```

<br/> 결과 <br/>
### 결과

#### BOARD

| ID | TITLE | CONTENT |
| :-: | :---: | :-----: |
| :---: | :---: | :-----: |
| 1 | 제목1 | 내용1 |

<br/>


#### COMMENT

| ID | COMMENT | COMMENTS_ID(FK) | POSITION (@OrderColumn) |
| :-: | :-----: | :-------------: | :---------------------: |
| :---: | :-----: | :-------------: | :---------------------: |
| 1 | 댓글1 | 1 | 0 |
| 2 | 댓글2 | 1 | 1 |
| 3 | 댓글3 | 1 | 2 |
| 4 | 댓글4 | 1 | 3 |

---

### 단점

다음의 단점들로 실무에서는 잘 사용하지 않는다.
### @OrderColumn 단점

개발자가 직접 `POSITION` 값을 관리하거나 `@OrderBy`를 사용하길 권장한다.
여러 단점들로 실무에서는 잘 사용하지 않는다.

- `@OrderColumn``Board` 엔티티에서 매핑하므로 `Comment``POSITION`의 값을 알 수 없다.

Expand All @@ -166,9 +165,15 @@ em.persist(comment4);

- 중간에 `POSITION` 값이 없으면 조회한 `List` 에는 `null`이 보관된다. 이 경우 `List` 를 조회하면 컬렉션을 순회할 때 `NullPointerException`이 발생한다.

- 예를 들어 댓글 2를 데이터베이스에서 강제로 삭제하고 다른 댓글의 POSITION 값을 수정하지 않으면, POSITION 값은 `[0, 2, 3]` 이 되어서 1번 위치 조회 시 예외 발생

<br/>

> 이러한 단점들 때문에 대신에 개발자가 직접 `POSITION` 값을 관리하거나 `@OrderBy`를 사용하길 권장한다.
---

## @OrderBy
## 14.1.5 @OrderBy

- 데이터베이스의 `ORDER BY` 절을 사용해서 컬렉션을 정렬한다. 따라서 순서용 컬럼을 매핑하지 않아도 된다.

Expand All @@ -190,7 +195,7 @@ public class Team {

@OneToMany(mappedBy = "team")
@OrderBy("username desc, id asc") // Member의 username 필드로 내림차순, id로 오름차순 정렬
private Set<Member> members = new HashSet<Member>();
private Set<Member> members = new HashSet<Member>(); // 이때 순서 유지를 위해 LinkedHashSet을 내부에서 사용
...
}

Expand All @@ -208,7 +213,4 @@ public class Member {
private Team team;
...
}

```

---

0 comments on commit be60c8a

Please sign in to comment.