본문 바로가기
Spring JPA

JPA 시작 - 4

by 홍굴이 2021. 6. 25.

개발 도구 : IntelliJ Ultimate

빌드 도구 : Maven

 

pom.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>jpabook</groupId>
    <artifactId>jpa-start</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>1.6</java.version>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
            <scope>provided</scope>
        </dependency>

        <!--JPA, 하이버네이트-->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>4.3.10.Final</version>
        </dependency>
        <!--H2 데이터베이스-->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.187</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

 

Member 클래스

package jpabook.start;

import lombok.*;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "MEMBER")
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Member {

    @Id
    @Column(name = "ID")
    private String id;

    @Getter
    @Column(name = "NAME")
    private String username;

    @Getter
    private int age;

    public void update(int age) {
        this.age = age;
    }

}
  • @Entity
    이 클래스를 테이블과 매핑한다고 명시 - 엔티티 클래스라 한다.
  • @Table
    엔티티 클래스에 매필할 테이블 정보를 알려준다.
    name 속성을 사용해 Member 엔티티를 MEMBER 테이블에 매핑하였다.
  • @Id
    엔티티 클래스의 필드를 테이블의 기본키에 매핑한다.
  • @Column
    필드를 칼럼에 매핑한다.
  • 매핑정보가 없는 필드
    자동으로 칼럼명으로 매핑한다.

 

Persistence.xml 설정

JPA는 persistence.xml을 사용해서 필요한 설정 정보를 관리한다.

※ Spring Boot의 경우는 application.properties 혹은 application.yml로 관리한다.

 

파일 경로 : resources/META-INF/persistence.xml

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">
    <persistence-unit name="jpabook">
        <properties>
            <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:mem:db"/>
            <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.user_sql_comments" value="true"/>
            <property name="hibernate.id.new_generator_mappings" value="true"/>
        </properties>
    </persistence-unit>
</persistence>
  • <persistence-unit name="">
    JPA 설정은 영속성 유닛이라는 것부터 시작하는데 일반적으로 연결할 데이터베이스당 하나의 영속성 유닛을 등록한다.
    영속성 유닛에는 고유한 이름을 부여해야 한다.
  • JPA 표준 속성
    - javax.persistence.jdbc.driver : JDBC 드라이버
    - javax.persistence.jdbc.user : 데이터베이스 접속 아이디
    - javax.persistence.jdbc.password : 데이터베이스 접속 비밀번호
    - javax.persistence.jdbc.url : 데이터베이스 접속 URL
  • 하이버네이트 속성
    - hibernate.dialect : 데이터베이스 방언 설정

    데이터베이스는 저마다 조금씩 다르다.
    예를 들어, MySQL은 VARCHAR로, 오라클은 VARCHAR2를 사용한다.
    이처럼 SQL 표준을 지키지 않거나 특정 데이터베이스만의 고유한 기능을 JPA에서는 방언이라 한다.
    하이버네이트를 포함한 대부분의 JPA 구현체들은 이런 문제를 해결하려고 다양한 데이터베이스 방언 클래스를 제공한다.

Main 코드

package jpabook.start;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import java.util.List;

public class JpaMain {
    public static void main(String[] args) {

        // 엔티티 매니저 팩토리 - 생성
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("jpabook");

        // 엔티티 매니저 - 생성
        EntityManager entityManager = entityManagerFactory.createEntityManager();

        // 트랜잭션 - 획득
        EntityTransaction entityTransaction = entityManager.getTransaction();

        try {

            // 트랜잭션 - 시작
            entityTransaction.begin();
            // 비즈니스 로직 - 실행
            logic(entityManager);
            // 트랜잭션 - 커밋
            entityTransaction.commit();
        } catch (Exception e) {
            // 트랜잭션 - 롤백
            entityTransaction.rollback();
        } finally {
            // 엔티티 매니저 - 종료
            entityManager.close();
        }

        // 엔티티 매니저 팩토리 - 종료
        entityManagerFactory.close();
    }

