<commit_before>---
layout: post
title: "JPA - 엔티티와 영속성"
description: "또 DB를 날릴 수는 없다."
tags: [develop, programming, java, jpa, db]
---

# 들어가기 전에

* JPA 설정 잘못 만졌다 DB 예쁘게 날린 허망함을 담아 작성
* 코드는 없이 개념만 설명

# JPA

> JPA는 Java의 ORM 기술 표준이다.

## 태초에 ORM이 있었다

ORM은 Object-Relational Mapping 의 두문자어이며, 객체-관계 매핑이라고 번역하는 사람이나 서적도 많이 보인다. 대충 봐도, OOP와 RDB를 엮어주는 기술 같아 보이며, 실제로도 ORM은 객체지향 언어에서 나타나는 객체와 관계형 데이터베이스에서 나타나는 데이터의 미묘한 차이를 줄이기 위해 등장한 기술이다.

### 객체지향과 데이터

객체는, 보통 __데이터와 관련된 연산의 집합__ 을 이르는 말이다. 데이터의 묶음은 보통 자료구조라고 한다.(보통, 자료구조에는 데이터 입출력에 관련한 연산이 수반되는 경우가 많다.) 객체의 큰 특징으로 `캡슐화` , `추상화` , `다형성` , `상속` 의 네 가지를 드는 경우가 많다.

관계형 데이터베이스에서, 데이터는 연산을 가지지 않는다. 상속이나 캡슐화 등이 될 리는 더더욱 만무하다. 대신, 데이터는 외래 키를 사용한 양방향 관계를 가질 수 있는데, 이러한 양방향 관계는 객체에서 참조를 통해 유사하게 구현할 수 있지만, 완전히 일치하게 구현하기 위해서는 많은 개발 비용이 든다.

간단히 말하면, 생각보다 두 개념을 예쁘게 붙이기는 힘들다는 것이다.

### ORM은

* __결론__ : ORM은, 이 붙이기 힘든 두 개념을 객체 기준으로(결국 비즈니스 로직이 붙는 쪽은 객체를 포함한 OOP+ORM 사이드일 것이니까) 예쁘게 붙일 수 있게 도와주는 프레임워크이다.
 
## JAP가 필요한 경우들

많은 서적이나 개발 회고에서, 아래와 같은 경우에 JPA 도입을 추천/검토하였다.

* 하루 종일 SQL만 작성하고 있을 때
* 한 서비스가 여러 DBMS를 사용할 가능성이 있을 때
    * DBMS 의존적인 SQL 방언을 어떻게 제거할 것인가?

JPA는 반복적인 SQL 문장 작성 시간을 Java 코드 작성으로 대체한다. SQL 대신 작성한 Java 코드는 연결한 DB 종류에 따라 적절한 SQL을 생성하여, 서비스가 DBMS 중립적이 될 수 있도록 한다.

# Entity

## 개념과 선언

엔티티 클래스는 테이블과 매핑되는 클래스이다. 엔티티는 테이블의 데이터로 사상된다(아마도). 

엔티티 클래스의 선언은 XML로 하는 방법과 애노테이션으로 하는 방법이 있는데, 클래스 명세에 애노테이션으로 작성하는 쪽이 직관적이라 생각한다. `@Entity` , `@Table` , `@Colum` 등의 애노테이션을 사용하여 적절히 엔티티 클래스를 구성하고, 완성된 엔티티 클래스는 애노테이션과 관련 정보를 기반으로 테이블과 매핑된다.

## 엔티티의 상태

엔티티 클래스를 바탕으로 생성된 엔티티는 생성부터 제거되기까지 4가지의 상태를 가질 수 있다.

* 비영속
* 영속
* 준영속
* 삭제

라고 많이들 번역하는데, 제거를 빼면 전혀 직관적이지 않고 도대체 이게 무슨 소리인지 알 수가 없다. 영어로 보면 조금 낫다.

* New
* Managed
* Detached
* Removed

### NEW / 비영속

엔티티를 생성한 시점에서 트랜잭션 구간에 진입하기 전까지, 엔티티는 비영속 상태를 유지한다. 이 상태에서는 엔티티는 JPA의 어떤 특징도 보이지 않는, 그냥 평범한 객체이다.

### MANAGED / 영속

