[코드로 배우는 스프링부트 웹 프로젝트] - 연관관계(1) : 테이블 생성

Jongwon·2023년 1월 2일
0

관계 데이터베이스에서 가장 복잡하고도 중요한 개념은 연관관계라고 할 수 있습니다. 두 객체가 1:1관계인지, 1:N관계인지, 아니면 M:N관계인지 파악하고 테이블을 먼저 설계해야 합니다.

예제 프로젝트를 생성하겠습니다.

그리고 이전 프로젝트에서 사용하던 MariaDB와 타임리프 설정을 가져옵니다.

build.gradle의 dependencies에 추가

// https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client
	implementation group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: '3.1.0'

application.properties에 추가

spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.url=jdbc:mariadb://localhost:3308/bootex
spring.datasource.username=bootuser
spring.datasource.password=bootuser

spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.show-sql=true


spring.thymeleaf.cache=false

직전 예제에 사용했던 BaseEntity도 그대로 생성해줍니다.

BaseEntity

package org.zerock.board.entity;

import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;

@MappedSuperclass
@EntityListeners(value = {AuditingEntityListener.class})
@Getter
abstract class BaseEntity {

    @CreatedDate
    @Column(name="regdate", updatable = false)
    private LocalDateTime regDate;

    @LastModifiedDate
    @Column(name="moddate")
    private LocalDateTime modDate;
}

엔티티리스너 기능을 추가했으므로 main에 해당하는 클래스에 어노테이션도 추가합니다.
BoardApplication.java

package org.zerock.guestbook;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
@EnableJpaAuditing
public class GuestbookApplication {

	public static void main(String[] args) {
		SpringApplication.run(GuestbookApplication.class, args);
	}

}



다음으로는 연관관계 설정 이전에 각 테이블과 매핑되는 엔티티 클래스를 먼저 생성하겠습니다.

Member 엔티티

package org.zerock.board.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import lombok.*;

@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@ToString
public class Member extends BaseEntity {
    
    @Id
    private String email;
    
    private String password;
    
    private String name;
}

Board 엔티티

package org.zerock.board.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.*;

@Entity
@Builder
@AllArgsConstructor
@Getter
@ToString
@NoArgsConstructor
public class Board extends BaseEntity {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long bno;
    
    private String title;
    
    private String content;
}

Reply 엔티티

package org.zerock.board.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.*;

@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@ToString
public class Reply extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long rno;

    private String text;

    private String replier;
}

이제는 연관관계에 대해 생각해보겠습니다. Member와 Board 사이의 관계를 생각했을 때, Member는 여러 board를 작성할 수 있지만, 각 board는 하나의 member와 매칭됩니다.

새발표기법으로 연관관계를 그려보면 위의 다이어그램으로 표현할 수 있습니다. 즉, Member와 Board는 1:N 관계를 가지고 있습니다.

1:N 관계를 표현할 때는 @ManyToOne 어노테이션을 N에 적용합니다. N에 해당하는 엔티티는 1에 해당하는 엔티티의 기본키를 외래키로 참조하기 때문에 Board를 아래와같이 수정합니다.

Board 엔티티

package org.zerock.board.entity;

import jakarta.persistence.*;
import lombok.*;

@Entity
@Builder
@AllArgsConstructor
@Getter
@ToString(exclude = "writer")   //외래키는 ToString에서 제외
@NoArgsConstructor
public class Board extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long bno;

    private String title;

    private String content;
    
    //외래키를 참조함을 명시
    @ManyToOne
    private Member writer;
}

다음으로 Reply와 Board의 관계 역시 다이어그램으로 표현해보면 아래와 같습니다.

Reply에도 @ManyToOne 어노테이션을 추가하여 아래와 같이 코드를 수정합니다.

Reply 엔티티

package org.zerock.board.entity;

import jakarta.persistence.*;
import lombok.*;

@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@ToString(exclude = "board")
public class Reply extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long rno;

    private String text;

    private String replier;
    
    @ManyToOne
    private Board board;
}


이제 프로젝트를 실행시켜 보겠습니다.


먼저 Hibernate가 3개의 테이블을 생성시키고 각각의 기본키를 지정했습니다.

다음으로 Board와 Reply에 대해 외래키를 지정해주었습니다.

MariaDB를 확인하면 새로운 테이블 3개가 생성된 것을 확인할 수 있습니다.

ToString에서 연관관계를 가지는 애트리뷰트를 exclude한 이유는 바로 다음의 Lazy Loading에서 알 수 있는데, 참조되는 엔티티의 toString까지 접근해야하기 때문에 DB접근이 한번 더 일어나게 되고, 접근량이 많아지면 성능저하를 유발하기 때문에 제외해야 합니다.
ex: Board를 toString할 때 Member객체인 writer를 같이 출력한다면, writer에 해당하는 Member의 toString을 출력해야하고, 이를 위해 Member테이블에 접근해야 함

profile
Backend Engineer

0개의 댓글