JPA Concept And Basic

JPA는 왜 필요한가?

객체지향언어와 SQL(관계형 DB)

  • 관계
    • 현재 시대는 객체를 관계형DB에 관리하는 시대
    • 객체지향언어과 SQL을 동시에 사용
  • 문제점
    • 객체지향언어를 사용하면서 코드 자체는 객체지향적으로 사용하지 않고 SQL에 집중하여 SQL 중심적으로 코드를 짬!

SQL 중심적인 개발의 문제점

  • 문제점
    • SQL을 통해 CRUD 를 하기 위해 직접 해당 query를 작성
    • 또한, 자바 객체를 SQL로.. SQL을 자바 객체로.. 변환하며 복잡하고 번잡한 코드를 작성
    • ex) 객체 필드 추가에 따른 CRUD 변경 (매핑을 위한 작업을 일일히 수정해주어야 함!)
    • 그렇다고 SQL에 의존적인 개발을 피하기는 사실상 불가능
  • 패러다임의 불일치
    • 객체지향의 사상 vs 관계형 DB의 사상
    • 관계형 DB의 사상 : 데이터를 잘 보관하여 정규화하는 것이 목표
    • 객체 지향의 사상 : 속성과 method를 잘 묶어 캡슐화하는 것이 목표
    • 즉, 둘의 패러다임(사상)이 달라서 여러가지 문제점이 발생
    • 결국 이 패러다임의 불일치를 해결하기 위해 객체 ↔ SQL 매핑 작업이 필수적이며 이는 개발자가 진행함에 있어서 주 비지니스를 맡기 보다는 SQL 매퍼가 되어가는 상황을 직면 (해결이 시급했음!)
    • 객체와 관계형 DB의 차이
      • 상속 (객체 O RDB X)
        • RDB에서 객체의 상속 관계와 가장 유사한 것이 ‘슈퍼타입 서브타입 관계(DTYPE)
        • 하지만 이를 객체와 매핑하려면 저장, 조회 등에서 굉장히 복잡하며 비효율적인 과정을 거쳐야 함
        • 그렇다면 DB가 아닌 Java Collection에 저장한다 생각하면? → 저장, 조회가 굉장히 쉬워짐. 또한 다형성 활용도 가능!
      • 연관관계 (객체 : 참조 RDB: PK, FK, Join)
        • 객체는 추가설정이 없는 한 단방향으로 밖에 이동 불가능, RDB는 추가설정 없이 양방향 이동 가능
        • 패러다임의 차이를 해결하기 위해 만약 객체를 테이블에 맞추어 모델링한다면? → 말 그대로 객체 지향적인 코드가 아님!
        • SQL의 JOIN 을 통해서 객체를 가져올 경우 객체 그래프 탐색이 불가능 → 처음 query를 통해 불러온 범위에 의해서 한정됨. → 그에 따른 엔티티 신뢰 문제(객체의 연관관계를 신뢰하며 그냥 부를 수 없게 되는 것! query에서 불러오지 않았을 수도 있으므로) 발생!! (물리적으론 가능하지만, 논리적으로는 불가능한 경우)
        • 그렇다고 모든 객체를 미리 로딩할 수는 없음
        • 만약 DB가 아닌 Java Collection에 저장한다 생각하면? → 참조값이 함께 들어가기 때문에 저장 및 조회가 간단해지고 객체 그래프 탐색 가능!
      • 데이터 타입, 데이터 식별 방법 등
    • 즉, RDB에 객체를 저장하며 객체답게 모델링 할수록 매핑 작업만 늘어나며 비효율적인 상황이 발생하게 됨!
    • 그렇다면 지속적으로 봐왔던 것처럼 객체를 Java Collection에 저장하듯이 DB에 저장할 수는 없을까?? → JPA(Java Persistence API) 개념 도입

JPA(Java Persistence API)

JPA

  • Java Persistence API
  • 인터페이스의 모음 (즉, 구현체를 요구함 → 구현체는 대표적으로 Hibernate 를 사용)
  • DB 이용을 마치 Java Collection에 객체를 담는 식으로 이용한다”는 사상
  • 자바 진영의 ORM 기술 표준 ⇒ ORM(Object-relational mapping = 객체 관계 매핑) : 객체는 객체대로 설계하고 관계형 DB는 관계형 DB대로 설계한 후 해당 프레임워크가 중간에 알아서 매핑해준다는 의미
  • JPA는 Application과 JDBC 사이에서 동작!

    Untitled

    • 개발자가 직접 JDBC를 사용하는 것이 아니라
    • 개발자가 JPA에게 명령을 내리면 JPA가 JDBC API를 사용하여 DB에 접근하는 것
  • JPA 동작
    • 저장

      Untitled

      • JPA에게 Entity 객체와 명령어를 같이 보내주면
      • JPA는 Entity를 분석하여 그에 맞는 INSERT query를 생성하여 JDBC API를 이용하여 DB를 사용하게 됨
      • JPA가 자동으로 직접 query를 생성해준다는 부분이 중요!
      • 패러다임 불일치 해결!
    • 조회

      Untitled

      • JPA에게 해당 Entity 객체를 찾으라 명령하면
      • JPA는 Entity를 분석하여 적절한 SELECT query 를 만들JDBC API를 이용하여 DB에 접근
      • 그 후 해당 ResultSet을 객체에 자동으로 매핑해줌
      • 패러다임 불일치 해결!
    • 즉, JPA가 그 번잡했던 작업들(CRUD, 매핑, … )을 직접해준다는 것!!

