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에는 아무 동작이 전송되지 않을 수 있다.
- 즉, 여러가지 동작이 많이 발생하더라도 쿼리는 트랜잭션당 최적화 되어 최소쿼리만 날라가게된다.
- why? 단일 쿼리로 수행함으로써 외부 트랜잭션에 의한 중복키 생성을 방지하여 단일키를 보장
✅ ORM을 사용하기 가장 쉬운 방법 : JpaRepository
💁♂️ Repository vs JpaRepository
- 기존 Repository
- @Repository 을 클래스에 붙인다.
- @Component 어노테이션을 포함하고 있어서 앱 실행시 생성 후 Bean으로 등록된다.
- 앞서배운 Repository 기본 기능만 가진 구현체가 생성된다. (DB별 예외처리 등)
- 새로운 JpaRepository
- JpaRepository<Entity,ID> 인터페이스를 인터페이스에 extends 붙인다.
- @NoRepositoryBean 된 ****상위 인터페이스들의 기능을 포함한 구현체가 프로그래밍된다. (@NoRepositoryBean = 빈생성 막음 →상속받으면 생성돼서 사용가능)
- JpaRepository (마스터 셰프): 데이터 액세스를 위한 핵심 기능의 종합적인 요리책(기능) 을 제공합니다.
- @NoRepositoryBean 인터페이스 (셰프): 각 인터페이스는 특정 데이터 액세스 방법을 제공하는 전문적인 기술 또는 레시피를 나타냅니다.
- JpaRepository 상속: 마스터 셰프의 요리책과 셰프의 전문성을 얻습니다.
- SpringDataJpa 에 의해 엔티티의 CRUD, 페이징, 정렬 기능 메소드들을 가진 빈이 등록된다. (상위 인터페이스들의 기능)
- @NoRepositoryBean 된 ****상위 인터페이스들의 기능을 포함한 구현체가 프로그래밍된다. (@NoRepositoryBean = 빈생성 막음 →상속받으면 생성돼서 사용가능)
- JpaRepository<Entity,ID> 인터페이스를 인터페이스에 extends 붙인다.
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 |