Back-End/Spring (SpartaCC)

[Spring] JPA - ORM의 배경, Raw JPA 기능

남건욱 2023. 7. 27. 18:38

목차

    반응형
    ORM의 탄생 배경

    탄생 배경을 알아야 하는 이유?

    - 내가 맡아야 할 프로젝트가 JPA가 적용이 안된 프로젝트일 수도 있다.

    - ORM은 갑자기 나타난 게 아니라 점차 발전해 온 DB 연동 기술이론이다.

     

     

     

    JDBC
    Java Database Connectivity

    - Java 앱과 DB를 연결시켜주기 위해 만들어진 기술이다.

    - JPA도 이 기술을 사용하여 구현되어 있다.

     

     

     

     

    JDBC Driver Manager

    - Connection (연결)을 생성하여 쿼리를 요청할 수 있는 상태를 만든다.

    - Statement (상태)를 생성하여 쿼리를 요청하게 해 준다.

    - ResultSet (결과 셋)을 생성하여 쿼리 결과를 받아올 수 있게 해 준다.

    - 사용 후에는 꼭 각각 close()를 호출해서 자원 해제를 시켜줘야 한다.

     

     

     

    JDBC로 직접 SQL을 작성했을 때의 문제

    - SQL 쿼리 요청 시 중복 코드 발생

    - DB별 예외에 대한 구분 없이 Checked Exception 처리

    - 자원관리를 따로 해주어야 한다.

    위와 같은 문제 해결을 위해 Persistence Framework가 등장하였다.

    SQL Mapper : JDBC Template, MyBatis

    ORM : JPA, Hibernate

     

     

     

    JDBC Template

    - 첫 번째 주자

    - 쿼리 수행 결과와 객채 필드 매핑

    - RowMapper로 응답필드 매핑코드 재사용

    - Connection, Statement, ResultSet 반복적 처리를 대신해 준다

    - 그러나 결괏값을 객체 인스턴스에 매핑하는데 여전히 많은 코드가 필요하다

     

     

     

    MyBatis

    - 두 번째 주자

    - 반복적인 JDBC 프로그래밍을 단순화

    - SQL 쿼리들을 XML 파일에 작성하여 코드와 SQL을 분리

    - 결국 SQL을 직접 작성하는 것은 힘들다

    - 테이블마다 비슷한 CRUD 반복, DB타입 및 테이블에 종속적이다.

     

     

     

     

    릴레이션 (관계형 DB)를 객체로 매핑하려는 이유는?

    - 객체 지향 프로그래밍의 장점을 활용할 수 있다.

    - 이를 통해 비즈니스 로직 구현 및 테스트 구현이 편하다.

    - 각종 디자인 패턴을 사용하여 성능 개선이 가능하다.

    - 코드 재사용이 가능하다.

     

     

     

    ORM이 해결해야 하는 문제점과 해결책

    상속의 문제

    - 객체 : 객체 간에 멤버변수나 상속관계를 맺을 수 있다.

    - RDB : 테이블들은 상속관계가 없고 모두 독립적으로 존재한다.

    - 해결방법 : 매핑정보에 상속정보를 넣어준다. (@OneToMany, @ManyToOne)

     

    관계 문제

    - 객체 : 참조를 통해 관계를 가지며 방향을 가진다. (다대다 관계도 있음)

    - RDB : 외래키(FK)를 설정하여 Join으로 조회 시에만 참조가 가능하다. (즉, 다대다는 매핑 테이블 필요)

    - 해결방법 : 매핑정보에 방향정보를 넣어준다. (@JoinColumn, @MappedBy)

     

    탐색 문제

    - 객체 : 참조를 통해 다른 객체로 순차적 탐색이 가능하며 컬렉션도 순회한다.

    - RDB : 탐색 시 참조하는 만큼 추가 쿼리나, Join 이 발생하여 비효율적이다.

    - 해결방법 : 매핑/조회 정보로 참조탐색 시점을 관리한다. (@FetchType, fetchJoin())

     

    밀도 문제

    - 객체 : 멤버 객체크기가 매우 클 수 있다.

    - RDB : 기본 데이터 타입만 존재한다.

    - 해결방법 : 크기가 큰 멤버 객체는 테이블을 분리하여 상속으로 처리한다. (@embedded)

     

     

     

    식별성 문제

    - 객체 : 객체의 hashCode 또는 정의한 equals() 메서드를 통해 식별

    - RDB : PK 로만 식별

    - 해결방법 : PK를 객체 Id로 설정하고 EntityManager는 해당 값으로 객체를 식별하여 관리한다. (@Id, @GeneratedValue)

     

     

     

    1차 캐시

    - 영속성 콘텍스트 내부에는 엔티티를 보관하는 저장소가 있는데 이를 1차 캐시라고 한다.

    - 일반적으로 트랜잭션을 시작하고 종료할 때까지만 1차 캐시가 유효하다.

    - 1차 캐시는 한 트랜잭션 계속해서 원본 객체를 넘겨준다.

     

     

     

    2차 캐시

    - 애플리케이션 범위의 캐시로, 공유 캐시라고도 하며, 애플리케이션을 종료할 때까지 캐시가 유지된다.

    - 2차 캐시는 캐시 한 객체 원본을 넘겨주지 않고 복사본을 만들어서 넘겨준다.

    - 복사본을 주는 이유는 여러 트랜잭션에서 동일한 원본객체를 수정하는 일이 없도록 하기 위해서이다.

     

     

     

    영속성

    - 데이터를 생성한 프로그램이 종료되어도 사라지지 않는 데이터의 특성을 말한다.

    - 영속성을 갖지 않으면 데이터는 메모리에서만 존재하게 되고 프로그램이 종료되면 해당 데이터는 모두 사라지게 된다.

    - 그래서 우리는 데이터를 파일이나 DB에 영구 저장함으로써 데이터에 영속성을 부여한다.

     

     

     

    쓰기 지연이 발생하는 시점

    - flush() 동작이 발생하기 전까지 최적화한다.

    - flush() 동작으로 전송된 쿼리는 더 이상 쿼리 최적화는 되지 않고, 이후 commit()으로 반영만 가능하다.

     

     

     

    쓰기 지연 효과

    - 여러 개의 객체를 생성할 경우 모아서 한 번에 쿼리를 전송한다.

    - 영속성 상태의 객체가 생성 및 수정이 여러 번 일어나더라도 해당 트랜잭션 종료 시 쿼리는 1번만 전송될 수 있다.

    - 영속성 상태에서 객체가 생성되었다 삭제되었다면 실제 DB에는 아무 동작이 전송되지 않을 수 있다.

    - 즉, 여러 가지 동작이 많이 발생하더라도 쿼리는 트랜잭션당 최적화되어 최소쿼리만 날아가게 된다.

     

     

     

    @Entity

    - 객체 관점에서의 이름

    - 디폴트로 클래스명으로 설정됨

    - 엔티티의 이름은 JQL에서 쓰임 (JQL : Entity 명으로 쿼리를 생성할 때 쓰이는 언어이다. JPQL, QueryDsl)

     

     

     

    @Table

    - RDB의 테이블 이름

    - @Entity의 이름이 테이블의 기본값.

    - 주로 Entity이름과 다르게 Table 명을 지정하고 싶을 때 @Table(name = " ** ") 같이 사용한다.

    - 테이블의 이름은 SQL에서 쓰인다.

     

     

     

    @Id

    - 엔티티의 주키를 맵핑할 때 사용.

    - 자바의 모든 primitive 타입과 그 랩퍼 타입을 사용할 수 있음

    - Date랑 BigDecimal, BigInteger도 사용 가능.

    - 복합키를 만드는 맵핑하는 방법도 있다.

     

     

     

    @GeneratedValue

    - 주키의 생성 방법을 맵핑하는 애노테이션

    - 생성 전략과 생성기를 설정할 수 있다.

    - 기본 전략은 AUTO: 사용하는 DB에 따라 적절한 전략 선택

    - TABLE, SEQUENCE, IDENTITY 중 하나.

     

     

     

    @Column

    - unique

    - nullable

    - length

    - columnDefinition

     

     

     

    @Temporal

    - 현재 JPA 2.1까지는 Date와 Calendar만 지원한다.

     

     

     

    @Transiend

    - 컬럼으로 맵핑하고 싶지 않은 멤버 변수에 사용한다.

     

     

     

    @OneToOne

    - 일대일 관계를 나타내는 매핑 정보

    - 1:1 관계를 지정하기에 앞서 이것이 꼭 물리적으로 테이블이 분리되어야 하는지에 대해 생각해 봐야 한다.

    - 1:1 관계로 구성한다는 것은 결국 하나의 목적에 부합되는 공통된 데이터를 관리한다고 볼 수 있으며 이것은 하나의 테이블에서 관리할 수 있는 데이터일 가능성이 높다는 의미이다.

    - 즉, 의도적 중복이 아니라면 사용할 일이 없다는 말이다.

    - 의도적 중복 예시) 버블 구독상품을 사서 채팅방이 생길 경우. 구독상품과 채팅방은 1:1 관계

     

     

     

    @OneToMany

    -일대다 관계를 나타내는 매핑 정보

    - 속도를 위해 기본적으로 FetchType 설정이 LAZY로 되어있다.

    속성

    - mappedBy : 연관관계의 주인 필드를 선택한다.

    - fetch : 글로벌 페치 전략 설정

    - cascade : 영속성 전이 기능을 사용한다.

    - targetEntity : 연관된 엔티티의 타입 정보를 설정한다.

     

     

     

    @ManyToOne

    - 다대일 관계를 나타내는 매핑 정보

    속성

    - optional (default true) : false로 설정하면 연관된 엔티티가 반드시 있어야 함.

    - fetch : 글로벌 패치 전략 설정

    - cascade : 영속성 전이 기능 사용

    - targetEntity : 연관된 엔티티의 타입 정보 설정 (targetEntity = Member.class 식으로 사용)

     

     

     

    @JoinColumn

    - 외래 키 매핑 시 사용 (Join을 요청하기 위한 매핑정보로 쓰인다.)

    - @ManyToOne 어노테이션과 주로 함께 쓰인다. (조인대상 칼럼 지정기능을 안 쓸 거면 생략해도 됨)

    - name 속성은 매핑할 외래키의 이름

    - 어노테이션을 생략해도 외래 키가 생성됨.

    - 생략 시 외래키의 이름이 기본 전략을 활용하여 생성된다.

    속성

    - name : 매핑할 외래 키의 이름

    - referencedColumnName : 외래 키가 참조하는 대상 테이블의 컬럼명

    - foreignKey : 외래 키 제약조건 지정 (테이블 생성 시에만 적용됨)

     

     

     

    @ManyToMany

    - 다대다 관계를 나타내는 매핑 정보 (N:M)

    - 다대다 설정을 하게 되면 중간 매핑테이블(JoinTable)이 자동으로 생성된다.

    - 중간 매핑 테이블은 JPA상에서 숨겨져서(Entity 정의 없이) 관리된다.

    - 매핑 테이블 관리가 불가능하여서 실무에서는 잘 사용하지 않는 기능이다.

     

     

     

     

    Cascade (영속성 전이)

    사용 위치

    - 연관관계의 주인 반대편 - 부모 엔티티(다대일에서 일)

    - 즉, @OneToMany 가 있는 쪽 또는 @OneToOne 도 가능

    - 예를 들어, 게시글과 첨부파일이라면 일에 해당하는 게시글에 설정한다.

     

    사용 조건

    - 양쪽 엔티티의 라이프사이클이 동일하거나 비슷해야 한다.

    - 예를 들어, 게시글이 삭제되면 첨부파일도 같이 삭제되어야 한다.

    - 대상 엔티티로의 영속성 전이는 현재 엔티티에서만 전이되어야 한다. (다른 곳에서 또 걸면 안 된다.)

    - 예를 들어, 첨부파일을 게시글이 아닌 다른 곳에서 영속성 전이를 하면 안 된다.

     

    옵션 종류

    - ALL : 전체 상태 전이

    - PERSIST : 저장 상태 전이

    - REMOVE : 삭제 상태 전이

    - MERGE : 업데이트 상태 전이

    - REFERESH : 갱신 상태 전이

    - DETACH : 비영속성 상태 전이

     

     

    Fetch (조회시점)

    사용위치

    - Entity에 FetchType으로 설정할 수 있다.

    - Query 수행 시 fetch Join을 통해서 LAZY 인 경우도 즉시 불러올 수 있다.

     

     

     

    사용법

    - 기본 LAZY를 설정한 뒤에 필요할 때만 fetch Join을 수행한다.

    - 항상 같이 쓰이는 연관관계 일 경우만 EAGER를 설정한다.

     

     

    옵션(FetchType)

    - EAGER : 즉시 로딩 (부모 조회 시 자식도 같이 조회)

    - LAZY : 지연 로딩 (자식은 필요할 때 따로 조회)

     

     

     

    반응형
    프로필사진

    남건욱's 공부기록