JPA는 왜 사용해야 하는가?

  • 생산성 (JPA와 CRUD)
    • 저장 : em.persist(member)
    • 조회 : em.find(memberId, Member.class)
    • 수정 : member.setName(”변경 이름”)영속성 컨텍스트 속 변경감지
    • 삭제 : em.remove(member)
    • 이와 같이 JPA가 제공하는 것을 통해 CRUD를 쉽게 이용할 수 있음
  • 유지보수
    • 기존 : 객체 필드 변경 시, Table에 매핑하기 위해 모든 CRUD query를 수정해야 했었음
    • JPA 사용 : 자동으로 매핑되기에 알아서 적용됨. 즉, CRUD query는 JPA가 처리해줌
  • JPA와 패러다임의 불일치 해결
    • 상속 관계
      • JPA를 통해 상속 관계에 있는 객체를 저장 및 조회해주면 해당 관계의 모든 객체를 알아서 저장 및 조회해줌 → 즉, DB 중심적인 개발에서 벗어날 수 있음
    • 연관 관계
      • 저장 : 연관관계에 있는 객체 하나를 저장하면 해당 객체와 연관된 객체를 알아서 저장해줌 (cascade)
      • 조회 : 한 객체를 조회하면, 그와 연관관계가 있는 객체를 알아서 끌어와줌 (fetch) → 객체 그래프 탐색 가능
      • 마치 Java Collection 에서 객체를 다루듯이 DB를 이용할 수 있어짐
    • 신뢰할 수 있는 Entity, 계층
      • JPA의 “지연 로딩, 즉시 로딩”에 의해 가능한 부분
      • JPA를 통해서 연관관계 저장 및 접근이 확실히 이루어진다는 것을 알 수 있음
      • 그에 따라 자유롭게 객체 그래프 탐색이 가능해짐
    • 객체 비교
      • 동일한 Transaction에서 조회한 Entity는 같음을 보장 (식별자 사용)
  • JPA의 성능 최적화 기능
    • 1차 캐시와 동일성 보장
      • 동일한 Transaction안에서는 같은 Entity를 반환 (즉, 캐시를 사용) → 약간의 조회 성능 향상
    • Transaction을 지원하는 쓰기(INSERT) 지연
      • Transaction 커밋할 때까지 INSERT query를 모음
      • JDBC BATCH SQL 기능을 사용하여 한번에 SQL 전송 (JPA를 통해 쉽게 이용할 수 있어진 것)
      • 즉, 그 즉시 query가 나가는 것이 아닌, 일단 모은 후에 중복되는 것들에 대해선 최적화 시켜준 후 한번에 query를 보내어 성능 최적화 실시 → 네트워크 측면에서의 최적화
    • 즉시 로딩과 지연 로딩
      • 지연 로딩 : 일단 객체를 모두 끌어와 놓는 것이 아닌, 실제 필요할 때에 끌어 오는 것Proxy 기능 사용
        • 장점 : 쓸데없는 데이터를 불러오지 않아도 됨. 무한루프에 빠질 염려가 없음 (연관관계 속 무한루프)
        • 단점 : query가 많이 나감 (네트워크 측면)
      • 즉시 로딩 : 불러오는 객체와 연관된 모든 객체를 미리 끌어와 놓아 사용하는 것
        • 장점 : query 한방으로 해결
        • 단점 : 쓸데없는 데이터를 모두 가져오게 되어 부담이 되고, 무한 루프에 빠질 위험이 존재

JPA 프로젝트 기본 설정

프로젝트 생성

  • Maven
    • Java 라이브러리, 빌드 관리
    • 라이브러리 자동 다운로드 및 의존성(Depedency) 관리
    • 요즘은 Gradle을 더 많이 사용
    • 이전 자료들은 보통 Maven을 기준으로 사용하기 때문에 알아두면 좋음

프로젝트 설정

  • 라이브러리 설정 (JPA, DataBase 라이브러리)
    • 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>
                <!-- JAVA11에서 Hibernate를 사용하기 위함 -->
                <dependency>
                    <groupId>javax.xml.bind</groupId>
                    <artifactId>jaxb-api</artifactId>
                    <version>2.3.0</version>
                </dependency>
                <!-- 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>
            <properties>
                <maven.compiler.source>17</maven.compiler.source>
                <maven.compiler.target>17</maven.compiler.target>
            </properties>
        </project>
      
    • JPA Hibernate 라이브러리 다운로드 및 의존성 추가
    • H2 DataBase 라이브러리 다운로드 및 의존성 추가
    • Java 11 에서 Hibernate 를 이용하기 위한 라이브러리 다운로드 및 의존성 추가
  • JPA 설정
    • persistence.xml

        <?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="park"/>
                    <property name="javax.persistence.jdbc.password" value=""/>
                    <property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/jpa-basic"/>
                    <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 설정 파일
      • 정해진 규칙에 따라 “/META-INF/persistence.xml” 에 위치
      • javax.persistence로 시작하는 속성: JPA 표준 속성
      • hibernate로 시작하는 속성: 하이버네이트 전용 속성
      • JPA는 특정 DB에 종속하지 않기에 여러 각각의 DB의 문법,함수 차이에 영향을 받지 않음 (데이터베이스 방언)

        Untitled

        • 속성 파일의 <property name="hibernate.dialect" value="..."/> 부분
        • Hibernate는 40가지 이상의 DataBase Dialect 지원