엔티티가 트랜잭션 영역에 진입하여 엔티티 매니저의 관리 하에 들어가면 해당 트랜잭션 구간동안 엔티티는 영속 상태가 된다. 영속 상태에서 엔티티는 몇 가지 중요한 특징을 가지게 된다. 

### DETACHED / 준영속

엔티티가 커밋되어 트랜잭션 구간에서 빠져나오는 경우, 이 엔티티는 준영속 상태가 된다. 사실상 비영속 상태와 거의 같다.

### REMOVED / 삭제

엔티티가 트랜잭션 구간 내에서 관련 메서드에 의해 삭제되는 경우, 매핑되는 데이터의 삭제와 함께 엔티티 또한 삭제 상태가 된다. 객체는 사용 가능한 상태이나, 만약을 위해 재활용 하지 않는 편이 좋다.

# Entity Manager

엔티티 매니저는 이름과 같이, 엔티티를 관리하는 객체이다. 엔티티 매니저는 DB 세션과 밀접한 연관을 가지므로, 여러 스레드에서 공유하여 사용하면 위험하다. 따라서 스레드-안전한 엔티티 매니저 팩토리를 공유하여 각 스레드에서 엔티티 매니저를 생성하는 방식이 권장된다. 

## 주요 기능

* DDL 생성
* 엔티티의 상태 관리
* 영속 상태의 엔티티 관리

# 주의사항

## Flush

* 영속 상태의 엔티티를 수정하면 트랜잭션 커밋 시에 자동으로 업데이트가 발생
    * Setter, Getter 로직에 따라 의도하지 않은 업데이트 발생 가능

## DDL.auto

엔티티 매니저는 프로젝트의 엔티티 클래스 명세로부터 엔티티 클래스에 대응하는 테이블 CREATE 데이터를 자동으로 생성하여 데이터베이스에 적용하는 기능을 가지고 있다. 조금 미묘하긴 하지만(데이터 타입이 안 맞는다거나 하는 등의 문제가 있을 수도 있다.), 그래도 꽤나 편한 기능이긴 한데... 일단 옵션을 보자.

* create
* create-drop
* update
* valid

보면 알겠지만, 데이터를 드랍하는 옵션만 두 개가 있다. 코드 멀쩡히 짜고서 DB 내용물이 싹 날아갈 수가 있다는 의미이다. 따라서 엔티티 매니저 생성 시에는 엔티티 매니저 팩토리 생성 옵션을 잘 살펴봐야 한다.

create 옵션을 고려해볼 수 있는 상황은 다음과 같다

* H2(혹은 다른 어떤 DB라도)와 같은 테스트 DB에 일회성 테스트
* 개발 초기단계에서 데이터베이스 원형 생성

이외에, valid 옵션의 경우 베타 스테이지에서 진행하는 QA 단계에서 사용하는 것을 고려할 수 있다. 그러나 리얼 스테이지에서 DDL 관련 옵션을 사용하는 것은 위험하므로, 개발 단계에 따라 신중하게 옵션을 결정하고, 설정 파일을 나누어야 한다.<commit_msg>내용 일부 수정<commit_after>---
layout: post
title: "JPA - 엔티티와 영속성"
description: "또 DB를 날릴 수는 없다."
tags: [develop, programming, java, jpa, db]
---

# 들어가기 전에

* JPA 설정 잘못 만졌다 DB 예쁘게 날린 허망함을 담아 작성하였음.

# JPA

> JPA는 Java의 ORM 기술 표준이다.

* JPA = ORM

## 태초에 ORM이 있었다

ORM은 __Object-Relational Mapping__ 의 두문자어이며, 이를 번역하여 객체-관계 매핑이라고 이르는 문서도 많이 보인다. 이름을 보면, 대충 OOP와 RDB에 관련된 기술 같아 보이는데, 짐작대로 ORM은 객체지향 언어의 객체와 관계형 데이터베이스의 데이터 사이에서 나타나는 미묘한 차이와 이로 인한 문제들을 극복하기 위해 등장한 기술이다.

### 객체지향과 데이터

객체는, 보통 __데이터와 관련된 연산의 집합__ 을 이르는 말이다. 데이터의 묶음은 보통 자료구조라고 한다.(보통, 자료구조에는 데이터 입출력에 관련한 연산이 수반되는 경우가 많다.) 객체의 큰 특징으로 `캡슐화` , `추상화` , `다형성` , `상속` 의 네 가지를 드는 경우가 많다.

