05 JPA 흐름
05 JPA 흐름
1. Note
- 엔티티의 생명주기(생성·조회·삭제 등)는 Repository가 책임
- DB와의 직접적인 연결, 영속성 컨텍스트 등록/삭제 등
- 엔티티의 상태 변화(필드 변경, 연관관계 조작 등)는 엔티티 자체가 책임
- 이미 영속 상태인 객체를 조작하면 트랜잭션 종료 시점에 자동으로 DB에 반영됨
- FK를 통한 연관 데이터 접근도 마찬가지
- 처음부터 Repository에서 조건을 걸어 조회할 수도 있지만
- 이미 영속성 컨텍스트에 적재된 엔티티에서 접근하는 것이 효율적임 (객체 그래프 탐색)
2. Product
1. Product Entity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Entity
@Table(name = "products")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int price;
private int stock;
// 비즈니스 로직
// 엔티티 내부를 수정할 때 사용하는 것
public void update(String name, int price, int stock) {
this.name = name;
this.price = price;
this.stock = stock;
}
}
2. Product Repository
1
2
// 레파지토리에 대한 접근과 관련된 부분
public interface ProductRepository extends JpaRepository<Product, Long> { }
3. Product Service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Service
@RequiredArgsConstructor
@Transactional
public class ProductService {
// 레파지토리
private final ProductRepository productRepository;
// 제품 추가
public Long createProduct(String name, int price, int stock) {
// 객체를 만들고
Product product = Product.builder()
.name(name)
.price(price)
.stock(stock)
.build();
// 레파지토리에 save
// productRepository.save(product) 리턴값은 product 엔티티
// 따라서 생성한 엔티티의 ID(PK값)
return productRepository.save(product).getId();
}
@Transactional(readOnly = true)
public Product getProduct(Long id) {
return productRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("상품 없음"));
}
public void updateProduct(Long id, String name, int price, int stock) {
// 바로 위에 퍼블릭 메소드에서 레파지토리에 접근해서 엔티티를 얻음.
// 레파지토리에서 얻은 엔티티는 영속성등록이 되어있는 상태,
// 따라서 이 값을 수정하면 트랜잭션이 끝나면 자동으로 엔티티가 관리됨.
Product product = getProduct(id);
product.update(name, price, stock);
}
public void deleteProduct(Long id) {
//생명주기와 관련된 부분은 레포지토리 직접 접근
productRepository.deleteById(id);
}
}
3. Product - ProductOption
1. Product/ProductOption Entity
1. 구조
1
2
3
4
5
6
7
8
products
- id
- name
product_options
- id
- name
- product_id (FK)
2. Product(부모)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Product {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "product", cascade = CascadeType.ALL)
private List<ProductOption> options = new ArrayList<>();
// 연관관계 편의 메서드
public void addOption(ProductOption option) {
// 파라미터 옵션에 추가를 해서 엔티티매니저가 관리하게 된거고
// 이 방법은 레포지토리매니저에서 엔티티에 접근하는게 아닌, 프로덕트를 통해서 접근
options.add(option);
// 여기서 부모 엔티티 설정을 함.
// 부모엔티티 설정을 하지 않으면, 그냥 옵션 테이블에 등록된 엔티티가됨.
option.setProduct(this);
}
}
3. ProductOption (자식)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ProductOption {
@Id @GeneratedValue
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "product_id")
private Product product;
public void setProduct(Product product) {
this.product = product;
}
}
3. Product Service - Create() Method
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Transactional
public void create() {
// 새로운 객체를 만들고
Product product = new Product("아이폰");
//옵션에 2종류를 추가함.
ProductOption op1 = new ProductOption("128GB");
ProductOption op2 = new ProductOption("256GB");
// 연관관계 메서드
// 옵션을 2가지 추가함.
product.addOption(op1);
product.addOption(op2);
// 레파지토리에 저장
// Product 테이블에 “아이폰” 1건 저장
👉 // Product를 FK로 참조하는 ProductOption 2건이 추가
productRepository.save(product);
}
4. Product Service - getProductOptions Method
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Transactional(readOnly = true)
public void getProductOptions(Long productId) {
// 상품 엔티티를 찾고
Product product = productRepository.findById(productId).orElseThrow();
// 연관관계로 옵션 조회
List<ProductOption> options = product.getOptions();
// 사용
for (ProductOption option : options) {
System.out.println(option.getName());
}
}
This post is licensed under CC BY 4.0 by the author.