TIL 49 [ JPA 심화 - ( MyBatis, ORM, 테이블 객체, Raw JPA 연관관계) ]

2025. 5. 1. 20:25·[내일배움캠프-Sparta]/Spring 6기
반응형

11:00 ~ 12:30  트랜잭션, 영속성 컨텍스트 세션

16:00 ~ 17:00 스탠다드반 Spring Security 세션

1️⃣ 쿼리 파일 만들기 ( QuerryMapper )

✅ MyBatis 

🔍 MyBatis는 RowMapper가 가진 단점인  " 반복되는 코드 " 줄이고 " 함께있는 프로그램 코드와 쿼리 코드를 분리하여 관리 " 하고 싶은 니즈를 반영하여 탄생함.

SQL Mapper 두번째 주자로 MyBatis 탄생

  • 반복적인 JDBC 프로그래밍을 단순화 하고싶다.
  • SQL 쿼리들을 XML 파일에 작성하여 코드와 SQL 을 분리 하고싶다.

MyBatis 특징

  • jdbc로 처리하는 코드의 설정 부분을 줄이고 실제 sql문에 연결함으로서 빠른 개발이 가능하게 한다. 
  • MyBatis 코드는 map 인터페이스(또는 클래스)와 SQL 쿼리와 ResultSet 매핑을 위한 xml 및annotation을 사용
  • 다른 방식에 비해 객체 자체보다 쿼리에 집중 가능

MyBatis 한계점

  • 결국 SQL을 직접 작성하는것은 피곤하다…(DB 기능에 종속적) 😫
  • 테이블마다 비슷한 CRUD 반복, DB타입 및 테이블에 종속적

MyBatis 동작

SqlSession Factory Builder (1), (2), (3)

  • MyBatis 설정 파일을 읽어와서
  • 설정정보 기반으로 SqlSession Factory 를 생성하는 빌더 객체

MyBatis Config File (2)

  • 매핑해줄 객체가 들어있는 패키지 경로와
  • Mapping File 목록을 지정해주는 설정 파일

SqlSession Factory (3), (5), (6)

  • 설정에 맞게 SqlSession 을 생성하여 들고있는 객체

SqlSession (6), (7), (9), (10)

  • Mapping File 에서 쿼리를 조회해서 쿼리를 수행하고 응답을 받아올 수 있는 세션 객체

Mapper Interface (8), (9)

  • DB 에서 조회하는 객체와 Java 프로그램의 객체간에 인터페이스를 정의하는 객체
  • 방법1. Dao 클래스 정의
    • SqlSession 를 직접적으로 사용하는 방법
    • SqlSession 멤버 변수로 사용하며 쿼리파일 수행 요청
// UserDao.java
import org.apache.ibatis.session.SqlSession;
import org.springframework.stereotype.Component;

import com.thesun4sky.querymapper.domain.User;

@Component
public class UserDao {

  // SqlSession 멤버 변수로 사용하며 쿼리파일 수행 요청
  private final SqlSession sqlSession;

  public UserDao(SqlSession sqlSession) {
    this.sqlSession = sqlSession;
  }

  public User selectUserById(long id) {
    return this.sqlSession.selectOne("selectUserById", id);
  }

}
  • 장점
    • 쿼리문 실행 전에 넣어줄 매개변수와 쿼리 결과값의 변형을 정의할 수 있다.
    • Namespace를 내 마음대로 둘 수 있다.
    • .xml 파일의 쿼리문 id와 mapper 메소드명을 일치시킬 필요가 없다.
  • 단점
    • Sqlsession 객체를 주입받아야 하며, 쿼리문 실행 시 항상 호출해야 한다.
    • 쿼리문 호출 시 sqlsession에 .xml 파일의 namespce와 쿼리문 id를 매개변수로 넘겨야한다.
  • 방법2. Mapper Interface 정의
    • SqlSession 를 간접적으로 사용하는 방법
    • ibatis 에서 구현해주는 org.apache.ibatis.annotations.Mapper 어노테이션을 사용하면 sqlSession 를 사용하여 자동으로 호출
// UserMapper.java
@Mapper
public interface UserMapper {

  User selectUserById(@Param("id") Long id);

}
  • 장점
    • 메소드의 내부 구현이 불필요하다.
    • Sqlsession 객체 주입이 불펼요하다.
    • .xml 파일의 쿼리문 id와 mapper 메소드 명이 일치한다.
  • 단점
    • .xml의 Namespace가 실제 Mapper.java 위치를 가르켜야 한다.
    • 메소드 내부 정의가 불가능

Mapping File (10)

  • SqlSession 가 실행하는 쿼리가 담긴 파일
  • 정의된 인터페이스에 기반해서 수행할 쿼리를 담아두고
  • 쿼리 수행결과를 어떤 인터페이스 매핑할지 정의해놓은 파일

