Programming/JPA ⁄Spring

H2 데이터베이스를 이용해 JPA 실습해보기

📝 작성 : 2021.09.21  ⏱ 수정 : 
반응형

인프런에서 우아한 형제들 김영한님의 JPA 강의를 학습하며 내용을 정리 해 보았습니다. 해당 강의를 들어보시길 추천합니다.

https://www.inflearn.com/course/ORM-JPA-Basic/dashboard

H2 데이터베이스는 자바기바느이 오픈소스 RDBMS(관계형 데이터베이스 시스템)입니다. 인메모리 DB 기능을 지원하며 굉장히 용량도 작고 가볍게 동작하기 때문에 번거롭게 데이터베이스를 구축 할 필요 없이 손쉽게 실습을 할 수 있어 편리합니다.

H2 DATABASE 설치

아래의 공식 사이트에서 다운 받습니다. 저는 MacOS 를 사용중이기 때문에 All platforms 를 클릭 해 다운 받았습니다.

https://h2database.com

image-20210921162850490

이후 적당한 폴더에 위치 시킵니다.

image-20210921163046647

이제 h2/bin/h2.sh 를 실행 해줍니다.

bash h2.sh

image-20210921163526246

Setting만 Server로 변경하고 아무 것도 바꾸지 않고 바로 Connect를 누르는데요, 보통 아래처럼 에러가 나옵니다.

image-20210921163655367

그러면 데이터 베이스를 먼저 생성 해 주어야 합니다. 아래 처럼 JDBC에 아래 내용만 입력을 하고 한번 연결을 하면 데이터베이스 파일이 생성 됩니다.

jdbc:h2:~/test

image-20210921163911151

​ 이렇게 입력하고 Connect를 한번 눌러주면 접속이 됩니다.

생성 후에는 다시 아래 주소로 연결 하면 문제 없이 연결이 됩니다. 위에서처럼 파일에 직접 접근하는 방법은 lock 이 걸린다고 하네요.

jdbc:h2:tcp://localhost/~/test

image-20210921164023590

이제 정상적으로 연결이 됩니다.

image-20210921164132980

JAVA 프로젝트 생성

이제 프로젝트를 생성해봅니다 Maven으로 생성합니다.

image-20210921164409630

image-20210921164634596 Name이나 Location, GroupID, ArtifacdtId 등은 모두 편하신 대로 하면 됩니다.

프로젝트 생성이 완료되었습니다.

image-20210921164743258

pom.xml에 dependency에 필요한 hibernate와 h2database를 넣어 줍니다.

h2 database는 아까 받았던 버전과 일치 시켜 줍니다.

<?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>com.tistory.shanepark</groupId>
    <artifactId>ex1-hello-jpa</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.4.29.Final</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.200</version>
        </dependency>
    </dependencies>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

</project>

이번에는 Hibernate 설정파일을 만들어 줍니다. resources에 META-INF 디렉터리를 만든 후에 persistence.xml 파일을 생성 하면 됩니다.

<?xml version="1.0" encoding="utf-8" ?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.2">
    <persistence-unit name="hello">
        <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:tcp://localhost/~/test"/>
            <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"/>
        </properties>
     </persistence-unit>
</persistence>

이제는 EntityManagerFactory를 만들어 볼 건데요, 아까 위에서 만들었던 persistence.xml에 쓴 unit name을 적어 주면 됩니다.

image-20210921171419039

package com.tistory.shanepark;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
    }
}

이제 main 메서드를 실행 해보면 뭔가 문제 없이 커넥션이 진행 되는게 보입니다.

image-20210921172836762

테이블 생성

다시 H2로 돌아와서 테스트를 위한 멤버 테이블을 생성합니다.

image-20210921173552394

create table member(
  id bigint not null,
  name varchar(255),
  primary key(id)
);

객체와 테이블 매핑

적당한 위치에 Member 클래스를 만들어 줍니다.

package com.tistory.shanepark;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Member {

    @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;
    }
}

이번에는 아까 작성했던 JpaMain 의 메인 메서드에 Member 를 insert 하는 내용을 작성 해 봅니다.

