-
JPA 연관관계 매핑spring boot 2023. 1. 12. 13:18
RDBMS를 사용할 때는 테이블 하나만 사용해서 애플리케이션의 모든 기능을 구현하기란불가능합니다. 대체로 설계가 복잡해지면 각 도메인에 맞는 테이블을 설계하고 연관관계를 설정해서 조인(Join)등의 기능을 활용합니다. 연관관계를 맺는 두 엔티티 간에 생성할 수 있는 연관관계의 종류는 다음과 같습니다.
- One To One : 일대일(1:1)
- One To Many : 일대다(1:N)
- Many To One : 다대일(N:1)
- Many To Many : 다대다(N:N)
1. 일대일 매핑
1234567891011121314151617181920212223@Getter@Setter@AllArgsConstructor@NoArgsConstructor@Builder@Entity@ToString(callSuper = true)@Table(name = "example")public class Example {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long number;private String name;private String email;@OneToOne(mappedBy = "example")@ToString.Exclude // StackOverflowError 가 발생하기 때문에 Exclude를 통해 제외 설정을 해준다.private ExampleDetail exampleDetail;}cs 123456789101112131415161718192021@Getter@Setter@AllArgsConstructor@NoArgsConstructor@Builder@Entity@ToString(callSuper = true)@Table(name = "example_detail")public class ExampleDetail {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String description;@OneToOne@JoinColumn(name = "example_number")private Example example;}cs 일대일 매핑은 하나의 엔티티에 다른 하나의 엔티티 정보만 매핑되는 구조라고 볼 수 있습니다. 일대일 매핑을 구현하기 위해서는 위에 Example과 ExampleDetail 엔티티처럼 두개의 엔티티가 필요합다. @OneToOne 어노테이션을 사용해서 일대일 매핑을 시키는 것인데 이때 두 엔티티 중 하나의 엔티티에만 @OneToOne 어노테이션을 써놓을 경우 단방향 매핑이라고 합니다. 말그대로 한 방향에서만 일대일 매핑을 한다는 뜻입니다. 그리고 서로 단방향 매핑을 하게되면 양방향 매핑이 됩니다. 위에 Example 클래스를 보면 @OneToOne 어노테이션에 mappedBy 가 정의 되어 있는것을 볼 수 있는데요, 양방향 매핑을 할 경우에는 한쪽의 테이블에서만 외래키를 바꿀 수 있도록 정의 하는 것이 좋습니다. 따라서 한쪽에게만 외래키를 주도록 설정을 해주어야 하는데 이때 사용되는 속성 값이 mappedBy 입니다. 그 밑에 @ToString.Exclude 어노테이션을 StackOverflowError 가 발생하는 것에 대비하여 설정해줍니다. ExampleDetail 클래스를 보면 @JoinColumn 어노테이션에 name 이 지정되어 있는데요 이는 매핑할 외래키의 이름을 설정합니다.
2. 다대일, 일대다 매핑
1234567891011121314151617181920212223@Getter@Setter@AllArgsConstructor@NoArgsConstructor@Builder@Entity@ToString(callSuper = true)@Table(name = "example")public class Example {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long number;private String name;private String email;@ManyToOne@JoinColumn(name = "exampleOne_id")private ExampleOne exampleOne;}cs 12345678910111213141516171819@Entity@Getter@Setter@ToString@NoArgsConstructor@AllArgsConstructor@Builder@Table(name = "exampleOne")public class ExampleOne {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String description;}cs 다대일 매핑은 하나의 데이터가 여러개의 데이터에 매핑되는 형식입니다. @ManyToOne 어노테이션을 사용하여 매핑해주고 하나의 컬럼에 여러개의 데이터를 매핑해줘야 하므로 @JoinColumn 어노테이션을 이용하여 매핑할 컬럼을 지정해주어야 합니다.123456789101112131415161718192021@Entity@Getter@Setter@ToString@NoArgsConstructor@AllArgsConstructor@Builder@Table(name = "exampleOne")public class ExampleOne {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String description;@OneToMany(fetch = FetchType.EAGER)@JoinColumn(name = "exampleOne_id")private List<ExampleDetail> exampleDetails = new ArrayList<>();}cs 1234567891011121314151617@Getter@Setter@AllArgsConstructor@NoArgsConstructor@Builder@Entity@ToString(callSuper = true)@Table(name = "example_detail")public class ExampleDetail {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String description;}cs 일대다 매핑이 여러개의 데이터들이 하나의 컬럼에 매핑되는 형식입니다. @OneToMany 어노테이션을 이용해서 매핑을 해주고 @JoinColumn 어노테이션을 이용해서 외래키를 지정해줍니다. 일대다 연관관계의 경우 여러 엔티티가 포함될 수 있어 ExampleOne 클래스의 exampleDetails처럼 컬렉션 형식으로 (Collection, List, Map) 필드를 생성합니다.123456789101112131415161718192021222324@Getter@Setter@AllArgsConstructor@NoArgsConstructor@Builder@Entity@ToString(callSuper = true)@Table(name = "example")public class Example {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long number;private String name;private String email;@ManyToOne@JoinColumn(name = "exampleOne_id")private ExampleOne exampleOne;}cs 123456789101112131415161718192021@Entity@Getter@Setter@ToString@NoArgsConstructor@AllArgsConstructor@Builder@Table(name = "exampleOne")public class ExampleOne {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String description;@OneToMany(mappedBy = "exampleOne", fetch = FetchType.EAGER)@ToString.Excludeprivate List<Example> exampleList = new ArrayList<>();}cs 위에 코드는 다대일 양방향 매핑을 하는 과정을 보여준 코드입니다. ExampleOne 클래스를 보면'fetch = FetchType.EAGER'라는 것이 있는데요 이렇게 선언한 이유는 @OneToMany의 기본 fetch 전량이 LAZY이기 때문입니다. LAZY는 지연로딩이고 EAGER는 즉시로딩입니다. 지연로딩과 즉시로딩은 엔티티를 조회할 때 적용되는데요, 지연로딩을 할경우 exampleList가 출력이 불가능하게 되고 직접 사용할 때만 사용이 가능하게 합니다. 즉시로딩을 할 경우 exampleList를 출력이 가능하게 해줍니다. 따라서 변수가 위처럼 collection 형식인 경우 지연로딩을 하고 단일 엔티티인 경우 즉시로딩을 하는 것이 효율적입니다.
3. 다대다 매핑
123456789101112131415161718192021222324@Getter@Setter@AllArgsConstructor@NoArgsConstructor@Builder@Entity@ToString(callSuper = true)@EqualsAndHashCode@Table(name = "exampleTwo")public class ExampleTwo {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long number;private String name;private String email;@ManyToMany@ToString.Excludeprivate List<ExampleThree> exampleThrees = new ArrayList<>();}cs 1234567891011121314151617181920212223@Getter@Setter@AllArgsConstructor@NoArgsConstructor@Builder@Entity@EqualsAndHashCode@ToString(callSuper = true)@Table(name = "exampleThree")public class ExampleThree {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String description;@ManyToMany@ToString.Excludeprivate List<ExampleTwo> exampleTwos = new ArrayList<>();}cs 다대다 매핑은 다수의 엔티티들끼리 매핑되는 관계입니다. 위에 두 클래스들을 보면 @EqualsAndHashCode 어노테이션이 쓰였는데요 이를 사용하면 @JoinColumn, @JoinTable과 같은 어노테이션을 사용하여 외래키를 선언 할 필요 없이 두 클래스의 동일성을 판단하여 서로 매핑해줍니다.
'spring boot' 카테고리의 다른 글
[Spring boot] Logback에 대해서 알아보자. (0) 2024.02.12 [spring boot] 에러 핸들링 @ExceptionHandler @ControllerAdvice (0) 2024.01.25 [Spring Boot] ResponseEntity란? (0) 2024.01.24 [Spring] 다양한 매핑 방법 (1) 2024.01.22 spring security 403 Forbidden Error (0) 2023.01.12