*인프런 '김영한'님의 실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발 강의를 듣고 정리한 내용입니다.
1.프로젝트 설정
- 프로젝트 생성
- *3.0.5버전은 java 17에서 지원, java11 지원 안 함.
- 라이브러리 살펴보기
- hikariCP 커넥션 풀 → 찾아보기
- 라이브러리 추가(from. 강의자료)
- Validation (JSR-303 validation with Hibernate validator) 모듈을 꼭! 추가해주세요.
- build.gradle에 다음 코드 추가
- implementation 'org.springframework.boot:spring-boot-starter-validation'
- JUnit4 추가 (안 하면 JUnit5로 동작)
- testImplementation("org.junit.vintage:junit-vintage-engine") { exclude group: "org.hamcrest", module: "hamcrest-core" }
- 실행시 Gradle → IntelliJ IDEA로 변경(속도때문)
- Preferences Build, Execution, Deployment Build Tools Gradle Build and run using: Gradle → IntelliJ IDEA Run tests using: Gradle → IntelliJ IDEA
- html 파일을 컴파일만 해주면 서버 재시작 없이 View 파일 변경
- 인텔리J 컴파일 방법: 메뉴 build Recompile
implementation 'org.springframework.boot:spring-boot-devtools'
- Validation (JSR-303 validation with Hibernate validator) 모듈을 꼭! 추가해주세요.
- JPA와 DB 설정, 동작확인
- Member 엔티티 생성
- MemberRepository 생성
- 🤔@PersistenceContext 란?
- EntityManager를 빈으로 주입할 때 사용하는 어노테이션
- 🤔@PersistenceContext 란?
- MemberRepositoryTest생성
- @RunWith(SpringRunner.class) JUnit4에는 반드시 필요함!
- @Rollback(false) → 바로 commit → h2데이터베이스 바로 반영되어 확인가능
❗️TIP - 쿼리 파리미터 로그 남기기
implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.5.6'
2.도메인 분석 설계
- 회원기능
- 회원등록
- 회원조회
- 상품기능
- 상품등록
- 상품수정
- 상품조회
- 주문기능
- 상품주문
- 주문내역조회
- 주문 취소
- 추가
- 재고 관리
- 도서, 음반, 영화
- 배송정보
테이블 설계
- 가능하면 양방향 연관관계는 쓰지않는게 좋다. → 그러나 다양한 연관관계를 표현하기 위해 사용함
- 예를 들어, Member와 Order는 사이에 1:N 관계는 굳이 필요없다. 주문이 회원을 참조하는 것으로 충분함.
- 참고: 외래 키가 있는 곳을 연관관계의 주인으로 정해라.
- 일대다 관계라면 다쪽에 외래키가 존재하므로, 연관관계의 주인이 된다.
🤔 다대다 관계를 풀어내기
- Order - OrderItem - Item
- 다대다를 사용하지 않고 중간 테이블(ORDERITEM)을 사용하여 설계하신것으로 이해하시면 됩니다.
- ORDER -(1:N) - ORDERITEM - (N : 1) - ITEM
- 주문시 여러 상품 선택가능, 같은 상품도 여러 번 주문될 수 있음
- 주문과 상품(물품)의 관계 - 인프런 | 질문 & 답변
- Category - Category Item -Item
- 다대다를 일대다, 다대일로 풀어냄
🤔 일대다 양방향 관계
- FK를 가진 엔티티는 실제 값을 변경하거나 하는 용도로 사용
- 연관 관계의 주인의 반대쪽 필드는 mappedby를 설정하여 읽기 전용으로 쓰게 된다.
엔티티 클래스 개발
- Order 엔티티
- name = “member_id”는 FK이름을 의미
- @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id")
- Address(임베디드 타입)
- @Embedded @Embeddable 둘 중에 하나만 써도 되지만, 김영한님은 그냥 둘 다 쓰신다고 하심.
- 기본생성자란 기본 생성자는 어떠한 매개변수도 전달받지 않으며, 기본적으로 아무런 동작도 하지 않음.
-
@Embeddable @Getter public class Address { private String city; private String street; private String zipcode; protected Address() { } public Address(String city, String street, String zipcode) { this.city = city; this.street = street; this.zipcode = zipcode; } }
- 상속관계
- Item 엔티티(Movie, Album, Book)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "dtype")
🤔 1:1 관계
- Order - Delivery
- 주문에서 배송을 확인하는 경우가 배송에서 주문을 확인하는 경우가 많다.
- 따라서 Order엔티티에서 delivery FK를 갖는 것이 바람직함.
@OneToOne(cascade = CascadeType.ALL,fetch = FetchType.LAZY) @JoinColumn(name = "delivery_id") private Delivery delivery;
@OneToOne(mappedBy = "delivery",fetch = FetchType.LAZY) private Order order;
🤔M : N 관계
- ❗️실무에서 사용 안 함❗️
- Item - Category
- 실제로 다대다 관계가 불가능하기 때문에, 중간에 category_item이라는 테이블을 둔다.
- joinColumns과 inverseJoinColumns
@ManyToMany
@JoinTable(name = "category_item",joinColumns = @JoinColumn(name = "category_id"),
inverseJoinColumns = @JoinColumn(name = "item_id"))
private List<Item> items = new ArrayList<>();
@ManyToMany(mappedBy = "items")
private List<Category> categories = new ArrayList<>();
모든 연관관계는 지연로딩으로 설정!!
- 즉시로딩( EAGER )은 예측이 어렵고, 어떤 SQL이 실행될지 추적하기 어렵다. 특히 JPQL을 실행할 때 N+1 문제가 자주 발생한다.
- @XToOne(OneToOne, ManyToOne) 관계는 기본이 즉시로딩이므로 직접 지연로딩으로 설정해야 한다.
CascadeType
cascade = CascadeType.*ALL 은* 상위 엔터티에서 하위 엔터티로 모든 작업을 전파한다는 의미이다.
따라서 아래코드에서 order가 저장될 때 orderItems도 한꺼번에 저장된다.(persist(order)만 수행)
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<>();
🧐 더 알아보기
JPA의 엔티티는 왜 기본 생성자가 있어야 할까요?
JPA의 엔티티에 protected, public 기본 생성자가 필요한 이유
JPA의 엔티티에 protected, public 기본 생성자가 필요한 이유
JPA는 엔티티에 기본 생성자, 즉 아무런 매개변수를 받지 않는 생성자를 만드는 것을 강제하고 있습니다. JPA 구현체마다 스펙이 조금 달라서 기본 생성자를 만들지 않아도 정상적으로 작동하는
velog.io
Spring Data JPA 에서 Entity에 기본 생성자가 필요한 이유는 동적으로 객체 생성 시 Reflection API 를 활용하기 때문이다.
JPA는 DB 값을 객체 필드에 주입할 때 기본 생성자로 객체를 생성한 후 Reflection API를 사용하여 값을 매핑한다.
때문에 기본 생성자가 없다면 Reflection은 해당 객체를 생성 할 수 없기 때문에 JPA의 Entity에는 기본 생성자가 필요하다.
- 그렇다면 왜 모든 엔티티에 기본생성자를 만드지 않은걸까?
- 기본 생성자에 관해 질문드립니다. - 인프런 | 질문 & 답변
기본 생성자에 관해 질문드립니다. - 인프런 | 질문 & 답변
13:43 부분을 보면 JPA 는 내부적으로 리플렉션을 통해서 객체를 생성하기 때문에 기본 생성자가 필요하다고 하셨는데요.다음과 같이 기본 생성자는 생성하지 않고 코드를 돌리더라도문제없이 돌
www.inflearn.com
'백엔드 > SpringBoot' 카테고리의 다른 글
스프링 AOP와 트랜잭션 (0) | 2025.02.12 |
---|---|
멀티스레드 환경에서의 트랜잭션 동작 - feat. 테스트 코드 (0) | 2025.02.10 |
② 실전! 스프링 부트와 JPA 활용1 (1) | 2023.11.14 |
② JPA 공부 By 실전! 스프링 데이터 JPA (0) | 2023.03.14 |
① JPA 공부 By 실전! 스프링 데이터 JPA (0) | 2023.03.10 |