-
[JPA] 간단하게 OneToMany 데이터 API 구현하기 - @Embeddable, @Embedded, @ElementCollection 활용하기Spring Boot/JPA 2023. 10. 15. 23:14
요구사항
- 레시피를 저장하고 조회, 삭제할 수 있는 API를 구성한다.
- JPA 연관관계 설정 없이 OneToMany 데이터 모델링 방법을 정리한다.
API Spec
- 레시피 저장 api
POST localhost:8881/api/recipe/new # requestBody { "name": "Fresh Mint Tea", "description": "Light, aromatic and refreshing beverage, ...", "ingredients": ["1 inch ginger root, minced", "1/2 lemon, juiced", "1/2 teaspoon manuka honey"], "directions": ["Place all ingredients in a mug and fill with warm water (not too hot so you keep the beneficial honey compounds in tact)", "Steep for 5-10 minutes", "Drink and enjoy"] } 200 OK # response { "id": 1 }
- 레시피 조회 api
GET localhost:8881/api/recipe/1 200 OK # response { "name": "Fresh Mint Tea", "description": "Light, aromatic and refreshing beverage, ...", "ingredients": [ "1 inch ginger root, minced", "1/2 lemon, juiced", "1/2 teaspoon manuka honey" ], "directions": [ "Place all ingredients in a mug and fill with warm water (not too hot so you keep the beneficial honey compounds in tact)", "Steep for 5-10 minutes", "Drink and enjoy" ] }
처음 요구사항을 보았을때, 기계적으로 아래처럼 생각했다.
1. Recipe, Ingredient, Direction Entity 클래스를 만들고,
2. OneToMany로 Recipe 필드에 List<Ingredient>, List<Direction> 필드 연관관계 맺기3. 단방향/양방향 매핑을 고민
4. Cascade나 한꺼번에 저장, 삭제를 신경쓴다.
API는 굉장히 단순한데 이렇게까지 복잡한 과정을 거쳐야할까?
@Embeddable, @Embedded, @ElementCollection이라는 굉장히 편한 도구가 있다.
- 효과적으로 Persist data를 다루기 위해 등장
- 코드를 더 깔끔하게, Data Persistence에 모듈화된 접근 방식 제공
- Complex data structure를 개발자가 더 쉽게 다룰 수 있게 돕는다.
@Embeddable, @Embedded
- Spring Data가 부모 Entity에 자식 Entity를 Embeded하게 할 수 있다.
- @Embeddable을 자식 Entity에 선언하고 @Embedded를 부모 Entity에 선언하면 자식 클래스가 부모의 부분으로 동작하게 할 수 있다.
아래 위치를 표현하는 Entity가 있고 위도와 경도를 추상화하여 따로 분리하고 싶다고 가정해보자.
@Entity public class Location { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String longitude; private String latitude; // getters and setters }
public class Coordinates { private String longitude; private String latitude; // getters and setters }
@Embeddable, @Embedded을 사용하면 아래처럼 구현 및 관리할 수 있다.
@Embeddable public class Coordinates { private String longitude; private String latitude; // getters and setters } @Entity public class Location { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @Embedded private Coordinates coordinates; // getters and setters }
- Spring Data를 사용하면 Hibernate가 JPA를 구현하기 때문에, @Embeddable, @Embedded 중에 하나만 선언하면 다른 한쪽은 생략가능하다.
- 애플리케이션 단에는 Object 2개로 관리되지만 DB에 Table은 하나이다.
@ElementCollection
- 종속되는 컬렉션을 정의하는 방법
- OneToMany 컬렉션을 처리하기 위한 별도 엔티티를 만들고, 관계를 구성하지 않아도 된다.
- @ElementCollection 선언만으로 OneToMany 관계 생성이 가능하다.
- 물론 별도의 클래스를 생성해서 Collection으로 관리되도록 하는 방식도 지원한다.
- The @ElementCollection can be used with standard library types like String, Integer or with embeddable objects annotated as @Embeddable
@Embeddable public class Rating { private String username; private int stars; // getters and setters } @Entity public class Location { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @Embedded private Coordinates coordinates; @ElementCollection private Set<String> tags; @ElementCollection private Set<Rating> ratings; // getters and setters }
API 구현
- 위에 요구했던 API 구현에 @ElementCollection을 이용하면 따로 클래스를 생성하고 연관관계를 맺는 과정들을 생략할 수 있다.
@Entity @Getter @NoArgsConstructor public class Recipe { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) private Long id; private String name; private String description; @ElementCollection // 기본 fetch 전략은 Lazy loading private List<String> ingredients; @ElementCollection // 기본 fetch 전략은 Lazy loading private List<String> directions; }
아래와 같이 테이블 생성됨
반응형'Spring Boot > JPA' 카테고리의 다른 글
QueryDsl projections 자바 Record에 적용하기 (1) 2024.04.23 JPA 주의점 (1) - OSIV false 설정 (open-in-view) (0) 2024.04.19 [JPA] Repository단에 Transactional을 선언하는 이유 (0) 2023.10.05 JPA 애플리케이션 데이터베이스 초기화 (0) 2023.09.17 Spring boot JPA/하이버네이트? (0) 2022.01.04