본문 바로가기

스프링/Spring Data

Hello JPA!! 애플리케이션 개발하기

반응형

 

 

 들어가며

이 포스팅의 내용은 «자바 ORM 표준 JPA 프로그래밍»을 참고하고 정리한 것입니다.

권장 환경은 다음과 같습니다.

Hello JPA 애플리케이션을 통해 간단히 CRUD 할 수 있는 서버 애플리케이션을 만들어보겠습니다.

 

 

 

라이브러리 추가 - 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>jpa-basic</groupId>
 <artifactId>ex1-hello-jpa</artifactId>
 <version>1.0.0</version>
<dependencies>
	<!-- JPA 하이버네이트 -->
	<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>5.3.10.Final</version>
	</dependency>
	<!-- H2 데이터베이스 -->
	<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<version>1.4.199</version>
	 </dependency>
	</dependencies>
</project>

JPA 설정하기 - persistence.xml

  • JPA 설정 파일
  • {프로젝트 디렉토리}/src/main/resources/META-INF/persistence.xml 위치함
  • persistence-unit name으로 이름 지정
  • javax.persistence로 시작: JPA 표준 속성
  • hibernate로 시작: 하이버네이트 전용 속성
<?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">
    <persistence-unit name="hello">
        <class>hellojap.Member</class>
        <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=""/>
            <!-- h2 접속할 때 사용한 jdbc url과 동일하게 만듬-->
            <property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test"/>
						<!-- h2 방언 사용 -->
            <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>


persistence-unit

JPA 설정은 영속성 유닛(persistence-unit)으로 시작합니다. 영속성 유닛, 즉 persistence-unit은 연결된 하나의 데이터베이스당 하나의 영속성 유닛을 등록합니다.

JPA 표준속성


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 표준 속성은 javax로 시작합니다. 위의 네 속성은 각각 드라이버, 유저명, 패스워드, db url입니다. JPA 표준 속성은 구현체와 관계없이 어디서든 사용할 수 있습니다.

하이버네이트 속성

property name=”hibernate.dialect” value=”org.hibernate.dialect.H2Dialect”
  • hibernate로 시작하는 속성은 하이버네이트 전용 속성입니다. 이 속성은 하이버네이트에서만 사용할 수 있습니다.
  • dialect는 JPA가 많은 데이터베이스를 지원해야 하는 문제 때문에 생긴 속성입니다.
  • 현재는 H2 데이터베이스 방언에 맞게 hibernate가 SQL을 만들어 준다는 것입니다.

** 데이터베이스 방언

  • JPA는 특정 데이터베이스에 종속되지 않습니다.
  • 각각의 데이터베이스가 제공하는 SQL 문법과 함수는 조금씩 다름
    • 가변 문자: MySQL은 VARCHAR, Oracle은 VARCHAR2
    • 문자열을 자르는 함수: SQL 표준은 SUBSTRING(), Oracle은 SUBSTR()
    • 페이징: MySQL은 LIMIT , Oracle은 ROWNUM
  • 방언: SQL 표준을 지키지 않는 특정 데이터베이스만의 고유한 기능
  • hibernate.dialect 속성에 지정
    • H2 : org.hibernate.dialect.H2Dialect
    • Oracle 10g : org.hibernate.dialect.Oracle10gDialect
    • MySQL : org.hibernate.dialect.MySQL5InnoDBDialect
  • 하이버네이트는 40가지 이상의 데이터베이스 방언 지원

** JPA 구동방식

  1. Persistence 클래스에서 persistence.xml을 참고해 설정정보를 조회합니다.
  2. 설정 정보를 기반으로 EntityManagerFactory를 생성하고
  3. EntityManagerFactory는 EntityManager를 생성합니다.

주의

1. 엔티티 매니저 팩토리는 하나만 생성해서 애플리케이션 전체에서 공유
2. 엔티티 매니저는 스레드 간에 공유하면 안 된다.(사용하고 버려야 한다)
3. JPA의 모든 데이터 변경은 트랜잭션 안에서 실행

회원 클래스 생성

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

  @Id
  @Column(name = "ID")
  private String id;
	
  @Column(name = "NAME")
  private String username;
  private Integer age;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

어노테이션의 의미는 다음과 같습니다.

@Entity

이 클래스를 테이블과 매핑한다고 JPA에게 선언합니다. 엔티티 애노테이션이 사용된 클래스를 엔티티 클래스라고 합니다.

@Table

엔티티 클래스에 매핑할 테이블 정보를 알려줍니다. 여기서는 name 속성을 이용하여 이 클래스를 MEMBER 테이블과 매핑했습니다. 이 애노테이션을 생략하면 클래스 이름을 테이블 이름으로 매핑합니다.

@id

엔티티 클래스의 필드를 테이블의 Primary key로 매핑합니다.

@Column

필드를 칼럼에 매핑합니다. 만약 매핑 정보를 주지 않는다면(위의 예제에서는 age), 필드명을 이용하여 칼럼과 매핑합니다.

애플리케이션 개발

package jpabook.start;

import javax.persistence.*;
import java.util.List;

public class JpaMain {