2️⃣ 쿼리 코드 만들기 (JpaRepository)

✅ ORM

  • ORM 은 DB의 주도권을 뺏어왔다고 표현해도 과언이 아닙니다.
  • ORM 은 DAO 또는 Mapper 를 통해서 조작하는것이 아니라 테이블을 아예 하나의 객체(Object)와 대응

✅ 문제점

상속의 문제

  • 객체 : 객체간에 멤버변수나 상속관계를 맺을 수 있다.
  • RDB : 테이블들은 상속관계가 없고 모두 독립적으로 존재한다.

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

관계 문제

  • 객체 : 참조를 통해 관계를 가지며 방향을 가진다. (다대다 관계도 있음)
  • RDB : 외래키(FK)를 설정하여 Join 으로 조회시에만 참조가 가능하다. (즉, 다대다는 매핑 테이블 필요)

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

탐색 문제

  • 객체 : 참조를 통해 다른 객체로 순차적 탐색이 가능하며 콜렉션도 순회한다.
  • RDB : 탐색시 참조하는 만큼 추가 쿼리나, Join 이 발생하여 비효율적이다.

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

밀도 문제

  • 객체 : 멤버 객체크기가 매우 클 수 있다.
  • RDB : 기본 데이터 타입만 존재한다.

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

식별성 문제

  • 객체 : 객체의 hashCode 또는 정의한 equals() 메소드를 통해 식별
  • RDB : PK 로만 식별

 💁‍♂️ 해결방법 : PK 를 객체 Id로 설정하고 EntityManager는 해당 값으로 객체를 식별하여 관리 한다.

( @Id, @GeneratedValue )

✅ 해결책

영속성 컨텍스트(1차 캐시)를 활용한 쓰기지연

  • 영속성 이란?
    • 데이터를 생성한 프로그램이 종료되어도 사라지지 않는 데이터의 특성을 말한다.
    • 영속성을 갖지 않으면 데이터는 메모리에서만 존재하게 되고 프로그램이 종료되면 해당 데이터는 모두 사라지게 된다. ( 휘발성 )
    • 그래서 우리는 데이터를 파일이나 DB에 영구 저장함으로써 데이터에 영속성을 부여한다.
더보기
  • 영속성 4가지 상태 ( 비영속 > 영속 > 준영속 | 삭제)
  • ① 비영속(new/transient) - 엔티티 객체가 만들어져서 아직 저장되지 않은 상태로, 영속성컨텍스트와 전혀 관계가 없는 상태
  • ② 영속(managed) - 엔티티가 영속성 컨텍스트에 저장되어, 영속성 컨텍스트가 관리할 수 있는 상태
  • ③ 준영속(detached) - 엔티티가 영속성 컨텍스트에 저장되어 있다가 분리된 상태로, 영속성컨텍스트가 더 이상 관리하지 않는 상태
  • ④ 삭제(removed) - 엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제하겠다고 표시한 상태
  • 객체의 영속성 상태는 Entity Manager 의 메소드를 통해 전환
    • new > (비영속상태) > persist(),merge() > (영속성 컨텍스트에 저장된 상태) > flush() > (DB에 쿼리가 전송된 상태) > commit() > (DB에 쿼리가 반영된 상태)
  • 쓰기 지연이 발생하는 시점
    • flush() 동작이 발생하기 전까지 최적화한다.
    • flush() 동작으로 전송된 쿼리는 더이상 쿼리 최적화는 되지 않고, 이후 commit()으로 반영만 가능하다.
  • 쓰기 지연 효과
    • 여러개의 객체를 생성할 경우 모아서 한번에 쿼리를 전송한다.
    • 영속성 상태의 객체가 생성 및 수정이 여러번 일어나더라도 해당 트랜잭션 종료시 쿼리는 1번만 전송될 수 있다.
    • 영속성 상태에서 객체가 생성되었다 삭제되었다면 실제 DB에는 아무 동작이 전송되지 않을 수 있다.
    • 즉, 여러가지 동작이 많이 발생하더라도 쿼리는 트랜잭션당 최적화 되어 최소쿼리만 날라가게된다.
    💁‍♂️ 키 생성전략이 generationType.IDENTITY 로 설정 되어있는 경우 생성쿼리는 쓰기지연이 발생하지 못한다.
    • why? 단일 쿼리로 수행함으로써 외부 트랜잭션에 의한 중복키 생성을 방지하여 단일키를 보장

✅ ORM을 사용하기 가장 쉬운 방법 : JpaRepository

더보기

