RE-Heat 개발자 일지

[JPA] [2] JPA 시작 - 프로젝트 생성 및 개발 본문

백엔드/JPA

[JPA] [2] JPA 시작 - 프로젝트 생성 및 개발

RE-Heat 2023. 8. 4. 23:51

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

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

초급자를 위해 준비한 [웹 개발, 백엔드] 강의입니다. JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자

www.inflearn.com

인프런 김영한 님의 강의를 듣고 작성한 글입니다.

 

 

[1] Hello JPA - 프로젝트 생성

 

■ 프로젝트 생성

▶ H2 데이터베이스

① 장점

  • 최고의 실습용 DB
  • 가볍다.(1.5M)
  • 웹용 쿼리툴 제공
  • MySQL, Oracle 데이터베이스 시뮬레이션 기능
  • 시퀀스, AUTO INCREMENT 기능 지원

② 설치방법 (윈도우 기준)

http://www.h2database.com/

 

H2 Database Engine (redirect)

H2 Database Engine Welcome to H2, the free SQL database. The main feature of H2 are: It is free to use for everybody, source code is included Written in Java, but also available as native executable JDBC and (partial) ODBC API Embedded and client/server mo

www.h2database.com

1] Windows Installer를 다운받고 실행

2] 설치된 곳(예 C:\Program Files(x86)\H2\bin)의 h2.bat 파일 cmd창에서 실행

3] 예전에 설치했다가 지웠던 사람은 초기화 필요

https://gocoder.tistory.com/2536

 

H2 데이터베이스 설정 초기화 하기

안녕하세요. 고코더 입니다. ● 1. H2 데이터베이스 환경설정 특히 저장한 설정이 변경되었을때 H2를 다시 초기화 하는 방법을 알아보겠습니다. ●2. 자신의 로컬 사용자 폴더로 이동합니다. "C:\Use

gocoder.tistory.com

C:\Users\사용자이름"에서 test.trace.db, test.mv.db 파일 삭제

h2.server.properties 파일은 메모장으로 열어서 변경(자세한 코드는 위 링크에서 확인)

 

▶ Maven

https://maven.apache.org/

 

Maven – Welcome to Apache Maven

Welcome to Apache Maven Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project's build, reporting and documentation from a central piece of information. If you

maven.apache.org

장점

자바 라이브러리, 빌드 관리

라이브러리 자동 다운로드 및 의존성 관리

단점

최근에는 그래들(Gradle)이 점점 유명

=> 그동안 사용하던 그래들을 쓰려고 했으나, xml 방식을 안 쓴 지 오래된 관계로 강의 따라서 Maven 쓰기로 결정

 

 JAVA 11(JAVA 8 이상)

강의의 세팅을 그대로 사용하면 JAVA 11이 호환이 되지 않을 때가 있다.

특히 javax.xml.bind.JAXBException 에러가 발생할 수 있는데, 그럴 땐 pom.xml에 아래와 같은 디펜던시를 추가해 주면 된다.

<dependency>
    <groupId>javax.xml.bind</groupId>
     <artifactId>jaxb-api</artifactId>
    <version>2.3.0</version>
 </dependency>

https://www.inflearn.com/questions/13985/java11-javax-xml-bind-jaxbexception-%EC%97%90%EB%9F%AC

=> 참고한 링크

 

▶ IntelliJ 설정① Maven 설정

  • groupId: jpa-basic
  • artifactId: ex1-hello-jpa
  • version: 1.0.0

② 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>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <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>2.2.220</version>
        </dependency>

        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.0</version>
        </dependency>
    </dependencies>

</project>

1] 하이버네이트 버전 : 5.3.10.Final

2] H2 데이터베이스 버전 : 2.2.220

    ※ 주의 : 실제로 다운받은 H2 데이터베이스 버전을 써줘야 한다!

3] JAVA 11을 사용하면 javax.xml.bind를 디펜던시에 추가해줘야 한다.

4] Error:java: error: release version 5 not supported가 뜨면 특정 JDK 버전으로 빌드해야 하므로 'maven compiler plugin'을 추가해야 한다. <build>... </build> 안에 추가된 부분이다.

 

③ persistence.xml

  • JPA 설정 파일
  • /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">
        <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.Oracle8iDialect"/>
            <!-- 옵션 -->
            <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.jdbc.batch_size" value="10"/>-->
            <!--<property name="hibernate.hbm2ddl.auto" value="create" />-->
        </properties>
    </persistence-unit>
</persistence>

유의사항

1] java.persistence.jdbc.url은 localhost:8082를 치면 뜨는 H2 데이터베이스 설정과 일치시켜야 한다.

2] hibernate.dialect는 데이터 베이스 방언을 세팅. 오라클, H2, MySQL 등을 선택할 수 있다.

 

■ 데이터베이스 방언

① 데이터베이스 방언이란?

• JPA는 특정 데이터베이스에 종속돼 있지 않다.

• 각각의 데이터베이스가 제공하는 SQL 문법과 함수는 조금씩 다르다.

    예시

        가변문자 : MySQL은 VARCHAR, Oracle은 VARCHAR2

        문자열을 자르는 함수 : SQL 표준은 SUBSTRING(), Oracle은 SUBSTR()

        페이징 : MySQL은 LIMIT , Oracle은 ROWNUM

 

• JPA는 DBMS별로 조금씩 다른 SQL방언을 해결하기 위해 Dialect라는 추상화된 방언 클래스를 제공한다.

② pom.xml에 dialect 속성 지정 방법

주요 RDBMS

