목차
개요
JDBC를 더 쉽게 사용하기 위한 JDBC Template은 Query의 결과를 객체로 변환해주는 Mapper를 사용할 수 있습니다. 이 Mapper는 DB 의존성 및 중복 쿼리 문제점이 있습니다. 또한 JDBC Template의 다음과 같은 문제점이 있습니다.
문제 | 설명 | 해결 방법 |
상속의 문제 | RDB는 객체의 상속 관계를 표현할 수 없습니다. | @OneToMany, @ManyToOne |
관계 문제 | RDB는 객체의 참조 관계 방향을 표현할 수 없습니다. | @JoinColumn, @MappedBy |
탐색 문제 | RDB는 객체처럼 참조를 따라 순차적으로 탐색할 수 없습니다. | @FetchType, fetchJoin() |
밀도 문제 | RDB는 객체처럼 복잡하고 큰 구조의 맴버 객체를 필드에 직접 가질 수 없습니다. | @embedded |
식별성 문제 | RDB는 객체처럼 hashCode나 주솟값이 아닌 오직 기본키(PK)로만 데이터를 식별합니다. | @Id, @GeneratedValue |
💡이러한 문제점을 해결하기 위해 JPA가 등장하였습니다.
JPA(Java Persistence API)
정의
자바에서 ORM(Object-Relational Mapping)을 표준화한 인터페이스(명세)입니다. 즉, 자바 객체와 데이터베이스 테이블 간의 매핑을 쉽게 하기 위한 표준 ORM API입니다.
ORM(Object-Relational Mapping)
객체지향 프로그래밍 언어의 객체와 관계형 데이터베이스의 테이블을 매핑(mapping)해주는 기술입니다. 쉽게 말해서 자바같은 프로그래밍 언어로 DB를 직접 다루게 해주는 도구입니다.
주요 목적
- SQL을 직접 쓰지 않고도 DB 접근
- 객체 중심 프로그래밍으로 DB 제어
- 영속성 컨텍스트, 더티 체킹, 1차 캐시 등 고급 기능 자동 처리
- 다양한 구현체에 대한 표준화된 추상 인터페이스 제공
핵심 개념
1. Entity
2. Field
3. Persistence Context(영속성 컨텍스트)
4. Repository
Entity
정의
데이터베이스 테이블과 매핑되는 자바 클래스입니다. 각각의 객체 인스턴스는 테이블의 한 행(Row)을 의미합니다.
예시
(테이블)
id | name | age |
1 | Alice | 23 |
2 | Bob | 30 |
- 각 컬럼(Column)은 자바 클래스의 Field 입니다.
(클래스)
import jakarta.persistence.*;
@Entity
@Table(name = "user")
public class User {
@Id // 기본키 지정
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int age;
// 기본 생성자 필수
public User() {}
// 생성자, getter, setter 등 생략 가능
}
- 이 클래스는 `user` 테이블과 연결되고, 객체 하나가 테이블의 한 줄을 나타냅니다.
- 각 Field는 테이블의 컬럼(Column)을 의미합니다.
구성 요소
구성 요소 | 설명 |
`@Entity` | 이 클래스가 엔티티임을 명시합니다. |
`@Table` | 매핑할 테이블 이름 지정 (생략 가능) |
`@Id` | 기본 키 (Primary Key) 지정 |
`@GeneratedValue` | 기본 키 생성 방법을 지정 |
`@Column` | (선택) 컬럼 세부 정보 지정, 클래스에 `@Entity`가 붙어있으면 자동으로 필드에 붙습니다. |
`@Enumerated` | Enum 매핑 용도로 사용. `@Enumerated(EnumType.STRING) 권장 |
`@Embeddable` | 복합 값 객체로 사용할 클래스 지정 |
`@Embedded` | 복합 값 객체 적용할 필드 지정 |
`@AttributeOverrides` | 복합 값 객체 여러개 지정 |
`@AttributeOverride` | 복합 값 객체 필드명 선언 |
`@Transient` | 컬럼으로 맵핑하고 싶지 않은 맴버 변수에 지정 |
⭐ 엔티티를 지정해주는 이유
엔티티는 단순한 데이터 구조체가 아닙니다. JPA 같은 ORM 프레임워크가 엔티티를 통해 다음을 해줍니다.
- SQL 없이 DB에 저장, 수정, 조회, 삭제하기 위해
- 1차 캐시, 더티 체킹, 연관관계 관리 등 고급 기능을 사용하기 위해
- 트랜잭션과 영속성 컨텍스트와 연결하기 위해
Persistence Context(영속성 컨텍스트)
정의
Entity 객체들을 보관하고 관리하는 JPA 내부의 메모리(캐시) 공간입니다.
역할
기능 | 설명 |
1차 캐시 | 동일한 엔티티를 또 조회하면 DB를 조회하지 않고 캐시에서 반환합니다. |
변경 감지(Dirty Checking) | 트랜잭션 내에서 값 변경을 추적하고, 자동으로 UPDATE 쿼리를 생성합니다. |
쓰기 지연(Write Behind) | INSERT, UPDATE, DELETE가 즉시 DB에 반영되지 않고 트랜잭션 종료 시 일괄 반영됩니다. |
고유성 보장 | 같은 엔티티(PK 기준)는 항상 동일한 객체로 관리됩니다. |
지연 로딩 지원 | 연관된 엔티티를 실제로 필요할 때 로딩 |
목적
1. 성능 최적화
2. 객체 중심 DB 작업을 지원합니다.
영속성
- 데이터를 생성한 프로그램이 종료되어도 사라지지 않는 데이터의 특성을 의미합니다.
- 영속성을 갖지 않으면 데이터는 메모리에서만 존재하게 되고 프로그램이 종료되면 해당 데이터는 모두 사라지게 됩니다.
- 그래서 데이터를 파일이나 DB에 영구 저장함으로써 데이터에 영속성을 부여합니다.
데이터 연동 구조
- 프로그램과 DB 사이에 `영속 컨텍스트`가 추가되었습니다.
- 프로그램과 영속 컨텍스트 사이는 `EntityManager`를 통해 관리됩니다.
- `EntityManager`가 객체의 영속성 상태(4가지)를 변경해줍니다.
영속성 4가지 상태
상태 | 설명 |
비영속(new/transient) | 엔티티 객체가 만들어져서 아직 저장되지 않은 상태로, 영속성 컨텍스트와 전혀 관계 없는 상태 |
영속(managed) | 엔티티가 영속성 컨텍스트에 저장되어, 영속성 컨텍스트가 관리할 수 있는 상태 |
준영속(detached) | 엔티티가 영속성 컨텍스트에 저장되어 있다가 분리된 상태로, 영속성 컨텍스트가 더 이상 관리하지 않는 상태 |
삭제(removed) | 엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제하겠다고 표시한 상태 |
- `Entity Manager`를 통해서 객체의 영속성 상태를 변경합니다.
예제 코드
Item item = new Item(); // 1
item.setItemNm("테스트 상품");
EntityManager em = entityManagerFactory.createEntityManager(); // 2
EntityTransaction transaction = em.getTransaction(); // 3
transaction.begin();
em.persist(item); // 4
em.flush(item). // 4-1 (DB에 SQL 보내기/commit시 자동수행되어 생략 가능함)
transaction.commit(); // 5
em.close(); // 6
1️⃣ 영속성 컨텍스트에 담을 상품 엔티티 생성
2️⃣ 엔티티 매니저 팩토리로부터 엔티티 매니저를 생성
3️⃣ 데이터 변경 시 무결성을 위해 트랜잭션 시작
4️⃣ 영속성 컨텍스트에 저장된 상태, 아직 DB에 INSERT SQL 보내기 전
5️⃣ 트랜잭션을 DB에 반영, 이 때 실제로 INSERT SQL 커밋 수행
6️⃣ 엔티티 매니저와 엔티티 매니저 팩토리 자원을 close() 호출로 반환
Repository
1. Repository
- `@Repository` : ORM을 사용하고자하는 클래스에 붙입니다.
- `@Component`를 포함하고 있습니다. 앱 실행 시 생성 후 Bean으로 자동 등록됩니다.
- Repository 기본 기능만 가진 구현체가 생성됩니다. (DB별 예외 처리 등)
- 직접 `Entity Manager`를 사용하여, 영속성 컨텍스트를 사용해야 합니다.
Repository 간단 구현
// UserRepository.java
@Repository
public class UserRepository {
@PersistenceContext
EntityManager entityManager;
public User insertUser(User user) {
entityManager.persist(user);
return user;
}
public User selectUser(Long id) {
return entityManager.find(User.class, id);
}
}
- EntityManager 맴버 변수를 직접적을 사용합니다.
2. JpaRepository
- `JpaRepository<Entity, ID 타입>` : ORM을 사용하고자하는 인터페이스에 상속시킵니다.
- `@NoRepositoryBean`을 포함하고 있습니다. 상위 인터페이스들의 기능을 포함한 구현체가 프로그래밍됩니다.
- Bean으로 생성되는 것을 막아줍니다. 이를 상속받아야만 생성되어 사용 가능해집니다. `JpaRepository<Entity, ID 타입>`을 상속받아야 하는 이유입니다.
- `SpringDataJpa`에 의해 Entity의 CRUD, 페이징, 정렬 기능 메소드들을 가진 빈이 등록됩니다. (상위 인터페이스들의 기능)
- `@NoRepositoryBean`을 포함하고 있습니다. 상위 인터페이스들의 기능을 포함한 구현체가 프로그래밍됩니다.
- Spring Data JPA의 `JpaRepository`는 자동 구현체를 제공합니다.
- `EntityManager`를 직접 선언하거나 조작하지 않아도, Spring Data JPA가 자동으로 `SimpleJpaRepository`라는 구현체를 만들어줍니다. 그 클래스 내부적으로 `EntityManager`를 사용하도록 구현되어있습니다.
JpaRepository 간단 구현
// UserRepository.java
public interface UserRepository extends JpaRepository<User, Long> {
// 기본 메서드는 자동으로 만들어짐
}
- EntityMananger 맴버변수를 간접적으로 사용합니다.
- CRUD, 페이징, 정렬 기능 메소드들을 자동으로 사용할 수 있게 됩니다.
💡`JpaRepository`를 상속받은 Repository는 다음과 같은 기능을 제공합니다.
- 직접 영속성 컨텍스트를 사용하지 않습니다.
- CRUD, 페이징, 정렬 등 다양한 기능을 직접 Query를 날리지 않아도 사용할 수 있습니다.
한계점
- 복잡한 쿼리는 결국 `@Query`나 `QueryDSL로 작성해야 합니다.
- 성능 튜닝이 어려운 경우도 있습니다. (특히 N+1, 지연로딩)
- 완전한 DB 독립은 불가능합니다. (JPQL도 결국 DB마다 약간의 차이가 있습니다.)
'MySql' 카테고리의 다른 글
[JDBC] JDBC Template (0) | 2025.06.25 |
---|---|
[JDBC] Query 요청을 위한 Statement vs PreparedStatement (0) | 2025.06.25 |
[JDBC] JDBC Driver 한 방 정리 (0) | 2025.06.24 |
[내일 배움 캠프, SQL 달리기 반] Lv5. 예산이 가장 큰 프로젝트는? (0) | 2025.03.27 |
[내일 배움 캠프, 달리기 반] Lv5. 가장 많이 팔린 품목은? (0) | 2025.03.26 |