# JPA 시작하기

```xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>jpa-basic</groupId>
    <artifactId>hello-jpa</artifactId>
    <version>1.0.0</version>

    <dependencies>
        <!--  Java 11 사용 시 추가 필요  -->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.1</version>
        </dependency>
        <!-- JPA 하이버네이트 -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <!--  hibernate core, javax.persistence 등이 담겨있다.   -->
            <!--     javax.persistence는 JPA 인터페이스를 가지고 있다.     -->
            <artifactId>hibernate-entitymanager</artifactId>
            <version>5.4.1.Final</version>
        </dependency>
        <!-- H2 데이터베이스 -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.200</version>
        </dependency>
    </dependencies>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

</project>
```

* Java 8
* Maven
  * groupId: jpa-basic
  * artifactId: ex1-hello-jpa
  * version: 1.0.0

## JPA 설정하기

### persistence.xml

```xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
             xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">

    <!-- JPA 이름 설정: 데이터베이스 당 하나를 부여한다. -->
    <persistence-unit name="hello">
        <properties>
            <!-- 필수 속성: DB 접속 정보 -->
            <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
            <property name="javax.persistence.jdbc.user" value="sa"/>
            <property name="javax.persistence.jdbc.password" value=""/>
            <property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test"/>
            <!-- JPA는 특정 DB에 종속적이지 않아 hibernate 전용 옵션을 추가해줘야 한다.  -->
            <!-- DB마다 각자 dialect가 다르기 때문에 H2 dialect를 추가한다. -->
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
            <!-- 옵션 -->
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.use_sql_comments" value="true"/>
            <!--<property name="hibernate.hbm2ddl.auto" value="create" />-->
        </properties>
    </persistence-unit>
</persistence>
```

* resources/META-INF/persistence.xml
* persistence-unit
  * 이름 지정
* javax.persistence
  * JPA 표준 속성
* hibernate
  * 하이버네이트 전용 속성

### dialect

![](/files/-MVeGOaCJ36_krIJvaxT)

* JPA는 특정 DB에 종속되지 않는다.
* 각각의 DB가 제공하는 SQL 문법과 함수가 조금씩 다르다.
* 이렇게 SQL 표준을 지키지 않는 특정 DB만의 고유한 기능을 방언이라고 한다.

## 애플리케이션 개발

### JPA 구동 방식

![](/files/-MVeXaDn58z8_3tL5Ezy)

JPA의 `Persistence` 클래스가 `META-INF/persistence.xml` 설정 파일을 읽어서 `EntityManagerFactory`라는 클래스를 생성한다. 여기서 필요할 때마다 `EntityManager`를 만든다.

### 회원 생성

```sql
create table Member
(
    id   bigint not null,
    name varchar(255),
    primary key (id)
);
```

이 테이블대로 객체를 만들고 DB에서 불러와보자.

{% tabs %}
{% tab title="JpaMain.java" %}

```java
public class JpaMain {

    public static void main(String[] args) {
        // EntityManagerFactory는 애플리케이션 로딩 시점에 딱 하나만 만들어야 한다.
        // persistence.xml에서 설정했던 'persistence-unit name' 값을 넘긴다.
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello");

        // EntityManager를 꺼낸다.
        // EntityManager는 트랜잭션마다 만들어줘야 한다.
        EntityManager entityManager = entityManagerFactory.createEntityManager();

        // JPA의 모든 작업은 트랜잭션 내에서 해야 적용된다.
        EntityTransaction tx = entityManager.getTransaction();
        tx.begin();

        try {
            // 실제 동작 코드를 작성한다.
            Member member = new Member();
            member.setId(1L);
            member.setName("helloA");

            // 저장한다.
            entityManager.persist(member);

            // 정상적이면 커밋한다.
            tx.commit();
        } catch (Exception e) {
            // 실패하면 롤백한다.
            tx.rollback();
        } finally {
            // EntityManager가 내부적으로 데이터베이스 커넥션을 물고 동작하기 때문에 쓰고 나서 꼭 닫아줘야 한다.
            entityManager.close();
        }

        // 전체 애플리케이션이 끝나면 팩토리까지 닫아준다.
        entityManagerFactory.close();
    }
}
```

{% endtab %}