💁‍♂️ Repository vs JpaRepository

  • 기존 Repository
    • @Repository 을 클래스에 붙인다.
    • @Component 어노테이션을 포함하고 있어서 앱 실행시 생성 후 Bean으로 등록된다.
    • 앞서배운 Repository 기본 기능만 가진 구현체가 생성된다. (DB별 예외처리 등)
  • 새로운 JpaRepository
    • JpaRepository<Entity,ID> 인터페이스를 인터페이스에 extends 붙인다.
      • @NoRepositoryBean 된 ****상위 인터페이스들의 기능을 포함한 구현체가 프로그래밍된다. (@NoRepositoryBean = 빈생성 막음 →상속받으면 생성돼서 사용가능)
        • JpaRepository (마스터 셰프): 데이터 액세스를 위한 핵심 기능의 종합적인 요리책(기능) 을 제공합니다.
        • @NoRepositoryBean 인터페이스 (셰프): 각 인터페이스는 특정 데이터 액세스 방법을 제공하는 전문적인 기술 또는 레시피를 나타냅니다.
        • JpaRepository 상속: 마스터 셰프의 요리책과 셰프의 전문성을 얻습니다.
      • SpringDataJpa 에 의해 엔티티의 CRUD, 페이징, 정렬 기능 메소드들을 가진 빈이 등록된다. (상위 인터페이스들의 기능)

3️⃣ 테이블 객체 이해하기

✅ Raw JPA 테이블 매핑 기능

@Entity

  • 객체 관점에서의 이름
  • 디폴트로 클래스명으로 설정됨
  • 엔티티의 이름은 JQL에서 쓰임
    • JQL : Entity 명으로 쿼리짤때 쓰이는 언어 (ex. JPQL, QueryDsl)

@Table

  • RDB 의 테이블 이름
  • @Entity의 이름이 테이블의 기본값
    • 주로 Entity 이름과 다르게 Table 명을 지정하고 싶을때 아래와 같이 사용
  • 테이블의 이름은 SQL에서 쓰임

@Id

  • 엔티티의 주키를 맵핑할 때 사용
  • 자바의 모든 primitive 타입과 그 랩퍼 타입을 사용할 수 있음
    • Date랑 BigDecimal, BigInteger도 사용 가능
  • 복합키를 만드는 맵핑하는 방법도 있지만 그건 논외로..

@GeneratedValue

  • 주키의 생성 방법을 맵핑하는 애노테이션
  • 생성 전략과 생성기를 설정할 수 있다
    • 기본 전략은 AUTO: 사용하는 DB에 따라 적절한 전략 선택
    • TABLE, SEQUENCE, IDENTITY 중 하나

@Column

  • unique
  • nullable
  • length
  • columnDefinition
  • ...

@Temporal

  • 현재 JPA 2.1까지는 Date와 Calendar만 지원

@Transient

  • 컬럼으로 맵핑하고 싶지 않은 멤버 변수에 사용

✅Raw JPA 필드 타입 매핑 기능

기본 타입

@Column

  • String, Date, Boolean, 과 같은 타입들에 공통으로 사이즈 제한, 필드명 지정과 같이 옵션을 설정할 용도로 쓰인다.
  • Class 에 @Entity 가 붙어있으면 자동으로 필드들에 @Column 이 붙음

@Enumerated

  • Enum 매핑용도로 쓰이며 실무에서는 @Enumerated(EnumType.*STRING*) 으로 사용권장
  • Default 타입인 ORDINAL 은 0,1,2.. 값으로 들어가기 때문에 추후 순서가 바뀔 가능성있다.

Composite Value 타입

  • @Embeddable : 복합 값 객체로 사용할 클래스 지정
  • @Embedded : 복합 값 객체 적용할 필드 지정

4️⃣ 테이블 객체끼리 관계만들기

✅ Raw JPA 연관관계 매핑 기능

@OneToOne

  • 일대일 관계를 나타내는 매핑 정보
  • 1:1 관계를 지정하기에 앞서 이것이 꼭 물리적으로 테이블이 분리되어야 하는지에 대해 생각해 봐야 합니다.
  • 1:1 관계로 구성 한다는 것은 결국 하나의 목적에 부합되는 공통된 데이타를 관리한다고 볼 수 있으며 이것은 하나의 테이블에서 관리 할 수 있는 데이타일 가능성이 높다는 의미입니다.
  • 즉, 의도적 중복이 아니라면 사용할일이 없다는 말
    • 의도적 중복 예시) 버블 구독상품을 사서 채팅방이 생길경우. 구독상품과 채팅방은 1:1 관계