package com.tistory.shanepark;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

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

        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();

        tx.begin();

        Member member = new Member();
        member.setId(1L);
        member.setName("park");
        em.persist(member);

        tx.commit();
        em.close();
        emf.close();;

    }
}

EntityManagerFactory 를 받아와서, EntityManager를 받아 온 뒤, 거기에서 Transaction을 받아 옵니다.

트랜잭션 안에서 Member를 생성 해서 em.persisst로 저장 해 주는 건데요. 이제 메인 메서드를 실행 해 보면

쿼리를 요청하는 것을 눈으로 확인 할 수 있습니다.

아까 persistence.xml에 옵션을 true로 했기 때문입니다.

image-20210921174815143

정말 잘 들어갔는지 h2에 들어가서 확인 해 보면, 정말 park 이라는 멤버가 들어간게 확인 됩니다.

image-20210921174841178

쿼리를 직접 짜지 않았지만, JPA가 알아서 Entity인 Member 클래스에서 관례에 따라 클래스명과 같은 Member 테이블을 찾아 insert 해 준 것 입니다.

클래스명이나 인스턴스 명이 테이블/컬럼명과 다를 경우에는 아래 처럼 수동으로 적어 줄 수도 있습니다.

package com.tistory.shanepark;

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

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

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

    @Column(name = "NAME")
    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;
    }
}

조회는 아래와 같이 할 수 있습니다.

package com.tistory.shanepark;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

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

        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();

        tx.begin();

        try {
            Member findMember = em.find(Member.class, 1L);
            System.out.println(findMember.getId());
            System.out.println(findMember.getName());

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

    }
}

image-20210921175551109

select 쿼리를 날리고, 아이디인 1 과 , name인 park을 잘 받아 왔습니다.

수정의 경우는 findMember.persist(findMember)를 할 필요도 없이, 그냥 setter 만 호출 해 주면 알아서 저장이 됩니다.

            Member findMember = em.find(Member.class, 1L);
            findMember.setName("Shane Park");

            tx.commit();

image-20210921175823855

정말 기가 막힙니다!

​ 편하게 자바 컬렉션을 다루는 것 처럼 사용 하면 되기 때문에 정말 편리합니다.

삭제도 어렵지 않습니다.

           Member member = em.find(Member.class, 3L);
            em.remove(member);

주의해 할 점

  • Entity Manager Factory 는 하나만 생성해서 어플리케이션 전체에서 공유

  • Entity Manager는 쓰레드간에 공유하면 절대 안됩니다. 사용하고 바로 버려야 합니다.

  • JPA의 모든 데이터 변경은 트랙잭션 안에서 실행되어야 합니다.

entityManager.createQuery를 이용하면 직접 쿼리를 작성 할 수도 있는데요

package com.tistory.shanepark;

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 emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();

        tx.begin();

        try {
            List<Member> result = em.createQuery("select m from Member as m", Member.class).getResultList();
            for (Member member : result) {
                System.out.println("member.name = " + member.getName());
            }

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

    }
}

image-20210921181059342

조금만 활용하면 페이지 네이션을 정말 쉽게 할 수 있습니다.

image-20210921181409264

위와 같은 데이터가 있을 때

List<Member> result = em.createQuery("select m from Member as m", Member.class)
        .setFirstResult(1).setMaxResults(2).getResultList();

setFirstResult로 1번째 결과부터 (0부터 세기때문에 jenny), setMaxResult로 최대2명까지 불러오게끔 할 수 있습니다.

image-20210921181551869

쿼리에 limit 과 offset이 적용 되며 jenny 와 tommy가 조회 되는 것을 확인 하실 수 있습니다.

H2 데이터베이스를 설치해서 기본적인 CRUD를 해 보았는데요 JPA를 꼭 사용해야 하는 이유를 충분히 느끼셨을 거라고 생각합니다.

마지막으로 강의 링크 한번 더 남기며 글 마무리 짓겠습니다. 여유가 되시는 분은 꼭 강의를 들으시길 추천드려요.

https://www.inflearn.com/course/ORM-JPA-Basic/dashboard

반응형