1. ORM
Object Relational Mapping, 객체-관계 매핑
관계형 데이터베이스의 데이터를 객체지향적 언어로 자동으로 매핑(연결)해주는 것을 말한다.
- 객체 지향 프로그래밍은 클래스를 사용하고, 관계형 데이터베이스는 테이블을 사용하기 때문에 객체 모델과 관계형 모델 간에 불일치가 존재한다.
- ORM을 통해 객체 간의 관계를 바탕으로 SQL을 자동으로 생성하여 불일치를 해결한다.
데이터베이스 데이터 <—매핑—> Object 필드 - 이를 통해 Spring에서 객체를 사용해 간접적으로 데이터베이스 데이터를 다룰 수 있다.
- Persistent API라고도 할 수 있다. Ex) JPA, Hibernate 등
데이터베이스의 관계의 종류는 크게 세 가지가 있다.
- 1 : 1 - One to One
- 한 테이블 레코드 하나와 다른 테이블의 레코드 한 개가 연관되는 관계
- 보통 1:1의 관계는 테이블을 하나로 합쳐서 표현하기 때문에 흔하지 않다.
- 특정 데이터를 성능이나 보안적 측면에서 나눠야 하는 경우 사용된다.
- 보통 Foreign key 컬럼에 Unique 제약 사항을 적용함으로써 구현한다.
- 1 : N - Many to One
- 한 테이블의 레코드가 다른 테이블의 레코드 0 개 이상과 연관된 관계
- 가장 흔한 관계 (ex) 게시글 → 댓글, 가게 → 상품 등)
- N : N - Many to Many
- 한 테이블의 레코드 0 개 이상과 다른 테이블의 레코드 0 개 이상이 연관된 관계
- 양쪽 테이블의 PK를 Foreign key 로 가지는 제 3의 테이블을 만들어 표현한다. 이를 Associative Table 또는 Join Table 이라고 부른다.
JPA에서 1:N, N:N을 나타내는 어노테이션을 제공한다.
- @ManyToOne, @OneToMany
- @ManyToMany
2. 양방향 관계 mappedBy
자바에서 객체 참조는 방향성이 있다. 따라서 양방향 관계가 필요한 경우 연관을 두 번 정의해야 한다.
하지만 RDBMS에서는 방향이 없기 때문에 외래키 하나로 두 테이블의 연관관계를 관리한다.
때문에 자바에서 @OneToMany의 mappedBy 속성을 사용해 양방향 연관 관계를 가지는 두 엔터티 중 외래키를 관리할 객체를 지정해줘야 한다.
- mappedBy에 owner 엔터티의 필드명을 지정한다.
- 연관관계의 주인은 mappedBy 속성을 사용하지 않는다.
- mappedBy를 사용하지 않으면, N:1 관계의 경우 Join table이 생성되며 1:1 관계의 경우 각 테이블에 서로를 참조하는 FK가 설정된다.
양뱡향 연관 관계 두 엔터티 중 어떤 엔터티가 Owner?
- N:1 양방향 관계
- N:1 관계를 가진 쪽이 외래키 owner가 된다. → @OneToMany 쪽의 컬렉션 컬럼에 mappedBy 속성을 작성
- 1:1 양방향 관계
- 반대 엔터티에 대한 FK를 가지는 쪽이 owner가 된다.
- N:N 양방향 관계
- 양쪽 아무나 owner가 될 수 있다.
예시
@Entity
public class Lecture {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
private Instructor instructor; // Lecture -> Instructor
// ...
}
@Data
@Entity
public class Instructor {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
@OneToMany(mappedBy = "instructor")
private List<Lecture> lectures;
}
cf) 양방향 관계에서 주의할 점
위 예시 코드를 보면 Lecture 와 Insructor 각각 @Data 어노테이션이 설정되어 있어 각각 toString을 호출할 수 있다.
만약 lecture.toString()을 호출하면 내부의 필드의 toString을 호출하고, Instructor.toString을 호출하게 된다. Instructor 클래스에도 Lecture가 있기 때문에, Lecture.toString을 호출한다.
이렇게 두 엔터티가 반복적으로 서로의 toString을 호출하면서 stackOverFlow 에러가 발생하게 된다.
해결 방법
다양한 해결 방법이 존재하는데 두 가지만 정리해 보겠다.
- 두 객체 중 한 객체에서 JSON 직렬화를 막는다.
- @JsonManagedReference : 해당 필드는 직렬화를 허용한다.
- @JsonBackRefrence : 해당 필드는 직렬화하지 않도록 막는다. (역직렬화 중에 해당 필드 값은 Managed 링크가 있는 인스턴스로 설정된다.)
- @JsonIgnoreProperties 사용
- 이 어노테이션이 붙은 필드는 JSON 데이터에 null로 들어가게 된다.
3. @JoinColumn
외래키를 매핑할 때 사용하는 어노테이션
- name 속성 : 매핑할 외래키 컬럼명, 단순히 컬럼명을 만들어 주는 역할
- referencedColumnName 속성 : 외래키가 참조하는 대상 테이블의 컬럼명, 대상 테이블의 어떤 컬럼을 참조하는지 지정
- foreignKey 속성 : 외래키 제약조건을 직접 지정할 때 사용, 테이블을 생성할 때만 사용한다.
- 그 외 @Column 속성과 동일 (unique, nullable, insertable, updatable, columnDefinition, table)
@JoinColumn을 생략하면 외래키를 찾는 전략으로 참조하는 테이블의 기본 키 컬럼명을 사용한다.
예시
현재 Lecture와 Instructor가 N:1 관계를 가지고 있다. @JoinColumn을 생략했기 때문에 Instructor의 기본키인 id (실제 테이블에서는 instructor_id)를 참조한다.
@Data
@Entity
public class Lecture {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
// @JoinColumn(name = "instructor") // 외래키 매핑 할때 사용, name 속성에 매핑할 외래 키 이름을 지정
private Instructor instructor;
// ...
}
@Data
@Entity
public class Instructor {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
}
'CS > Backend' 카테고리의 다른 글
[Spring] @Valid와 @Validated를 이용한 유효성 검증 (0) | 2023.06.29 |
---|---|
[Spring] 서블릿, HTTP request & response (0) | 2023.06.27 |
[Spring] Pagiantion, Pageable 인터페이스 (0) | 2023.06.27 |
[Java] 직렬화, 역직렬화 (0) | 2023.06.25 |
[Spring] REST, REST API (0) | 2023.06.23 |