* __객체__ = 데이터 + 연산
* 객체지향
    * 캡슐화
    * 추상화
    * 다형성
    * 상속

관계형 데이터베이스에서, 데이터는 연산을 가지지 않는다. 상속이나 캡슐화 등이 될 리는 더더욱 만무하다. 대신, 데이터는 외래 키를 사용한 양방향 __관계__ 를 가질 수 있는데, 이러한 양방향 관계는 객체에서 참조를 통해 유사하게 구현할 수 있지만, 완전히 일치하게 구현하기 위해서는 많은 개발 비용이 든다.

```java
public class Reader {
    private long id;
    // ...
    private List<Book> bookList;
}

public class Book {
    private long id;
    // ...
    private Reader reader;
}
```

* 이 두 객체에 적절한 데이터를 넣기 위해서 몇 개의 SQL 문장을 작성해야 하는가?

간단히 말하면, 생각보다 두 개념을 예쁘게 붙이기는 힘들다는 것이다.

### 그래서 ORM은...

* __결론__ : ORM은, 객체와 데이터, 두 개념을 코드와 객체 기준으로(결국 비즈니스 로직이 붙는 쪽은 객체를 포함한 OOP+ORM 사이드일 것이니까) 예쁘게 붙일 수 있게 도와주는 프레임워크이다.
 
## JAP가 필요한 경우들

물론 JDBC를 생짜로 사용하는 사람은 별로 없다, MyBatis가 있으니까. 그러면 JPA를 언제 사용하는가? 많은 서적이나 개발 회고에서, 아래와 같은 경우에 JPA 도입을 추천, 또는 검토하였다.

* 하루 종일 SQL만 작성하고 있을 때
* 한 서비스가 여러 DBMS를 사용할 가능성이 있을 때
    * DBMS 의존적인 SQL 방언을 어떻게 제거할 것인가?

### 반복되는 SQL 작성

JPA를 사용하여, 개발자는 SQL 문장을 Java 코드로 대체할 수 있다. 몇 몇 프레임워크는, 특정 클래스를 상속하는 것으로 원하는 데이터에 대한 CRUD 메서드를 자동으로 생성해 개발자에게 제공한다. 그대로 사용하기엔 여러 문제점이 있지만, 이 정도면 꽤 혹하지 않는가.

### SQL 방언

DBMS마다 SQL 문법이 일부 다른 경우가 있는데(오라클과 MySql의 페이지네이션 방법을 생각해보라), 이러한 문법적 차이가 발생하는 부분을 SQL 방언이라고 한다(그렇다는 것 같다). JPA는 연결한 DB 종류에 따라 적절한 SQL을 스스로 생성하여, 서비스가 DBMS 중립적이 될 수 있도록 한다.

# Entity

## 개념과 선언

엔티티 클래스는 테이블과 매핑되는 클래스이다. 엔티티는 테이블의 데이터로 사상된다(아마도). 

엔티티 클래스의 선언은 XML로 하는 방법과 애노테이션으로 하는 방법이 있는데, 클래스 명세에 애노테이션으로 작성하는 쪽이 직관적이라 생각한다. `@Entity` , `@Table` , `@Colum` 등의 애노테이션을 사용하여 적절히 엔티티 클래스를 구성하고, 완성된 엔티티 클래스는 애노테이션과 관련 정보를 기반으로 테이블과 매핑된다.

```java
@Entity
@Table(name = "reader")
public class Reader{
    // ...
}
```

* Reader 클래스는 DB의 reader 테이블과 매핑된다

## 엔티티의 상태

엔티티 클래스를 바탕으로 생성된 엔티티는 생성부터 제거되기까지 4가지의 상태를 가질 수 있다.

* 비영속
* 영속
* 준영속
* 삭제

라고 많이들 번역하는데, 제거를 빼면 전혀 직관적이지 않고 도대체 이게 무슨 소리인지 알 수가 없다. 영어로 보면 조금 낫다.

* New
* Managed
* Detached
* Removed

### NEW / 비영속

엔티티를 생성한 시점에서 트랜잭션 구간에 진입하기 전까지, 엔티티는 비영속 상태를 유지한다. 이 상태에서는 엔티티는 JPA의 어떤 특징도 보이지 않는, 그냥 평범한 객체이다.