{% tab title="Member.java" %}

```java
// JPA가 사용하는 데이터라고 인식한다.
@Entity
public class Member {

    // PK를 지정한다.
    @Id
    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;
    }
}
```

{% endtab %}
{% endtabs %}

```
Hibernate:
    insert into Member(name, id) values(?, ?)
```

실행하면 위와 같은 쿼리가 날아간다.

### 회원 수정

{% tabs %}
{% tab title="JpaMain.java" %}

```java
public class JpaMain {

    public static void main(String[] args) {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello");
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        EntityTransaction tx = entityManager.getTransaction();
        tx.begin();

        try {
            // 수정할 대상을 가져온다.
            Member findMember = entityManager.find(Member.class, 1L);

            System.out.println("id: " + findMember.getId());
            System.out.println("name: " + findMember.getName());

            findMember.setName("helloJPA");

            // 수정한 객체를 따로 저장하지 않아도 된다. 
            // 데이터를 JPA를 통해 가져오면 변경 여부를 트랜잭션 커밋 시점에
            // 다 체크해서 바뀐 내용에 대해 업데이트 쿼리를 만들어 날린다.

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            entityManager.close();
        }

        entityManagerFactory.close();
    }
}
```

{% endtab %}
{% endtabs %}

```
update Member set name=? where id=?
```

update 쿼리가 나가는 걸 확인할 수 있다.

## 주의할 점

* EntityManagerFactory
  * 애플리케이션 실행 시점에 하나만 생성해서 애플리케이션 전체에 걸쳐 공유한다.
* EntityManager
  * 요청이 왔을 때 썼다가 끝나면 버리는 사이클을 반복한다.
  * 그래서 절대 스레드 간에 공유해서는 안된다. 사용하고 바로 버려야 한다.
* JPA의 모든 데이터 변경은 트랜잭션 안에서 실행해야 한다.
  * 트랜잭션을 실제 지정해본 경험이 없어도 DB 자체적으로 트랜잭션을 사용하기 때문에 적용해왔을 것이다.

## JPQL

* `EntityManger.find()`는 가장 단순한 조회 방법이다.
* `나이가 18살 이상인 회원` 같은 쿼리는 JPQL을 사용해야 한다.

{% tabs %}
{% tab title="JpaMain.java" %}

```java
public class JpaMain {

    public static void main(String[] args) {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello");
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        EntityTransaction tx = entityManager.getTransaction();
        tx.begin();

        try {
            // 일반적인 쿼리처럼 생겼지만 JPA는 테이블이 아니라
            // 객체를 대상으로 쿼리를 짜기 때문에
            // 여기서 Member는 테이블이 아니라 객체를 가리킨다.
            List<Member> result = entityManager
                    .createQuery("select m from Member as m", Member.class)
                    // 페이지네이션을 할 수도 있다. 아래는 1번부터 10개 가져온다.
                    .setFirstResult(1)
                    .setMaxResults(10)
                    .getResultList();

            for (Member member : result) {
                System.out.println("name: " + member.getName());
            }

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            entityManager.close();
        }

        entityManagerFactory.close();
    }
}
```

{% endtab %}
{% endtabs %}

* JPA를 사용하면 Entity 객체를 중심으로 개발하게 된다.
* 검색 또한 테이블이 아닌 Entity 객체를 대상으로 한다.
  * 모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능하다.
* 필요한 데이터만 DB에서 불러오려면 결국 검색 조건이 포함된 SQL이 필요하다.
  * 하지만 쿼리를 쓰면 SQL에 종속된다.
* 이때 객체를 대상으로 검색할 수 있게 하는 기술이 JPQL이다.

### SQL과의 차이

* JPQL
  * Entity 객체를 대상으로 쿼리
* SQL
  * 데이터베이스 테이블을 대상으로 쿼리

### 특징

* JPA가 제공하는 SQL을 추상화한 객체 지향 쿼리 언어
  * 한마디로 객체지향 SQL이다.
  * 추상화했기 때문에 특정 데이터베이스에 의존하지 않는다.
* SQL과 문법이 유사하다.
  * select, from, where, group by, having, join을 지원한다.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://dodeon.gitbook.io/study/kimyounghan-orm-jpa/02-starting-jpa.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