    public static void main(String[] args) {

        //엔티티 매니저 팩토리 생성
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook");
        EntityManager em = emf.createEntityManager(); //엔티티 매니저 생성

        EntityTransaction tx = em.getTransaction(); //트랜잭션 기능 획득

        try {
            tx.begin(); //트랜잭션 시작
            logic(em);  //비즈니스 로직
            tx.commit();//트랜잭션 커밋

        } catch (Exception e) {
            e.printStackTrace();
            tx.rollback(); //트랜잭션 롤백
        } finally {
            em.close(); //엔티티 매니저 종료
        }

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

    public static void logic(EntityManager em) {

        String id = "id1";
        Member member = new Member();
        member.setId(id);
        member.setUsername("지한");
        member.setAge(2);

        //등록
        em.persist(member);

        //수정
        member.setAge(20);

        //한 건 조회
        Member findMember = em.find(Member.class, id);
        System.out.println("findMember=" + findMember.getUsername() + ", age=" + findMember.getAge());

        //목록 조회
        List<Member> members = em.createQuery("select m from Member m", Member.class).getResultList();
        System.out.println("members.size=" + members.size());

        //삭제
        em.remove(member);

    }
}

로직 구현 흐름은 다음과 같습니다.

엔티티 매니저 팩토리 생성 > 엔티티 매니저 생성 > 트렌잭션 획득 > 트렌잭션 시작 > 비즈니스 로직 시작 > 트렌잭션 커밋 또는 롤백 > 엔티티 매니저 종료 > 엔티티 매니저 팩토리 종료

엔티티 매니저 팩토리 생성

JPA를 시작하려면 우선 persistence.xml의 설정 정보를 보고 엔티티 매니저 팩토리를 생성해야 합니다. 이때 Persistence 클래스를 사용하는데 이 클래스는 엔티티 매니저 팩토리를 생성하여 JPA를 사용할 수 있게 해 줍니다.

즉 persistence.xml에서 설정한

<persistence-unit name="jpabook">

것을 보고,

EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook")

엔티티 매니저 팩토리를 생성합니다.

엔티티 매니저 생성

EntityManger em = emf.createEntityManager();
  • 엔티티 매니저 팩토리에서 엔티티 매니저를 생성한다. JPA의 기능 대부분은 이 엔티티 매니저가 제공한다. 대표적으로 엔티티 매니저를 사용해서 엔티티를 데이터베이스에 등록/수정/삭제/조회 할 수 있다.
  • 엔티티 매니저는 내부에 데이터소스를(데이터베이스 컨넥션)를 유지하면서 데이터베이스와 통신한다.
  • 따라서 애플리케이션 개발자는 엔티티 매니저를 가상의 데이터베이스로 생각할 수 있다.
  • 엔티티 매니저는 데이터베이스 컨넥션과 밀접한 관계가 있음으로 스레드 간에 공유하거나 재사용하면 안된다.

종료

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

em.close();

애플리케이션이 종료될 때 엔티티 매니저 팩토리도 종료해야 한다.

emf.close();

트렌잭션 관리

EntityTransaction tx = em.getTransaction();
try {
    tx.begin(); // 트랜잭션 시작
    logic(em); // 비즈니스 로직 실행
    tx.commit(); // 트랜잭션 커밋
}catch (Exception e) {
    tx.rollback(); // 예외발생시 트랜잭션 롤백
}
  • JPA를 사용하면 항상 트랜잭션 안에서 데이터를 변경해야 한다.
  • 트랜잭션 없이 데이터 베이스를 변경하면 예외가 발생한다. 트랜잭션을 시작하려면 엔티티 메니저 에서 트랜잭션 API를 받아와야 한다.
  • 트랜잭션 API를 사용해서 비즈니스 로직이 정상 동작하면 트랜잭셔을 커밋하고 예외가 발생하면 롤백 한다.

비즈니스 로직

public static void logic(EntityManager em){
    String id = "id";
    Member emember = new Member();
    member.setId(id);
    member.setUsername("yun");
    member.setAsge(28);

    // 등록
    em.persist(member);

    // 수정
    member.setAge(20);

    // 한건 조회
    Member findMember = em.find(Member.class, id);
    
    // 목록 조회
    List<Member> members = em.createQuery("select m from member m", Member.class);
    
    // 삭제
    em.remvoe(member);
    
}

 

비즈니스 로직을 보면, 트렌잭션 매니저를 가지고 CRUD를 하는 것을 알 수 있다.

엔티티 매니저에 객체를 등록하면, JPA는 객체의 매핑 정보를 분석하여 SQL을 생성한다.

위와 같은 코드에서는 다음과 같은 SQL을 만들 것이다.

INSERT INTO MEMBER (ID, NAME, AGE) VALUES (‘id1’, ‘지한’, 2);
  • 테이블이 아닌 엔티티 객체를 대상으로 검색하려면 데이터베이스의 모든 데이터를 애플리케이션으로 불러와서 엔티티 객체로 변경한 다음 검색 해야하는데, 이는 사실상 불가능하다.
  • 애플리케이션이 필요한 데이터만 데이터베이스에서 불러오려면 결국 검색 조건이 포함된 SQL을 사용해야한다. JPA는 JPQL 이라는 쿼리 언어로 이런 문제를 해결한다.
  • JPQL은 엔티티 객체를 대상으로 쿼리한다. 쉽게 이야기해서 클래스와 필드를 대상으로 쿼리한다.
  • SQL은 데이터베이스 테이블을 대상으로 쿼리한다.
  • JPA는 JPQL을 분색해서 다음과 같은 적절한 SQL을 만들어 데이터베이스에서 데이터를 조회한다.

 

마무리

애플리케이션을 동작시키면 H2 데이터베이스에 데이터가 저장된 것을 확인할 수 있습니다.

 

 
반응형

'스프링 > Spring Data' 카테고리의 다른 글

JPA 소개 및 JPA의 기본 동작 과정  (0) 2021.05.20
SQL 중심적인 개발의 배경과 문제점  (0) 2021.05.20