JPA 기본 사용법 공부의 기록

timothy jeong·2021년 7월 18일
0

Study

목록 보기
6/11

공부의 기록 시리즈는 공유하고자 적는 글이 아닙니다. 공부할때 필기 하는 습관이 있고, 모르는 내용을 메모할 공간이 필요하기 때문에 적는 글입니다. 나중에 이 글을 잘 정제하고 편집해서 공유를 위한 글을 따로 포스팅합니다.

JPA 기본 이론

(1) 실습용 DB 셋팅

책의 내용은 h2 DB를 사용하지만, h2를 셋팅하는데 오류가 너무 많이 발생해서 그냥 mysql로 셋팅했다.

mysql 은 homebrew를 이용해서 설치했다.

$ > brew install mysql

$ > mysql -u root -p
// passwd

mysql > CREATE DATABASE jpa;
mysql > USE jpa;
mysql > CREATE TABLE MEMBER (
	ID VARCHAR(255) NOT NULL, 
	NAME VARCHAR(255),
	AGE INTEGER,
	PRIMARY KEY (ID)
	);

mysql > ALTER USER 'yourusername'@'localhost' IDENTIFIED WITH mysql_native_password BY 'youpassword';

mysql GUI 는 워크밴치를 이용했다.

(2) JPA 셋팅

원래는 gradle을 이용했으나, gradle을 이용한 설정시 잘 해결되지 않는 오류가 나와서 책의 maven 설정을 이용했다.

전체적인 프로젝트 구조는 다음과 같다.

jdk 는 11.0.11 ,IDE 는 intellij를 이용하였다.

다음은 porm.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>

    <dependencies>
        <!-- JPA, 하이버네이트 -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>5.5.3.Final</version>
        </dependency>
        
        <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.25</version>
    </dependency>
        
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-plugin-api</artifactId>
            <version>3.8.1</version>
        </dependency>

    </dependencies>

</project>

persistence unit 을 설정해야한다. 그 설정 값들은 resources/META-INF/persistence.xml 에 입력한다.
springboot 라면 application.properties에서 설정이 가능하다.

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">
    <persistence-unit name="jpabook" >
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <properties>

            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
            <property name="javax.persistence.jdbc.user" value="{ID}" />
            <property name="javax.persistence.jdbc.password" value="{PASSWD}" />
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost/jpa" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />

            <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.id.new_generator_mappings" value="true" />
        </properties>
    </persistence-unit>
</persistence>

javax.persistence : 이 속성은 JPA 표준 속성으로 특정 구현 체에 종속되지 않는다.
hibernate : 이 속성은 하이버네이트 전용 속성이므로 하이버네이트에서만 사용할 수 있다.

■ JPA 표준 속성

  • javax.persistence.jdbc.driver: JDBC 드라이버
  • javax.persistence.jdbc.user: 데이터베이스 접속 아이디
  • javax.persistence.jdbc.password: 데이터베이스 접속 비밀번호
  • javax.persistence.jdbc.url: 데이터베이스 접속 URL

■ 하이버네이트 속성

  • hibernate.dialect: 데이터베이스 방언Dialect 설정데이터 베이스 방언이란, SQL 표준을 지키지 않는 해당 데이터 베이스만의 특성을 의미한다. JPA는 표준적인 방언 체계를 제공하지 않는다.하이버네이트에서 제공하는 방언이라는 표준 덕분에 개발자는 상위의 DB 셋팅과 방언만 교채해주면 된다.

  • hibernate.show_sql: 하이버네이트가 실행한 SQL을 출력한다.

  • hibernate.format_sql: 하이버네이트가 실행한 SQL을 출력할 때 보기 쉽게 정렬한다.

  • hibernate.use_sql_comments: 쿼리를 출력할 때 주석도 함께 출력한다.

  • hibernate.id.new_generator_mappings: JPA 표준에 맞춘 새로운 키 생성 전략을 사용한다.

대표적인 데이터베이스 방언은 다음과 같다.
H2: org.hibernate.dialect.H2Dialect
오라클 10g: org.hibernate.dialect.Oracle10gDialect
MySQL: org.hibernate.dialect.MySQL5InnoDBDialect

추가 방언 정보

(3) 엔티티 클래스 생성

MEMBER 테이블을 나타내는 엔티티 클래스를 생성한다.

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

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

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

    // 매핑 정보가 없음.
    private Integer age;
    
    //getter setter
}
  • @Entity 에노테이션
    이 클래스를 테이블과 매핑한다고 JPA에게 알려준다.
    이렇게 @Entity가 사용 된 클래스를 엔티티 클래스라 한다.

  • @Table(name="MEMBER")
    JPA에게 어떤 테이블과 매핑되는 엔티티 클래스인지 알려준다.
    이 에노테이션을 생략하면 클래스 이름과 동일한 테이블과 매핑한다.

  • @Id
    엔티티 클래스의 필드를 테이블의 기본 키(Primary key)에 매핑한다.
    이렇게 @Id 에노테이션이 사용된 필드를 식별자 필드라고 부른다.

  • @Column(name = "ID")
    필드를 컬럼에 매핑한다.

  • 매핑 정보가 없음
    매핑 정보가 없을 경우 필드 명을 그래도 칼럼명으로 인식하여 매핑한다. 이때 대소문자 구분 여부를 주의해야한다.

(4) JPA 사용

persistence unit 설정이 완료되다면 해당 유닛으로부터 엔티니 매니저 팩토리를 만들고 엔티닌 매니저 팩토리로부터 엔티니 매니저를 생성한다.

persistenc.xml --> persistenc --> EntityManagerFactory --> EntityManager