JPA 기본 사용법

JPA 구동 방식 이해

  • JPA 구동 방식

    Untitled

    • 먼저 persistence.xml 에 설정된 JPA 설정 정보를 이용한 Persistence를 통해 EntityMangerFactory 생성EntityManagerFactory emf = Persistence.*createEntityManagerFactory*("hello");
      • EntityMangerFactory
        • EntityManger 를 여러 개를 관리하고 생성할 수 있는 Factory.
        • application 에서 하나만 생성해서 사용해야 됨
        • 모든 application 의 동작이 끝나면 닫아줘야 함 → emf.close();
    • 그 후 EntityMangerFactory를 통해 EntityManger 생성EntityManager em = emf.createEntityManager();
      • EntityManger
        • DB 조회, 저장 등 단일적인 DB 연결 및 사용 시 Entity Manger 를 생성하고 이를 통해 진행
        • 쉽게 생각하면 DB Connection 을 하나 받았다고 생각
        • 여러개 생성 가능
        • 고객의 요청이 올 때마다 생성하고 버려지고 하는 존재 (그냥 진짜 DB Connection 과 같다 생각하면 됨)
        • 엔티티 매니저는 쓰레드간에 공유해선 안됨 (사용하고 버려야 함)
        • 한 단위의 작업이 끝나면 항상 엔티티 매니저는 닫아줘야 함 → em.close();
    • 실제 DB와의 작업을 위해 Trasaction 생성EntityTransaction tx = em.getTransaction();
      • Transaction
        • 저장, 조회, 삭제, 수정 등 모든 EntityManger의 작업이 이루어지는 하나의 단위
        • 모든 EntityManger의 작업(데이터 변경)은 해당 Transaction 안에서 이루어져야 됨
        • Transaction 시작 : tx.begin();
        • Transaction 끝 및 SQL 쿼리저장소 속 query 전송 : tx.commit();commit 으로 최종 query 를 날리는 것
        • [참고] Transaction 작업 취소 : tx.rollback();

JPA 사용

  • 객체와 테이블 생성 및 매핑
    • @Entity
      • 해당 Class 를 Entity 로 사용하겠다! 선언하는 것 → 원하는 객체를 Table과 매핑시키겠다는 것
      • 즉, Entity Manger 에서 관리하겠다는 뜻
    • @Id
      • 해당 Field 를 PK(Primary Key) 로 설정
      • 필수 값영속성 컨텍스트(1차 캐시)에서 해당 id 값을 key 로 사용하고 Entity 를 value 로 사용하여 저장하기 때문
    • ex) Member 객체 생성 및 Table과 매핑
      • Member 객체

          @Entity 
          public class Member {
          	 @Id 
          	 @GeneratedValue(strategy = GenerationType.IDENTITY)
          	 private Long id;
          	 private String name;
          	 //Getter, Setter …
          }
        
      • 그에 따른 Member Table (JPA가 자동으로 생성해준 Create query)

          create table Member (
          	 id bigint not null,
          	 name varchar(255),
          	 primary key (id)
          );
        
  • JPA를 통한 객체 Baisc CRUD
    • 객체

        Member member = new Member();
        member.setId(1L);
        member.setName("HelloA"); // 비영속 상태
      
    • 객체 저장 : em.persist(member)
    • 객체 조회 : Member foundMember = em.find(Member.class, member.getId());
    • 객체 수정 : foundMember.setName("Edit Hello"); [변경 감지]
    • 객체 삭제 : em.remove(foundMember);
    • JPA를 통해서 ORM을 실행한다면, 이전과는 다르게 굉장히 간단하게 DB CRUD가 가능 해지며, 번잡한 매핑과정도 거치지 않아도 됨!

JPQL 간단 소개

  • SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어
  • 테이블이 아닌 객체를 대상으로 검색하는 객체 지향 쿼리 → SQL을 추상화해서 특정 데이터베이스 SQL에 의존X
  • SQL과 문법 유사, SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 지원
  • JPA의 가장 단순한 조회방법으로는 비지니스로직을 수행할 수 없는 경우 사용 [ ex) 나이가 10세 이하인 회원 모두를 조회 ]
  • 검색을 할 때도 테이블이 아닌 Entity 객체를 대상으로 검색 가능
  • 즉, JPA의 기본 조회에서 검색 조건이 포함된 SQL을 추가한 것