- 시작하게 된 계기 및 다짐 😮
이번 코드스테이츠의 백엔드 엔지니어링 개발자 부트캠프
에 참여하게 되면서 현직개발자 분들의 빠른 성장을 위한 조언 중 자신만의 블로그를 이용하여 배운 것 들을 정리하는게 많은 도움이 된다 하여 시작하게 되었다.
- 학습 목표 😮
목표 | 결과 |
---|---|
- JPA 엔티티에 대한 매핑을 할 수 있다. | O |
JPA 기반의 엔티티 간 연관 관계를 매핑 | O |
JPA가 무엇인지 이해 및 동작 원리 | O |
Spring Data JDBC를 이용해서 데이터의 저장, 수정, 조회, 삭제 작업 | O |
Spring Data JDBC 기반의 엔티티 연관 관계를 매핑 | O |
- 정리 😮
0. @애너테이션
@Entity
@GeneratedValue
1. JPA란?
2. 데이터 엑세스 계층에서의 JPA위치
3. 영속성 컨텍스트 _ JPA에서의 P의 의미
####. 1. 영속성 컨텍스트(Persistence Context) [사진]
- ORM은 객체와 테이블의 매핑을 통해 엔티티 클래스 객체 안에 포함된 정보를 테이블에 저장하는 기술
- 이때, 매핑된 엔티티 객체 정보를 이 영속성 컨텍스트 안에 보관하여 오래 머물도록 해준다.
- '1차 캐시'와 '쓰기 지연 SQL저장소'라는 영역이 있다.
- 저장시, 먼저 1차 캐시에 엔티티 정보가 저장됨
[JPA API 사용 설정 기반 코드
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // (1)
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
(1) 기본적인 Spring Data JPA 기술 사용
}
[JPA 설정(application.yml)]
spring:
h2:
console:
enabled: true
path: /h2
datasource:
url: jdbc:h2:mem:test
jpa:
hibernate:
ddl-auto: create # (1) 스키마 자동 생성
show-sql: true # (2) SQL 쿼리 출력
(1) 엔티티 클래스를 정의하고 실행 시, 매핑되는 테이블을 DB에 자동으로 생성해준다.
- 기존 JDBC에서는 스키마를 직접 지정했지만 JPA가 자동으로 DB에 테이블 생성
(2) JPA API를 통해 실행되는 SQL 쿼리를 로그로 출력해준다.
4. 영속성 컨텍스트 사용 Code
CommandLineRunner
영속성 컨텍스트에 엔티티 저장
1). 예제 Code 분석
(1) EntityManagerFactory의 createEntityManager()를 통해서 DI를 받아 객체를 얻을 수 있음
- 이 EntitiyManger 클래스 객체를 통해 JPA API 메서드를 이용할 수 있음
(3) em.persist(member)코드를 통해 member엔티티를 영속성 콘텍스트에 등록한다.
- EntityManger클래스 객체를 통해 Member등의 엔티티를 영속성 컨텍스트에 저장할 수 있다.
- em.persist(member)코드를 실행하고나서, '1차캐시'엔 'Member1' 그리고 '쓰기 지연 SQL저장소'에는 'INSERT...(member1)' 의 쿼리문이 등록되지만, 실제 테이블에 회원 정보를 등록하지는 않는다.
(4) em.find(조회 할 엔티티 클래스 타입, 조회 할 엔티티 클래스 식별자 값)
[예제 Code]
@Cofiguration
public class {
private EntityManager em;
private EntityTransaction tx;
@Bean
public CommandLineRunner testJpaBasicRunner(EntityManagerFactory emFactory) { // (1)
this.em = emFactory.createEntityManager(); // (2)
return args -> {
Member member = new Member("hgd@gmail.com");
// (3)
em.persist(member);
// (4)
Member resultMember = em.find(Member.class, 1L);
System.out.println("Id: " + resultMember.getMemberId() + ", email: " +
resultMember.getEmail());
};
}
}
영속성 컨텍스트와 테이블에 엔티티 저장
1). 예제 Code 분석
(1) EntityManager 객체로부터 Transaction객체를 얻어 이 객체로 DB테이블에 데이터를 저장
(2) Transcation을 시작하기 위해 tx.begin()을 호출필요
(4) tx.commit()을 호출하는 시점에서 영속성 컨텍스트에 저장되어 있는 객체를 DB 테이블에 저장한다.
- 이 시점에서 '쓰기 지연 SQL 저장소'의 SQL문이 사용되어 사라짐
(6) member2의 경우, 영속성 컨텍스트에 없으면 직접 SELECT 쿼리문을 요청해 테이블에 조회를 한번더 한다.
[Hibernate ORM 구현체가]
★ 즉, 1차로 영속성 컨텍스트에 조회하고 없을시 SELECT 쿼리문을 통해 테이블에 직접 조회한다.
[예제 Code]
@Cofiguration
public class {
private EntityManager em;
private EntityTransaction tx;
@Bean
public CommandLineRunner testJpaBasicRunner(EntityManagerFactory emFactory) {
this.em = emFactory.createEntityManager();
// (1)
this.tx = em.getTransaction();
return args -> {
tx.begin(); // (2)
Member member = new Member("hgd@gmail.com");
em.persist(member); // (3)
tx.commit(); // (4)
// (5)
Member resultMember1 = em.find(Member.class, 1L);
System.out.println("Id: " + resultMember1.getMemberId() + ", email: " +
resultMember1.getEmail());
// (6)
Member resultMember2 = em.find(Member.class, 2L);
// (7)
System.out.println(resultMember2 == null);
};
}
}
쓰기 지연을 통한 영속성 컨텍스트와 테이블에 엔티티 일괄저장
1). 테이블 엔티티 업데이트 과정
(1). Transaction을 시작한다는 tx.begin() 이후, update하고자하는 값을 em.find(클래스,식별자 값)을 통해서 찾음
(2). 해당 값을 setter메서드를 통해 값을 변경 시킨후, tx.commit()을 하면 완료가 됨 ( 따로 업데이트 해주는 메서드는 없음)
(3). 영속성 컨텍스트에 저장되는 엔티티의 경우, 저장 되는 시점의 상태를 가지고 스냅샷을 생성하는데, 이 때 setter메서드를 통해 값이 변경되면 tm.commit()시기에 이 스냅샷과 변경된 엔티티를 비교하여 '쓰기 지연 SQL 저장소'에 UPDATE 쿼리문을 생성하고 이를 실행한다.
2). 테이블 엔티티 삭제 과정
1). tx.begin()이후, 삭제하고 하는 엔티티를 em.find()메서드를 통해 찾은 후, em.remove(엔티티)를 통해 해당 엔티티를 삭제한다.
2). 이후, tx.commit()을 통해 테이블에 이 삭제된 데이터 정보를 등록
3). 위와 마찬가지로, em.remove()로 1차 캐시의 엔티티가 제거되고 이 후, 쓰기 지연 SQL저장소에 등록된 DELETE 쿼리가 실행된다.
★ tx.commit() 메서드가 호출되면, JPA 내부적으로 em.flush() 메서드가 호출되어 영속성 컨텍스트의 변경 내용을 DB에 반영한다.
Extra
0. @애너테이션
1. 엔티티 테이블 간의 매핑
2. 기본키 매핑
기본키 생성 전략
1). 기본키 직접 할당
- 애플리케이션 코드 상에서 기본키를 직접 할당
- @Id 애너테이션을 사용하여 기본키를 직접 할당
- 기본키 필드값을 받는 생성자를 생성
2). 기본키 자동 생성
0). Repository.save() == persist 시점
1). IDENTITY
- 기본키 생성을 DB에 위임, MySQL 방식의 AUTO_INCREMENT 기능을 통해 자동 숫자 증가를 기본키
- @Id + @GeneratedValue 애너테이션과 애트리뷰트의 값(strategy= GenerationType.IDENTITY) 지정
- 생성자가 없는 기본 생성자로 생성하면, DB가 자동생성
★다른 기본키 생성과 달리, 영속성 컨텍스트에 등록을 하기 위해 기본키가 필요하여, em.persist()메서드 호출시에 flush => Insert문이 실행이 되어 테이블에 데이터를 저장 후 기본키가 생성된 후 영속성 컨텍스트에 저장
즉, Transcation을 지원하는 쓰기 지연 방식이 동작하지 않는다.
★ em.find()를 찾기전에, tx.commit이 필요
2). SEQUENCE
- DB에서 제공하는 시퀸스를 사용해 기본키 생성 전략
- @Id + @GeneratedValue 애너테이션과 애트리뷰트의 값(strategy= GenerationType.SEQUENCE) 지정
★ em.persist() 메서드 호출시, DB Sequence를 조회 후 조회한 식별자를 entity에 할당하여 영속성 컨텍스트에 저장한다. 이 후, commit 하여 flush가 발생하면 entity를 DB에 저장
★ em.find() 이후 commit해도 돌아감
3). AUTO
- GnerationType.AUTO를 지정하면, JPA가 DB의 Dialect에 따라서 적절한 전략을 자동으로 선택
0). TABLE
- 별도의 키 생성 테이블을 사용하는 전략
- 성능면에서 좋은 선택은 아니라, 사용하지 않음 보통
3. 필드와 컬럼간의 매핑
@애너테이션
1). @Column(nullable=false[true], updatable = false[true], unique = true[false])
- 애트리뷰트 - [length(최대 길이),name(column이름), )
- 필드와 컬럼을 매핑해주는 애너테이션으로, 붙이지 않아도 필드는 모두 컬럼으로 매핑하고 디폴트값으로 적용됨
2). @Transient
- 해당 필드를 테이블의 컬럼과 매핑하지 않겠다는 의미이다.
- 따라서, DB에 저장도 하지않고, 조회시에도 매핑하지 않는다
- 임시 데이터를 메모리에 저장하기 위한 용도
3). @@Enumerated
- enum 타입과 매핑할 때 사용하는 애너테이션으로,
1). EnumType.ORDINAL : enum의 순서를 나타내는 숫자를 테이블에 저장
2). EnumType.STRING : enum의 이름을 테이블에 저장
엔티티 클래스에서 발생한 예외처리
0. @애너테이션
@ManyToOne
@JoinColumn
@OneToMany(mappedBy = "member", cascade = CascadeType.PERSIST)
★ 외래키의 소유자측이 보통 사용
1. 연간 관계 매핑 방식
단방향 연관 관계
양방향 연관 관계
일대다 단방향 연관 관계
다대일 연관 관계
다대다 연관 관계
**2. 엔티티 간의 연관 관계 매핑 권장 방법
Extra
1. Spring Data JPA란
각 클래스 Repository에서 JpaRepository를 상속하여 JPA에 특화된 더많은 기능들을 사용
1). JPQL을 통한 객체 지향 쿼리
2). 네이티브 SQL을 통한 조회
3). JPA를 통한 장점
Extra
7월7일 강의 뒷부분 open-in-view 확인
Hibernate 옵션
1). ddl-auto:
- 피드백 😮
Spring Data JPA란, JPA라는 ORM기반 기술의 표준 사양을 구현한 구현체인 Hibernate ORM을 좀더 쉽게 사용할 수 있게 해주는 모듈 이다.
내부적으로 Spring Data JDBC를 사용하고, Jpa Repository를 상속하여 리포지토리를 인터페이스로 생성
- 앞으로 해야 될 것 😮