엔티니 매니저 성성 과정을 그림으로 다음과 같고

코드는 다음과 같다.

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) {
            tx.rollback(); //[트랜잭션]-롤백
        } finally {
            em.close(); //[엔티티 매니저] - 종료
            }
        emf.close(); //[엔티티 매니저 팩토리] - 종료 }
    }

    //비즈니스 로직
    private static void logic(EntityManager em) {

    }
}
  • EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook")
    persistence.xml 에서 jpabook 이라고 명명된 영속성 유닛의 설정값을 읽어서, EntityManagerFactory 를 생성하는 메서드.
    엔티니 매니저는 persistence.xml을 읽고 경우에 따라서는 DB 커넥션 풀까지 생성하므로 생성에 따른 비용이 큼. 따라서 어플리케이션당 하나씩만 생성하고 내부에서 공유해서 사용하는 것이 바람직함.
  • EntityManager em = emf.createEntityManager()
    JPA의 기능 대부분은 이 엔티티 매니저가 제공한다. 대표적으로 엔티티 매니저를 사용해서 엔티티를 데이 터베이스에 등록/수정/삭제/조회할 수 있다. 엔티니 매니저는 DB와 소통한다. 따라서 개발자는 엔티티 매니저를 DB라고 생각할 수 있다. 엔티티 매니저는 데이터베이스 커넥션과 밀접한 관계가 있으므로 스레드간 에 공유하거나 재사용하면 안 된다.

  • em.close(); emf.close()
    어플리케이션 종료시 엔티니 매니저와 엔티니 매니저 팩토리를 종료해줘야 한다.
    엔티니 매니저는 DB와의 커넥션을 유지하고 있고, 엔티티 매니저 팩토리는 DB 커넥션 풀을 가지고 있기 때문에 close하지 않고 어플리케이션을 종료하면 오류가 발생할 수 있다.

  • EntityTransaction tx = em.getTransaction();
    JPA를 사용하면 항상 트랜잭션 안에서 데이터를 변경해야한다. 트랜젝션 없이 데이터를 변경하면 오류가 발생하므로, 항상 tx.begin(), tx.commit() 을 이용하고, 예외 발생시 tx.rollback() 으로 트랜잭션을 롤백하자.
    이렇게 함으로써 오류가 발생할 경우 실제 DB에는 내용이 반영되지 않도록 한다.

실제 구동하는 logic 클래스는 다음과 같다.

private 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 jpastart.Member m", Member.class).getResultList();
        System.out.println("members.size=" + members.size());
        //삭제
        em.remove(member);
    }
  • em.persist(member)
    위에서 생성한 member 엔티티 객체를 DB에 등록하는 역할을 한다. SQL문으로 치면 INSERT INTO MEMBER (ID, NAME, AGE) VALUES ('id1', '지한', 2) 의 코드가 실행되는 것과 같습니다.

  • member.setAge(20)
    엔티티 객체를 수정하여 DB에 등록될 내용을 수정 합니다. 이때 persist나 em을 호출하지 않고, UPDATE SQL문을 호출하지 않음에도 DB에 들어갈 내용이 수정되는 이유는 JPA는 어떤 엔티티가 변경되었는지 추적하는 기능을 갖추고 있기 때문입니다. 따라서 엔티티 객체를 변경하면 자동으로 DB에 등록될 내용이 변경됩니다.

  • em.remove(member)
    DELETE SQL문을 실행하는 것과 같습니다. 이때 id등의 정보를 추가로 입력하지 않아도 되는 이유는 member 자체가 엔티티 객체로서 필요한 정보를 모두 가지고 있기 때문입니다.

  • Member findMember = em.find(Member.class, id)
    find() 메소드는 조회할 엔티티 타입과 @Id로 데이터베이스 테이블의 기본 키 와 매핑한 식별자 값으로 엔티티 하나를 조회하는 가장 단순한 조회 메소드다.

  • em.createQuery("select m from Member m", Member.class).getResultList();
    하나 이상의 회원 목록을 조회하는 다음 코드입니다. JPA는 엔티티 객체를 중심으로 개발하므로 검색을 할 때도 테이블이 아닌 엔티티 객 체를 대상으로 검색해야 합니다. 그런데 테이블이 아닌 엔티티 객체를 대상으로 검색하려면 데이터베이스의 모든 데이터를 애플리케이션으로 불러와서 엔티티 객체로 변경한 다음 검색해야 하는데, 이는 사실상 불가능하다. 애플리케이션이 필요한 데이터만 데이터베이스에 서 불러오려면 결국 검색 조건이 포함된 SQL을 사용해야 한다. JPA는 JPQL(Java Persistence Query Language)이라는 쿼리 언어로 이런 문제를 해결한다.

(5) JPQL

JPQL은 SQL과 문법이 거의 유사해서 SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 등 을 사용할 수 있다. 둘의 가장 큰 차이점은 다음과 같다.

■ JPQL은 엔티티 객체를 대상으로 쿼리한다. 쉽게 이야기해서 클래스와 필드를 대상으로 쿼리한다.
■ SQL은 데이터베이스 테이블을 대상으로 쿼리한다.

위의 예제에서 JPQL이 사용된 부분은 em.createQuery("select m from jpastart.Member m", Member.class).getResultList() 뿐이다.
JPQL의 from Member 는 회원 엔티티 객체를 말하는 것이지 MEMBER 테이블이 아니다. JPQL은 데이터베이스 테이블을 전혀 알지 못한다.

em.createQuery(JPQL, 반환 타입) 메소드를 실행해서 쿼리 객체를 생성한 후 쿼리 객체의 getResultList() 메소드를 호출

profile
개발자

0개의 댓글