• H2 : org.hibernate.dialect.H2Dialect 
• Oracle 10g : org.hibernate.dialect.Oracle10gDialect 
• MySQL : org.hibernate.dialect.MySQL5InnoDBDialect 

 

하이버네이트는 40가지 이상의 데이터베이스 방언을 지원한다.

 

[2] Hello JPA - 애플리케이션 개발

■ JPA 구동방식

① Persistence에서 설정 정보를 조회하는 곳은 META-INF/persistence.xml이다. 

② persistence.xml에서 설정정보를 조회한 후 EntityManagerFactory를 생성

③ EntityManagerFactory는 EntityManger라는 클래스를 생성해서 사용한다. 

 

■ 실습 - JPA 동작확인

JPA Main

EntityManagerFactory emf = Persistence.createEntityManager("hello");
EntityManager em = emf.createEntityManager();

//code 

em.close();
emf.close();

 

EntityManagerFactory를 만들 때 persistence.xml의 name과 꼭 일치시켜야 한다.

 

객체와 테이블을 생성하고 매핑하기

① H2 데이터베이스에서 Member 테이블 생성

 

② Member 클래스 생성

Member

1] @Entity로 지정해 줘야 한다. 추가로 @Table(name=)을 넣어주면 매핑할 테이블의 이름을 지정할 수 있다. 없으면 class명으로 만든다.

2] @Id로 기본키를 지정해야 한다.

3] @Column(name="username") 만일 칼럼명이 username이면 이렇게 지정해 줘야 알맞게 값이 들어간다. 

4] getter와 setter도 필요하다.

 

■ 실습 - 회원 저장

① 회원 등록

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 member = new Member();
            member.setId(2L);
            member.setName("HelloB");

            em.persist(member);

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

        emf.close();
    }
}

1] EntityTransaction tx = em.getTransaction() : DB 데이터 등록 변경 등은 트랜잭션 내에서 이루어져야 한다. 

tx.begin() : 트랜잭션 시작

tx.commit() : 트랜잭션이 성공적으로 마무리됐고 DB가 일관성이 있을 때 트랜잭션이 끝났다는 것을 알려주기 위한 연산

tx.rollback() : 비정상적으로 종료돼 원자성이 깨진 경우 트랜잭션을 처음부터 시작하거나 연산 결과를 취소시킨다.

=> 원자성 : 트랜잭션이 DB에 모두 반영되든가 전혀 반영되지 않아야 한다.

 

참고] 트랜잭션 예시

게시판 사용자가 게시글을 작성 후 등록버튼을 누른다. 이후 자신의 글이 포함된 게시판을 보게 된다.

insert문(게시글 등록) + 게시판 조회(select) : 이런 하나의 작업 단위가 하나의 트랜잭션이다.

 

2] Member객체에 id와 name 세팅

3] em.persist(member)로 db에 등록

    => 엄밀히 말하면 엔티티를 영속성 컨텍스에 저장하는 것이다.(다음 챕터에서 설명)

4] EntityMangerFactory와 EntityManager가 생성 종료된 것을 확인할 수 있다.

 

② 회원 조회

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() = " + findMember.getId());
            System.out.println("findMember.getId() = " + findMember.getName());

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

        emf.close();

        //code
        em.close();

        emf.close();
    }
}

1] em.find()를 쓰면 select문이 날아간다. (1L이라는 아이디를 가진 값이 있다고 가정)

 

③ 회원 삭제

Member findMember = em.find(Member.class, 1L);
em.reomve(findMember);

db에서 삭제하려면 em.remove()를 쓰고 해당 객체를 넣으면 된다.(id가 아닌 객체 전체)

 

④ 회원 수정

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

        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction(); //JPA 안 모든 변경은 transaction 안에서 이뤄져야 함.
        tx.begin();

        try{
            Member member = em.find(Member.class, 150L);
            member.setName("ZZZZZ");

            System.out.println("============");

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

1] member를 찾은 후 setName으로 이름을 교체

2] update 쿼리가 날아가는 것을 확인할 수 있다.

JPA에선 트랜잭션 커밋하기 전 엔티티와 스냅샷(값을 읽어온 최초 시점)을 비교하고 바뀐 부분이 있다면 update 쿼리를 날린다.

=> 자세한 내용은 [3] 영속성 관리에서 확인

 

■ JPQL 소개

• JPA를 사용하면 엔티티 객체를 중심으로 개발
• 문제는 검색 쿼리
• 검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색
• 모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능
• 애플리케이션이 필요한 데이터만 DB에서 불러오려면 결국 검색 조건이 포함된 SQL이 필요

그래서 만들어진 게 JPA에서 SQL 역할을 하는 JPQL

 

① JPQL로 전체회원 검색

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

JPQL로 Member 클래스의 전체 회원 검색

2] for문으로 전체회원의 이름 로그창에 찍기

3] select 쿼리문이 날아가는 걸 확인할 수 있다.

 

② JPQL로 까다로운 pagination도 손쉽게 가능

페이징 : 조회된 결과를 몇 번째부터 몇 개씩 가져올지 정하기

setFirstResult(1) : 시작위치는 첫 번째부터

setMaxResults(10) : 시작위치부터 10개씩 가져오기

 

③ 데이터 방언 바꿔보기 (H2=>Oracle)

오라클로 바꾸면 Pagination을 rownum으로 하는 것을 확인할 수 있다(JPA가 각 DBMS에 맞게 알아서 처리해 줌).

 

JPQL은 [10], [11] 객체지향 쿼리 언어에서 더 자세히 다룰 예정이다.