@OneToMany

  • 일대다 관계를 나타내는 매핑 정보
  • @OneToMany가 단방향으로 쓰이면 문제가 발생할 수 있다.
  • 속도를 위해 기본적으로 FetchType 설정이 LAZY 로 설정되어 있습니다.
  • 속성
    • mappedBy : 연관관계의 주인 필드를 선택한다.
    • fetch : 글로벌 페치 전략 설정
    • cascade : 영속성 전이 기능을 사용한다.
    • targetEntity : 연관된 엔티티의 타입 정보를 설정

@ManyToOne

  • 다대일 관계를 나타내는 매핑 정보
  • 속성
    • optional (default true) : false로 설정하면 연관된 엔티티가 반드시 있어야 함.
    • fetch : 글로벌 패치 전략 설정
      • ✋ 기본이 EAGAR 로 설정되어있으나 실무에서는 기본 LAZY로 설정하는것 추천!
    • cascade : 영속성 전이 기능 사용
    • targetEntity : 연관된 엔티티의 타입 정보 설정 (targetEntity = Member.class 식으로 사용)

@JoinColumn

  • 외래 키 매핑 시 사용 (Join 을 요청하기 위한 매핑정보로 쓰인다.)
  • @ManyToOne 어노테이션과 주로 함께 쓰인다. (조인대상 컬럼 지정기능을 안쓸거면 생략해도 됨)
  • name 속성은 매핑할 외래키의 이름
  • 어노테이션을 생략해도 외래 키가 생성됨.
    • 생략 시 외래키의 이름이 기본 전략을 활용하여 생성된다.
  • 속성
    • name : 매핑할 외래 키의 이름
    • referencedColumnName : 외래 키가 참조하는 대상 테이블의 컬럼명
    • foreignKey : 외래 키 제약조건 지정 (테이블 생성 시에만 적용됨)
    • unique/nullable/insertable/updateable/columnDefinition/table : @Column의 속성과 같음

2025.05.01 - [[내일배움캠프-Sparta]/Spring 6기 ( + TIL )] - JWT 인증/인가

 

TMI

감기 기운이 좀 있는것 같다 팀 프로젝트에 들어가기전에 건강 관리에 유의좀 해야 겠다. 내일 병원좀 들려야 겟다..

반응형

'[내일배움캠프-Sparta] > Spring 6기' 카테고리의 다른 글

🚧 트랜잭션 (스탠다드)  (0) 2025.05.02
🔒 JWT 인증/인가  (0) 2025.05.01
TIL 48 [ JPA 심화 ( 의존성, DB, SQL, Driver, JDBC ) ]  (0) 2025.04.30
TIL 47 [ Spring 심화 프로젝트 ( D-day ) - 발표/마무리, 트러블 슈팅 ]  (0) 2025.04.29
TIL 46 [ Spring 심화 프로젝트 ( Day 5 ) - 마무리 작업 ( ppt, 대본, ERD/API 명세서 리뉴얼) ]  (0) 2025.04.28
'[내일배움캠프-Sparta]/Spring 6기' 카테고리의 다른 글
  • 🚧 트랜잭션 (스탠다드)
  • 🔒 JWT 인증/인가
  • TIL 48 [ JPA 심화 ( 의존성, DB, SQL, Driver, JDBC ) ]
  • TIL 47 [ Spring 심화 프로젝트 ( D-day ) - 발표/마무리, 트러블 슈팅 ]
dimenshun
dimenshun
한 소년의 개발 일기
    반응형
  • dimenshun
    Dev Life Notes
    dimenshun
  • 전체
    오늘
    어제
    • 분류 전체보기 (268)
      • CS (23)
        • 자료구조 (0)
        • 알고리즘 (0)
        • 컴퓨터 구조 (8)
        • 네트워크 (6)
        • 운영체제 (3)
        • DB ( + SQLD ) (5)
        • SW공학 (1)
      • 프로그래밍 (3)
        • Java (0)
        • Spring (0)
        • HTML,CSS (3)
        • JavaScript (0)
      • 개발 툴 (7)
        • Git(버전관리) (1)
        • Docker (3)
        • AWS (2)
        • JSP (1)
      • 코딩테스트(Algorithm) (125)
        • 백준 (6)
        • 프로그래머스 (119)
      • [내일배움캠프-Sparta] (110)
        • Spring 6기 (106)
        • KPT 회고 (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    AWS
    OS
    KPT
    코딩테스트
    Java
    Python
    network
    세션
    컴퓨터구조
    spring
    개발자
    web
    메모리
    내일배움캠프
    Til
    백엔드
    배포
    네트워크
    docker
    웹
    SQLD
    Testcode
    알고리즘
    운영체제
    SQL
    CPU
    It
    cs
    db
    트랜잭션
  • hELLO· Designed By정상우.v4.10.3
dimenshun
TIL 49 [ JPA 심화 - ( MyBatis, ORM, 테이블 객체, Raw JPA 연관관계) ]
상단으로

티스토리툴바