```java
Reader reader = new Reader();
```

### MANAGED / 영속

엔티티가 트랜잭션 영역에 진입하여 엔티티 매니저의 관리 하에 들어가면 해당 트랜잭션 구간동안 엔티티는 영속 상태가 된다. 영속 상태에서 엔티티는 몇 가지 중요한 특징을 가지게 된다.

* 1차 캐시 사용
* 같은 키(식별값)를 사용하는 여러 객체의 동등성 보장
* 지연된 쓰기와 변경사항 자동 업데이트

### DETACHED / 준영속

엔티티가 커밋되어 트랜잭션 구간에서 빠져나오는 경우, 이 엔티티는 준영속 상태가 된다. 사실상 비영속 상태와 거의 같다.

### REMOVED / 삭제

엔티티가 트랜잭션 구간 내에서 관련 메서드에 의해 삭제되는 경우, 매핑되는 데이터의 삭제와 함께 엔티티 또한 삭제 상태가 된다. 객체는 사용 가능한 상태이나, 만약을 위해 재활용 하지 않는 편이 좋다.

# Entity Manager

엔티티 매니저는 이름과 같이, 엔티티를 관리하는 객체이다. 엔티티 매니저는 DB 세션과 밀접한 연관을 가지므로, 여러 스레드에서 공유하여 사용하면 위험하다. 따라서 스레드-안전한 엔티티 매니저 팩토리를 공유하여 각 스레드에서 엔티티 매니저를 생성하는 방식이 권장된다. 

## 주요 기능

* DDL 생성
* 엔티티의 상태 관리
* 영속 상태의 엔티티 관리

# 주의사항

## Flush

* 영속 상태의 엔티티를 수정하면 트랜잭션 커밋 시에 자동으로 업데이트가 발생
    * Setter, Getter 로직에 따라 의도하지 않은 업데이트 발생 가능

## DDL.auto

엔티티 매니저는 프로젝트의 엔티티 클래스 명세로부터 엔티티 클래스에 대응하는 테이블 CREATE 데이터를 자동으로 생성하여 데이터베이스에 적용하는 기능을 가지고 있다. 조금 미묘하긴 하지만(데이터 타입이 안 맞는다거나 하는 등의 문제가 있을 수도 있다.), 그래도 꽤나 편한 기능이긴 한데... 일단 옵션을 보자.

* create ; 엔티티 클래스 명세를 바탕으로 테이블을 추가하는 DDL을 생성하여 커밋한다. 기존에 존재하는 테이블은 드랍된다.
* create-drop : create 후 애플리케이션 종료시 테이블을 드랍한다.
* update : 엔티타 클래스 명세를 기준으로 기존 테이블을 업데이트 한다.
* valid : 애플리케이션 시작 시 엔티티 클래스와 테이블 스키마가 일치하는지 여부를 확인 후, 경고 메세지와 함께 애플리케이션 시작을 중단한다.

보면 알겠지만, 데이터를 드랍하는 옵션만 두 개가 있다. 코드 멀쩡히 짜고서(개발자는 자신의 코드가 테이블 구조를 건드리지 않을 것이라 확신하는 상태에서) DB 내용물이 싹 날아갈 수가 있다는 의미이다. 따라서 엔티티 매니저 생성 시에는 엔티티 매니저 팩토리 생성 옵션을 잘 살펴봐야 한다.

create 옵션을 고려해볼 수 있는 상황은 다음과 같다

* H2(혹은 다른 어떤 DB라도)와 같은 테스트 DB에 일회성 테스트
* 개발 초기단계에서 데이터베이스 원형 생성

이외에, valid 옵션의 경우 베타 스테이지에서 진행하는 QA 단계에서 사용하는 것을 고려할 수 있다. 그러나 리얼 스테이지에서 DDL 관련 옵션을 사용하는 것은 위험하므로, 개발 단계에 따라 신중하게 옵션을 결정하고, 설정 파일을 나누어야 한다.
{'fraction_non_alphanumeric': 0.07178217821782178, 'fraction_numerical': 0.0006875687568756876, 'mean_word_length': 2.7335728952772076, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 6, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3735387', 'n_tokens_mistral': 6698, 'n_tokens_neox': 9399, 'n_words': 1476}