ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JPA 기본
    BackEnd/JPA 2021. 4. 4. 22:08

     

    객체와 관계형 데이터베이스의 차이

     

    객체지향 프로그램은 추상화, 캡슐화, 정보은닉, 상속, 다형성 등 시스템의 복잡성을 제어할 수 있는 다양한 장치들을 제공한다. 비지니스 요구사항을 정의한 도메인 모델도 객체로 모델링하면 객체지향 언어가 가진 장점을 활용할 수 있다.

    하지만 이 객체를 메모리가 아닌 어딘가에 영구 보관해야하는데, 이는 관계형 데이터베이스이다.

    관계형데이터베이스는 객체지향과 목적이 다르므로 기능과 표현방법이 다르다.

     

    1. 상속

    테이블에는 상속이라는 개념이 없다. 비슷하게 슈퍼타입, 서브타입이 있지만 객체지향에서 말하는 상속과 다르다.

    예를 들어 Item 이라는 부모 테이블에 Album, Movie 등의 자식 테이블이 있다면, 관계형 데이터베이스에서 이를 관리하기 위해서 Item 과 해당 자식 테이블 모두 SQL 을 따로 작성해줘야하고 Dtype 도 넣어줘야한다.

    이런 과정은 패러다임의 불일치를 해결하기 위한 작업이라고 볼 수 있다.

    자바 컬렉션에 보관한다면 부모 자식이나 타입에 대한 고민없이 컬렉션을 사용하면 된다.

    JPA에서는 이런 패러다임 불일치를 해결해주는데, 자바 컬렉션에 저장하듯 코드를 작성하면 SQL 을 두개로 나눠 각 테이블에 저장해준다.

     

    2. 연관관계

    객체는 참조를 사용해 연관 객체를 조회한다. 테이블은 외래키를 이용해 조인을 사용해 연관 테이블을 조회한다.

    또, 객체는 참조가 있는 방향으로만 조회가 가능하지만, 테이블은 외래키 하나로 양방향 조회가 가능하다.

     

    그러면 객체를 테이블에 맞춰 모델링 해야한다.

    예를 들어 Member 에 Team 객체를 참조하는 대신 테이블 외래키인 Long teamId 필드를 가지는데, 

    이는 객체처럼 member.getTeam().getName() 처럼 사용하지 못하게 된다.

    그러면 개발자는 이를 중간에서 변환해주는 작업을 해주어야 한다.

    JPA 를 사용하면 연관관계에 대한 패러다임 불일치 문제를 해결해준다. member.setTeam(team); 

     

    3. 객체그래프 탐색

    객체의 연관관계에서는 member.getOrder.getOrderItem() 과 같이 연관된 객체를 참조를 통해 계속 탐색할 수 있다.

    하지만 SQL 에서는 처음 실행하는 SQL 에 따라 어디까지 데이터 탐색이 가능한지 정해진다.

    Member 를 조회하면서 어디까지 조회했는지 확인하기 위해 DAO, SQL 을 모두 확인해봐야 하며, 어느 객체까지 조회했는지 상황에 따라 메서드를 여러개 만들어야 할 수 있다.

    이것은 진정한 의미의 계층 분할이 되지 않고 엔티티를 신뢰할 수 없으며 SQL 의존적인 개발을 피하기 어렵다는 단점이 드러나는 것이다. 

    JPA 를 사용하면 실제 객체를 사용하는 시점까지 DB 조회를 미루며 객체를 신뢰하고 그래프를 마음껏 탐색할 수 있다.

     

    4. 비교 / 동일성 비교 

    list.get(0) 과 list.get(1) 에 같은 객체보관했다면 둘은 == 비교해서 true 를 반환한다. 하지만 dao.getMember() 를 두 번하면 두 객체는 동일성 비교에서 false 를 반환한다.

    DB에 같은 로우를 조회할 때마다 같은 인스턴스를 반환하도록 구현하는 것은 어렵다.

    JPA 에서는 영속성 컨텍스트를 사용해 같은 트랜잭션일 때 같은 객체가 조회되는 것을 보장한다.

     

    JPA 는 자바 ORM 기술에 대한 API 표준 명세이다. 인터페이스를 모아둔 것이다.

    JPA 를 사용하려면 JPA 를 구현한 ORM 프레임워크를 선택해야하는데, 하이버네이트가 가장 대중적이며 이외에 EclipseLink, DataNucleus 가 있다. 

     

    JPA 사용이유 

    > SQL 을 반복적으로 작성하고 JDBC API 를 반복적으로 사용하는 작업을 JPA 가 대신해준다.

    > 수정/삭제에 대해 위와 같은 이유로 유지보수해야하는 코드가 줄어든다.

    > 패러다임의 불일치 해결

    > 애플리케이션과 데이터베이스 사이에서 다양한 성능 최적화 기회를 제공한다. 

    > 애플리케이션과 데이터베이스 사이에 추상화된 데이터 접근 계층을 제공해 특정 데이터베이스 기술에 종속되지 않는다.


    ORM (Object-Relational Mapping) 객체 관계 매핑

    -객체는 객체대로, 관계형 데이터베이스는 관계형 데이터베이스대로 설계

    -ORM 프레임워크가 중간에서 매핑

    -JPA 는 애플리케이션과 JDBC 사이에서 동작 

     

     

    JPA 의 역할 

    -Entity 분석

    -SQL 생성

    -JDBC API 사용

    -패러다임 불일치 해결

     

    JPA 의 성능 최적화 기능

    1. 1차 캐시와 동일성 보장

    -같은 트랜젝션 내 동일한 엔티티를 반환한다.

    -DB Isolation level 이 READ ONLY 더라도 애플리케이션에서 Repeatable Read 를 보장한다.

     

    2. 트랜젝션을 지원하는 쓰기 지연

    - 버퍼에 데이터를 모으듯이 sql 문을 바로 전송하지 않고 모아두다가 트랜젝션을 commit 하면 한 번에 전송한다.

     

    3. 지연로딩 (객체가 실제 사용될 때 로딩 <-> 즉시 로딩)

    -update , delete 로 인한 row lock 시간을 최소화한다.

    -트랜젝션 commit 시 update, delete 실행하고 바로 커밋한다.

     

    데이터베이스의 방언

    데이터베이스마다 제공하는 데이터 타입이나 SQL 문법이 조금씩 다르다. 이를 각 데이터베이스의 방언이라한다.

    JPA 는 특정 데이터 베이스에 종속적이지 않기 때문에 설정에 따라 각 데이터베이스에 맞는 문법으로 SQL 문을 생성한다.

     

    JPA 의 CRUD

    저장 : jpa.persist(object);

    조회 : jpa.find(id);

    수정 : member.setName(newName);

    삭제 : jpa.remove(id);

     

    JPA 설정과 EntityManager 

    -JPA 의 설정정보는 META-INF 아래 persistence.xml 에 설정한다.

    -설정정보는 Persistence 객체를 이용해 EntityManagerFactory 를 생성할 수 있게한다.

     

    EntityManagerFactory  는 ? 

    - EntityManagerFactory를 만드는 것은 JPA 를 동작시키기 위한 기반 객체를 만들고 데이터베이스 커넥션 풀도 생성하므로 비용이 많이 드는 작업이다. 애플리케이션마다 1개만 만들어 공유해서 사용해야 한다.

     

    - EntityManagerFactory여러 EntityManager 를 생성하는데, 이 비용은 거의 들지 않는데. 

    - EntityManagerFactory는 여러 스레드가 동시에 접근해도 안전하므로 스레드간 공유해도 된다.

     

    EntityManager 는 ?

    - EntityManager 는 엔티티를 관리하는 객체이며, EntityManager를 통해 테이블과 매핑된 엔티티를 실제로 사용하게 된다.

    - EntityManager 는 고객 요청 올때마다 생성되며,

    - EntityManager 는 데이터베이스 커넥션과 밀접한 관계가 있으므로, 스레드 공유하면 안된다.

    - EntityManager 는 트랜젝션이 시작되는 시점에 커넥션풀에서 DB커넥션을 얻는다.

    - EntityManager 를 통해 엔티티를 저장, 조회 등을 하면 하면 엔티티 매니저는 그 엔티티를 영속성 컨테스트에 보관하고 관리한다.

    - 영속성 컨텍스트는 엔티티를 영구 저장하는 환경으로 EntityManager 를 통해 영속성 컨텍스트에 접근할 수 있다.

    영속성 컨텍스트는 엔티티 매니저를 생성할 때 하나 만들어진다. 여러 엔티티 매니저가 같은 영속성 컨테이너에 접근할 수도 있다.

    * EntityManager와 영속성 컨텍스트 의 관계는 N:1 이다. (J2EE, 스프링같은 컨테이너 환경에서)

    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="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>

    insert  를 위한 persist 나  update 를 위해 setName 을 사용할 때 transaction 안에 두지 않는다면 아무일도 일어나지 않는다. JPA  에서는 트랜젝션의 단위 개념 중요하고 , 모든 데이터 변경은 트랜젝션 안에서 일어나야한다.

    package hellojpa;
    
    import javax.persistence.*;
    import java.util.List;
    
    public class JpaMain {
        public static void main(String[] args) {
            EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
            EntityManager entityManager = emf.createEntityManager();
            EntityTransaction transaction = entityManager.getTransaction();
            transaction.begin();
    
            try {
                // insert
                Member member= new Member();
                member.setId(2L);
                member.setName("helloB");
                entityManager.persist(member);
    
               // update - jpa 를 통해서 entity 를 가져오면 jpa 가 관리함, 가져온 시점과 달라진 데이터 있으면 update 날림
               Member member = entityManager.find(Member.class, 1L);
                System.out.println("member.getId() = " + member.getId());
                member.setName("HelloJPA");
               
                transaction.commit();
            }catch (Exception ex) {
                transaction.rollback();
            }finally{
                entityManager.close();
                emf.close();
            }
    
        }
    }
    

     


    영속성 컨텍스트의 이점

    1차 캐시 , 동일성 보장, 쓰기 지연, 변경 감지, 지연 로딩

     

    1. 1차 캐시 

    영속성 컨텍스트는 내부에 캐시를 갖고 있는데 이를 1차 캐시라 한다.

    영속 상태의 엔티티는 모두 1차 캐시에 저장된다.

    find 했는데 없으면 DB에서 조회해오고 1차 캐시에 저장한 후 영속상태의 엔티티를 반환한다.

    영속석 컨텍스트는 데이터 트랜잭션마다 만들고 트랜잭션이 끝나면 컨테스트를 지우기 때문에 1차 캐시 이점이 적다 

    2. 동일성 보장 

    동일성 보장은 애플리케이션 레벨에서 트랜잭션 격리수준을 repeatable read 로 제공한다.

    Non-Repeatable Read

    한 트랜잭션에서 같은 쿼리를 두 번 수행할 때, 두 쿼리의 결과가 상이하게 나타나는 비일관성 현상

    한 트랜잭션이 수행 중일 때 다른 트랜잭션이 값을 수정 또는 삭제함으로써 나타난다.

     

    3. 쓰기 지연

    엔티티 매니저는 트랜젝션을 커밋하기 직전까지 데이터베이스에 엔티티를 저장하지 않고 내부 쿼리 저장소에 insert SQL 을 모아둔다. 그리고 트랜젝션 커밋 시점에 한번에 DB에 보내는데 이를 쓰기 지연이라 한다.

     

    엔티티의 생명주기 

    비영속 (new/transient)  : 영속성 컨텍스트와 관계가 없는 새로운 상태

    영속 (managed) : 영속성 컨텍스트에 관리되는 상태  

        - persist : 엔티티를 등록

        - merge : 준영속 상태의 엔티티를 새로운 영속 상태의 엔티티로 반환

    준영속 (detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태,

     > 영속성 컨텍스트의 기능 사용 불가,  지연로딩 불가능, 식별자 값 있음(과거에 영속 상태였으므로)

        - detach(obj) : 특정 엔티티를 준영속 상태로 전환

        - clear() : 영속성 컨텍스트를 완전히 초기화 

        - close() : 영속성 컨텍스트를 종료

    삭제 (removed) : 삭제된 상태 (remove)

     

    * FLUSH 

    데이터베이스와 동기화를 최대한 늦출 수 있는 것은 트랜잭션이라는 작업 단위가 있기 때문이다. 트랜잭션 커밋 직전에만 변경 내용을 데이터베이스에 보내 동기화하면 된다.

     

    -영속성 컨텍스트의 변경내용을 데이터베이스에 반영,동기화

    -영속성 컨텍스트를 비우지 않음

     

    호출방법

    -em.flush()

    -트랜젝션 commit 

    -JPQL 쿼리 실행 

     

    Mode

    -FlushModeType.AUTO  (default) : 커밋이나 쿼리 실행 시 플러시

    -FlushModeType.COMMIT : 커밋할 때만 플러시 

     

    public class MergeMain{
    	static EntityManagerFactory emf = Persistence.createEntityMangerFactory("jpa");
        
        public static void main(String[] args){
        	Member member = createMember("memberA","회원1"); //준영속
            member.setUserName("회원명변경"); // 1차 캐시, 변경감지, 지연로딩, 쓰기지연, 동일성 보장 안됨
            mergerMember(member);
        }
        
        static Member createMember(String id, String name){
        	EntityManager em1 = emf.createEntityManager();
            EntityTransaction tx1 = em1.getTransaction();
            tx1.begin();
            
            Member member = new Member();
            member.setId(id);
            member.setUserName(name);
            
            em1.persist(member);
            tx1.commit();
            
            em1.close();
            return member;
        }
    
     	static Member mergeMember(Member member){  // -- userName="회원명변경", 준영속
        	EntityManager em2 = emf.createEntityManager();
            EntityTransaction tx2 = em1.getTransaction();
            tx2.begin();
                    
            Member member = em2.merge(member); // -- userName="회원명변경" , 영속
            
            tx2.commit();        
            em2.close();
            return member;
        }
    }

     


    엔티티 매핑

    객체와 테이블 매핑 @Entity @Table

    -@Entity 기본 생성자 필수 & final, enum, interface,inner 클래스에 사용불가 , 저장 필드에 final 사용 불가

    필드와 컬럼 매핑 @Column

    기본키 매핑 @Id  (@GeneratedValue 자동생성)

    연관관계 매핑 @ManyToOne @JoinColumn

    날짜타입 매핑 @Temporal (Temporal.DATE, Temporal.TIME, Temporal.TIMESTAMP)

    -java.util.Date,java.util.Calendar 와 매핑, LocalDate,LocalDateTime 을 사용할 땐 생략가능

    enum타입 매핑 @Enumerated (EnumType.STRING)

    BLOB,CLOB 매핑 @Lob (필드타입이 String, char[],java.sql.CLOB 일때는 CLOB 으로, 나머지는 BLOB 으로 자동 매핑)

    매핑 무시 @Transient

     

    기본 키 전략

     

     

    1. 후보키 (Candidate Key)

     

    * 릴레이션을 구성하는 속성들 중에서 튜플을 유일하게 식별할 수 있는 속성들의 부분집합을 의미합니다. 

    * 모든 릴레이션은 반드시 하나 이상의 후보키를 가져야합니다.

    * 릴레이션에 있는 모든 튜플에 대해서 유일성과 최소성을 만족시켜야합니다.

     

    4. 대체키(Alternate Key)

    - 후보키가 두개 이상일 경우 그 중에서 어느 하나를 기본키로 지정하고 남은 후보키들을 대체키라한다.

    - 대체키는 기본키로 선정되지 않은 후보키이다.

     

    기본 키 제약 조건: null 아님, 유일, 변하면 안된다. • 미래까지 이 조건을 만족하는 자연키는 찾기 어렵다. 대리키(대 체키)를 사용하자. • 예를 들어 주민등록번호도 기본 키로 적절하기 않다. 

    • 권장: Long형 + 대체키 + 키 생성전략 사용

     

    initialValue = 1, allocationSize = 10 성능

     

    GeneratorType=Identity 전략

    - db에 값이 들어간 후 id 을 알 수 있다.

    영속성 컨텍스트에서 관리되려면 pk 값을 알아야하는데 db에 들어간 후 알 수 있다는 단점

    이 전략을 사용할 때 JPA 가 insert 하고 select 해옴

     

    GeneratorType=Sequence 전략

    -insert 되기 전에 db에서 sequence 를 가져온다. insert 시 가져온 pk 인 시퀀스와 다른 데이터를 함께 보낸다.

     

    ->allocationSize 로 성능

    기본값은 50

    nextcall 할때 50개를 미리 db에 세팅하고 메모리에서 그 갯수만큼 사용함 


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

     

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

    JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., 본 강의는 자바 백엔

    www.inflearn.com

     

    반응형

    'BackEnd > JPA' 카테고리의 다른 글

    JPA 경로표현식  (0) 2021.04.27
    JPA > JPQL, 프로젝션  (0) 2021.04.27
    JPA > 값 타입  (0) 2021.04.25
    JPA > 영속성 전이와 고아객체  (0) 2021.04.25
    JPA > 연관관계 매핑  (0) 2021.04.18
Designed by Tistory.