간단한 게시판을 만들거나 복잡한 프로젝트를 진행할때 RDBMS를 이용한다. RDBMS를 이용하면서, 테이블 하나로 모든 데이터를 관리할 수 없다. 즉, 테이블을 여러개 만들어 관련된 데이터들을 나누고, 관리한다. 필요시에는 테이블 조인(Join)하여 처리해야 한다.
JPA를 이용하면 테이블간의 관계처럼, 엔티티들 사이의 관계를 통해 데이터를 관리할 수 있다.
객체지향 프로그램에서의 객체와 RDBMS에서의 테이블이 서로 연관관계를 맺는 방법이 다르다. 그렇기 때문에 이 둘의 차이를 채우기 위한 매핑과정이 필요하고 이를 ORM 인 JPA 가 수행하게 된다.
객체 연관 관계
회원 테이블과 팀 테이블은 양방향 관계입니다. 회원 테이블의 BOARD_ID 외래 키를 통해서 회원과 팀을 조인할 수 있고, 반대로 팀과 회원도 조인할 수 있습니다. 예를 들어, USER 테이블의 BOARD_ID 외래 키 하나로 MEMBER board TEAM 과 board JOIN User 둘 다 가능합니다.
객체 연관관계와 테이블 연관관계의 가장 큰 차이
참조를 통한 연관관계는 항상 단방향입니다. 객체간에 연관관계를 양방향으로 만드록 싶으면 반대쪽에도 필드를 추가해서 참조를 보관해야 합니다. 이렇듯 양쪽에서 서로 참조하는 것을 양방향 연관관계라고 합니다.
그러나 이것은 양방향 관계가 아니라 서로 다른 단방향 관계 2개입니다. 반면에 테이블은 외래 키 하나로 양방향으로 조인할 수 있습니다.
객체 연관관계 vs 테이블 연관관계 정리

[테이블연관관계]
User엔티티
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Builder
@ToString(exclude = "boardList")
@Table
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false,unique = true)
private String email;
@Column(nullable = false)
private String password;
@Column(nullable = false)
private String name;
@ElementCollection(fetch = FetchType.EAGER)
@Builder.Default
private List<String> roles = new ArrayList<>();
@OneToMany(fetch = FetchType.LAZY,mappedBy = "user",cascade = CascadeType.ALL)
@JsonIgnore
private List<Board> board;
//계정이 가지고 있는 권한 목록 리턴
@Override
public Collection<? extends GrantedAuthority> getAuthorities(){
return this.roles.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
//계정의 비밀번호 리턴
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@Override
public String getPassword(){
return this.password;
}
//계정의 이름 리턴
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@Override
public String getUsername() {
return this.getEmail();
}
//계정이 만료됐는지 리턴 - true면 만료 X
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@Override
public boolean isAccountNonExpired() {
return true;
}
//계정이 잠겨 있는지 리턴 -> true면 안잠김.
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@Override
public boolean isAccountNonLocked() {
return true;
}
//계정의 비밀번호가 만료됐는지 리턴 -> ture면 만료 X
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@Override
public boolean isCredentialsNonExpired() {
return true;
}
//계정이 활성화돼 있는지 리턴 -> true면 활성화
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@Override
public boolean isEnabled() {
return true;
}
}
Board엔티티
package com.springboot.jwt_securityprac.data.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Getter
@Setter
@ToString(exclude = "member")
@Table(name = "Board")
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String title;
@Column
private String content;
@Column
private LocalDateTime createDate;
@Column
private LocalDateTime updateDate;
@ManyToOne //다대일 관계, 여러 게시물이 하나의 user의 것
@JoinColumn(name = "user_id")
@JsonIgnore
private User user;
}
[객체 연관관계]
User엔티티
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Builder
@ToString(exclude = "boardList")
@Table
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false,unique = true)
private String email;
@Column(nullable = false)
private String password;
@Column(nullable = false)
private String name;
@ElementCollection(fetch = FetchType.EAGER)
@Builder.Default
private List<String> roles = new ArrayList<>();
@OneToMany(fetch = FetchType.LAZY,mappedBy = "user",cascade = CascadeType.ALL)
@JsonIgnore
private Board board;
//계정이 가지고 있는 권한 목록 리턴
@Override
public Collection<? extends GrantedAuthority> getAuthorities(){
return this.roles.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
//계정의 비밀번호 리턴
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@Override
public String getPassword(){
return this.password;
}
//계정의 이름 리턴
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@Override
public String getUsername() {
return this.getEmail();
}
//계정이 만료됐는지 리턴 - true면 만료 X
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@Override
public boolean isAccountNonExpired() {
return true;
}
//계정이 잠겨 있는지 리턴 -> true면 안잠김.
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@Override
public boolean isAccountNonLocked() {
return true;
}
//계정의 비밀번호가 만료됐는지 리턴 -> ture면 만료 X
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@Override
public boolean isCredentialsNonExpired() {
return true;
}
//계정이 활성화돼 있는지 리턴 -> true면 활성화
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@Override
public boolean isEnabled() {
return true;
}
}
Board엔티티
package com.springboot.jwt_securityprac.data.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Getter
@Setter
@ToString(exclude = "member")
@Table(name = "Board")
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String title;
@Column
private String content;
@Column
private LocalDateTime createDate;
@Column
private LocalDateTime updateDate;
}
@ManyToOne, @JoinColumn
@ManyToOne: 다대일(N:1) 관계라는 매핑 정보입니다. 위에서 봤던 회원과 팀은 다대일 관계입니다. 연관관계를 매핑할 때 이렇게 다중성을 나타내는 어노테이션을 필수로 사용해야 합니다.
| 속성 | 기능 | 기본값 |
|---|---|---|
| optional | false로 설정하면 연관된 엔티티가 항상 있어야 한다 | true |
| fetch | 글로벌 패치 전략을 설정한다. | @ManyToOne=FetchType.EAGER, @OneToMany=FetchType.LAZY |
| cascade | 영속성 전이 기능을 사용한다. | |
| targetEntity | 연관된 엔티티의 타입 정보를 설정한다. 이 기능은 거의 사용하지 않음 |
@JoinColumn: 외래 키를 매핑할 때 사용합니다.
| 속성 | 기능 | 기본값 |
|---|---|---|
| name | 매핑할 외래 키 이름 | "필드명" + "_" + "참조하는 테이블의 기본 키 컬럼명" |
| referencedColumnName | 외래 키가 참조하는 대상 테이블의 컬럼명 | 참조하는 테이블의 기본 키 컬럼명 |
| foreignKey(DDL) | 외래 키 제약조건을 직접 지정할 수 있다. 이 속성은 테이블을 생성할 때만 사용한다. | |
| unique | @Column의 속성과 같다 | |
| nullable | @Column의 속성과 같다 | |
| insertable | @Column의 속성과 같다 | |
| updatable | @Column의 속성과 같다 | |
| columnDefinition | @Column의 속성과 같다 | |
| table | @Column의 속성과 같다 |