    private static void logic(EntityManager entityManager) {

        String id = "identity";
        Member member = new Member(id, "영준", 26);

        entityManager.persist(member);

        member.update(27);

        Member savedMember = entityManager.find(Member.class, id);
        System.out.println("savedMember : " + savedMember.getUsername() + ", age : " + savedMember.getAge());

        List<Member> memberList = entityManager.createQuery("select m from Member m", Member.class).getResultList();

        System.out.println("Member.size : " + memberList.size());

        entityManager.remove(member);
    }
}

 

엔티티 매니저 생성 과정

  • 엔티티 매니저 팩토리 생성
    EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("jpabook");

    JPA를 시작하려면 우선 persistence.xml의 설정 정보를 이용해서 엔티티 매니저 팩토리를 생성해야 한다.
    persistence.xml의 설정 정보를 읽어서 JPA를 동작시키기 위한 기반 객체를 만들고 JPA 구현체에 따라서는 데이터베이스 커넥션 풀도 생성하므로 엔티티 매니저 팩토리를 생성하는 비용이 아주 크다.
    따라서 엔티티 매니저 팩토리는 애플리케이션 전체에서 딱 한 번만 생성하고 공유해서 사용해야 한다.

  • 엔티티 매니저 생성
    EntityManager entityManager = entityManagerFactory.createEntityManager();

    엔티티 매니저 팩토리에서 엔티티 매니저를 생성
    JPA의 기능 대부분은 이 엔티티 매니저가 제공한다.
    즉, 엔티티 매니저를 사용해서 엔티티를 데이터베이스에 CRUD 할 수 있다.
    엔티티 매니저는 내부에 데이터소스(데이터베이스 커넥션)을 유지하면서 데이터베이스와 통신한다.
    참고로 엔티티 매니저는 데이터베이스 커넥션과 밀접한 관계가 있으므로 스레드간에 공유하거나 재사용하면 안 된다.

  • 종료
    entityManager.close();
    entityManagerFactory.close();

    사용이 끝난 엔티티 매니저, 엔티티 매니저 팩토리는 반드시 종료해야 한다.

트랜잭션 관리

JPA를 사용하면 항상 트랜잭션 안에서 데이터를 변경해야 한다.

트랜잭션 없이 데이터를 변경하면 예외가 발생한다.

트랜잭션을 시작하려면 엔티티 매니저에서 트랜잭션을 받아와야 한다.

EntityTransaction entityTransaction = entityManager.getTransaction();

try {
	
    entityTransaction.begin();
    logic(entityManager);
    entityTransaction.commit();
    
} catch (Exception e) {        
	
    entityTransaction.rollback();
}

비즈니스 로직

엔티티 매니저를 통해 CRUD를 작성하였다.

 

JPQL

List<Member> memberList = entityManager.createQuery("select m from Member m", Member.class).getResultList();

 

JPA를 사용하면 애플리케이션 개발자는 엔티티 객체를 중심으로 개발하고 데이터베이스에 대한 처리는 JPA에 맡겨야 한다.

문제는 검색 쿼리에서 발생한다.

테이블이 아닌 엔티티 객체를 대상으로 검색하려면 데이터베이스의 모든 데이터를 애플리케이션으로 불러와서 엔티티 객체로 변경한 다음 검색해야 하는데, 이는 사실상 불가능하다.

JPA는 JPQL (Java Persistence Query Language)라는 쿼리 언어로 이 문제를 해결한다.

SQL을 추상화한 JPQL이라는 객체지향 쿼리 언어를 제공한다.

  • JPQL은 엔티티 객체를 대상으로 쿼리한다.
  • SQL은 데이터베이스 테이블을 대상으로 쿼리한다.

코드에서 select m from ~ 부분은 JPQL이다.

여기서 from Member는 회원 엔티티 객체를 말하는 것이지 MEMBER 테이블이 아니다.

 


엔티티 매니저 팩토리를 만들 때 doResolve 메소드를 사용해서 자동으로 META/persistence.xml을 부르는 것을 확인할 수 있다.

 

'Spring JPA' 카테고리의 다른 글

영속성 관리 - 6  (0) 2021.06.29
영속성 관리 - 5  (0) 2021.06.26
JPA 소개 - 3  (0) 2021.06.23
JPA 소개 - 2  (0) 2021.06.22
JPA 소개 - 1  (0) 2021.06.22

댓글