본문 바로가기

Dot Programming/JPA

[JPA] JPA 프로젝트 생성 및 개발

    JPA

    JPA란 Java Persistence API의 약자로, Java 객체와 관계형 DB를 맵핑해주는 ORM 프레임워크이다. (자세히)

     

    [JPA] JPA에 대해 알아보자

    JPA란?  과거에는 객체를 DB에 저장하려면 복잡한 JDBC api와 sql을 직접 작성  Jdbc Template이나 MyBatis같은 Sql Mapper가 등장하여 개발코드는 많이 줄었지만 sql은 직접 작성 Sql중심 개발의 문제점은 뭘

    loosie.tistory.com

     

    JPA는 어떻게 왜 사용될까?

    JPA는 애플리케이션과 JDBC사이에서 동작한다. 안에서 JPA는 저장과 조회를 하는데 자세한 동작은 다음과 같다.

    저장

    1.  Entity분석 
    2.  INSERT SQL 생성
    3. JDBC API 사용
    4. 패러다임 불일치 해결

    조회

    1. SELECT SQL 생성
    2. JDBC API 사용
    3. ResultSet 매핑
    4. 패러다임 불일치 해결

     

     JPA 사용해야 하는 이유

    1. SQL 중심적인 개발에서 객체 중심으로 개발
    2. 생산성
    3. 유지보수
    4. 패러다임의 불일치 해결
    5. 성능
    6. 데이터 접근 추상화와 벤더 독립성
    7. 표준
     

    [JPA] JPA는 왜? 어떻게? 사용될까

    우선 알아야 할 점은 JPA는 애플리케이션과 JDBC 사이에서 동작한다는 것이다. JPA 동작 1. 저장 JPA안에서  Entity분석  INSERT SQL 생성 JDBC API 사용 패러다임 불일치 해결 이렇게 4가지를 처리해준다. J

    loosie.tistory.com

     

    개발환경

    앞으로의 포스팅을 통해 JPA를 사용해볼 것인데, 이에 사용할 환경은 다음과 같다.

    • IDE: IntelliJ
    • 빌드툴: Maven
    • 언어: Java
    • Database: H2

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

     

    Maven은 자바 라이브러리, 빌드 관리 툴이다. 라이브러리 자동 다운로드 및 의존성 관리를 해준다. 최근에는 그래들(Gradle)이 점점 유명해지고 있지만 기존 프로젝트는 Maven을 사용하는 것이 많아 Maven을 택했다.

     

    1. 라이브러리 추가 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>org.example</groupId>
        <artifactId>jpa_basic</artifactId>
        <version>1.0-SNAPSHOT</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>1.4.200</version>
            </dependency>
            <dependency>
                <groupId>javax.xml.bind</groupId>
                <artifactId>jaxb-api</artifactId>
                <version>2.3.0</version>
            </dependency>
        </dependencies>
    
    </project>

     

    발생한 에러 

     

    [Java/ Maven] Error:java: error: release version 5 not supported

    Error:java: error: release version 5 not supported Spring Project 빌드 도중 에러가 발생하였다. 이는 jdk버전이 일치하지않아서 발생한 것으로 버전을 설정해주면 된다. ✔︎ 첫 번째 방법 직접 설정..

    loosie.tistory.com

     

    2. JPA 설정하기 - persistence.xml

    persistence.xml은 JPA설정 파일이다.

     

    /META-INF/persistence.xml에 위치하고 persistence-unit name으로 이름 지정해야한다.

    해당 설정에는 JPA 표준 속성인 javax.persistence와 하이버네이트 전용 속성인 hibernate를 설정해주었다.

     

    * Spring Project를 할 때에는 지원을 해주기 때문에 application.yml같은 곳에서 간단하게 설정하면된다.

    <?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.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>

     

    JPA는 특정 데이터베이스에 종속되지 않는다. hibernate.dialect 속성에 지정

    (하이버네이트는 40가지 이상의 데이터베이스 방언 지원)

    • H2 : org.hibernate.dialect.H2Dialect
    • Oracle 10g : org.hibernate.dialect.Oracle10gDialect
    • MySQL : org.hibernate.dialect.MySQL5InnoDBDialect
    <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
    

     

    각각의 데이터베이스가 제공하는 SQL 문법과 함수는 조금씩 다른데 이를 방언으로 칭한다.

    • 방언: SQL 표준을 지키지 않는 특정 데이터베이스만의 고유한 기능

    ex ) 가변 문자: MySQL VARCHAR, Oracle VARCHAR2

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

           페이징: MySQL LIMIT , Oracle ROWNUM

     

     

     

    3. JPA 애플리케이션 개발

    JPA 구동 방식

     

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

    Member 객체 생성 

    @Entity: JPA가 관리할 객체 ,  @Id: 데이터베이스 PK와 매핑

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

     

    h2 db에 Member테이블 생성

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

     

    2. 회원 저장 (em.persist())

    • 엔티티 매니저 팩토리는 하나만 생성해서 애플리케이션 전체에서 공유한다.
    • 엔티티 매니저는 쓰레드간에 공유X (사용하고 버려야 한다)
    • JPA의 모든 데이터 변경은 트랜잭션 안에서 실행
    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) {
            // META-INF -> persistence-unit name :hello
            EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
    
            EntityManager em = emf.createEntityManager();
    
            // 트랜잭션 시작
            EntityTransaction tx = em.getTransaction();
            tx.begin();
    
    
            try{
    //            1.멤버 저장
                Member member = new Member();
                member.setId(1L);
                member.setName("helloA");
    //            --em save--
                em.persist(member);
    
                // 트랜잭션 커밋
                tx.commit();
            }catch (Exception e){
                tx.rollback();
            }finally {
                em.close();
            }
    
            emf.close();
        }
    }
    
    

     

    3. 회원 조회 및 수정 (em.find())

    JPA를 통해 엔티티를 조회(em.find)하면 JPA가 tx.commit()시점에 무엇이 수정되었는지 체크하고 update쿼리를 날려준다. 그래서 수정 후 em.persist()로 저장하지 않아도 된다.

    (Java Collections을 다루듯 설계가 되었음)

    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) {
            // META-INF -> persistence-unit name :hello
            EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
    
            EntityManager em = emf.createEntityManager();
    
            // 트랜잭션 시작
            EntityTransaction tx = em.getTransaction();
            tx.begin();
    
    
            try{
    //            2.멤버 수정
                Member findMember = em.find(Member.class, 1L);
                findMember.setName("helloJPA");
                
                // 트랜잭션 커밋
                tx.commit();
            }catch (Exception e){
                tx.rollback();
            }finally {
                em.close();
            }
    
            emf.close();
        }
    }
    
    

    update 쿼리문

     

    4. JPQL로 멤버 조회

    JPQL

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

     

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

    • JPQL을 한마디로 정의하면 객체 지향 SQL로 SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 지원하여 SQL문법과 유사하다.
    • 그리고 SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않는다.

     

    SQL은 DB테이블을 대상으로 쿼라힌다면 JPQL은 엔티티 객체를 대상으로 쿼리한다.

     

    JPQL의 가장 단순한 조회 방법

    • EntityManager.find()
    • 객체 그래프 탐색(a.getB().getC())

     

    setFirstResult, setMaxResult와 같은 함수로 Pagination도 가능하다.

    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) {
            // META-INF -> persistence-unit name :hello
            EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
    
            EntityManager em = emf.createEntityManager();
    
            // 트랜잭션 시작
            EntityTransaction tx = em.getTransaction();
            tx.begin();
    
    
            try{
    //            jpql 멤버 조회
                List<Member> result = em.createQuery("select m from Member as m", Member.class)
                        .setFirstResult(5) // pagination
                        .setMaxResults(10) // pagination
                        .getResultList();
    
                for(Member member : result){
                    System.out.println("member.getName() = " + member.getName());
                }
    
                // 트랜잭션 커밋
                tx.commit();
            }catch (Exception e){
                tx.rollback();
            }finally {
                em.close();
            }
    
            emf.close();
        }
    }
    
    

     

     


    ❈ 출처

    인프런 - 자바 ORM 표준 JPA 프로그래밍