상똥이의 Back-End 공부방
[Board Project] 5. 데이터베이스 연동하기(2) (annotation/ JPA) 본문
목차
1. Entity 구성
2. Entity 기본 기능 구현
3. 동일성 검사
[1. Entity 구성]
1. JPA 연동
- 자동화를 위해 JPA를 연동해야한다. 한다
(1) board_practice 패키지 안에 config 패키지를 만든다
(2) config 패키지 안에 클래스 JpaConfig를 만든다
(3) Jpa를 활성화하기 위해 이름 또는 타입을 기반으로 필요한 빈을 찾아 자동으로 반환 하는 @EnableJpaAuditing을 클래스 전체에 삽입한다. 엔티티가 데이터베이스에 저장되거나 업데이트될 때 생성 시간과 수정 시간을 자동으로 입력시켜준다.
(4) AutoAwared를 사용해 엔터티의 생성자와 수정자를 자동으로 가져와야 하는데 이 메서드는 @Bean을 사용해야 한다.
(5) 지정된 클래스와 그 하위 클래스의 모든 빈을 스캔할 수 있도록 해주는 @Configuration을 삽입한다.
(6) 생성자와 수정자는 현 단계에서 만들기엔 한계가 있으므로 대충 아무 아이디로 결정해두고 후에 수정할 TODO로 남겨둔다
코드 (접은 글)
package com.fastcampus.projectbord.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import java.util.Optional;
@EnableJpaAuditing
@Configuration
public class JpaConfig {
@Bean
public AuditorAware<String> auditorAware() {
return () -> Optional.of("uno"); // TODO: 스프링 시큐리티로 인증 기능을 붙이게 될 때, 수정하자
}
}
2. Article 클래스에 어노테이션 삽입
(1) @Getter : 모든 필드에 접근이 가능해지기 위해 전체에 붙여준다
(2) @ToString : 출력을 원활히 하기 위해 전체에 붙여준다
(3) @Table : @Index를 사용해 검색도구를 지정하기 위해 붙여준다
- index : title, hashtag, createdAt, createdBy
- content(본문 내용)는 index로 검색하기에 내용이 너무 광활하므로, MySQL에서 기능을 추가한다
(4) @Entity : 만들어둔 도메인을 엔터티로 만들기 위해 전체에 붙여준다
(5) @AuditingEntityListener : 클래스를 읽으며 수집할 때 엔터티를 자동적으로 수집하도록 돕는 어노테이션
코드(접은 글)
package com.fastcampus.projectbord.domain;
import lombok.Getter;
import lombok.ToString;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
@Getter
@ToString
@Table(indexes = {
@Index(columnList = "title"),
@Index(columnList = "hashtag"),
@Index(columnList = "createdAt"),
@Index(columnList = "createdBy")
})
@EntityListeners(AuditingEntityListener.class)
@Entity
public class Article {
(생략)
3. Article 도메인 각각에 애노테이션 삽입
(1) id 객체
- @id @GeneratedValue(Strategy = GenerationType.IDENTITY) : id 객체가 기본키임을 명시한다.
(2) title 객체
- @Setter : 도메인에서 수정이 가능하도록 한다
- @Column(nullable=false) : null값을 갖지 못하도록 한다
(3) content 객체
- @Setter : 도메인에서 수정이 가능하도록 한다
(4) hashtag 객체
- @Setter : 도메인에서 수정이 가능하도록 한다
(5) createdAt 객체
- @createdDate : 생성 날짜를 반환한다
- @Column(nullable=false) : null값을 갖지 못하도록 한다
(6) createdBy 객체
- @createdBy : 생성주체를 반환한다
- @Column(nullable=false, length=100) : null값을 갖지 못하도록 한다, 길이를 100으로 제한한다
(7) modifiedAt 객체
- @LastModifiedDate : 마지막 수정일자를 반환한다
- @Column(nullabl=false) : null값을 갖지 못하도록 한다
(8) modifiedBy 객체
- @LastModifiedBy : 마지막 수정자를 반환한다
- @Column(nullable=false, length=100) : null값을 갖지 못하도록 한다, 길이를 100으로 제한한다
※ Setter를 도메인 각각에 거는 이유 : Article class전체에 걸면 도메인에서 id객체에 접근하여 id를 바꾸는 것이 가능해지는데, 그런 기능은 넣고 싶지 않기 때문이다.
코드(접은 글)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; // 게시글 작성자 아이디, PK
@Setter
@Column(nullable = false)
private String title; // 게시글 제목
@Setter
@Column(nullable = false, length = 10000)
private String content; //게시글 내용
@Setter
private String hashtag; // 해시태그
@CreatedDate
@Column(nullable = false)
private LocalDateTime createdAt; // 게시글 작성일시
@CreatedBy
@Column(nullable = false, length = 100)
private String createdBy; // 게시글 생성자
@LastModifiedDate
private LocalDateTime modifiedAt; // 게시글 수정일시
@LastModifiedBy
@Column(nullable = false, length = 100)
private String modifiedBy; // 게시글 수정자
5. ArticleComment 클래스에 어노테이션 삽입
(1) @Getter : 모든 필드에 접근이 가능해지기 위해 전체에 붙여준다
(2) @ToString : 출력을 원활히 하기 위해 전체에 붙여준다
(3) @Table : @Index를 사용해 검색도구를 지정하기 위해 붙여준다
- index : content, createdAt, createdBy
(4) @Entity : 만들어둔 도메인을 엔터티로 만들기 위해 전체에 붙여준다
코드(접은 글)
package com.fastcampus.projectbord.domain;
import lombok.Getter;
import lombok.ToString;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
@Getter
@ToString
@Table(indexes = {
@Index(columnList = "content"),
@Index(columnList = "createdAt"),
@Index(columnList = "createdBy")
})
@EntityListeners(AuditingEntityListener.class)
@Entity
public class ArticleComment {
6. ArticleComment 도메인 각각에 애노테이션 삽입
(1) id 객체
- @id @GeneratedValue(Strategy = GenerationType.IDENTITY) : id 객체가 기본키임을 명시한다.
(2) Article 객체
- @Setter : 도메인에서 수정이 가능하도록 한다
- @ManyToOne(Optional = false) : N:1관계로, 게시글 하나에 N개의 댓글이 달릴 수 있으며 Optional=false는 댓글이 아예 없을 수 있음을 뜻한다.
(3) content 객체
- @Setter : 도메인에서 수정이 가능하도록 한다
- @Column(nullable=false, length=500) : null값을 갖지 못하게 하며 길이는 500으로 제한한다
(4) createdAt 객체
- @createdDate : 생성 날짜를 반환한다
- @Column(nullable=false) : null값을 갖지 못하도록 한다
(5) createdBy 객체
- @createdBy : 생성주체를 반환한다
- @Column(nullable=false, length=100) : null값을 갖지 못하도록 한다, 길이를 100으로 제한한다
(6) modifiedAt 객체
- @LastModifiedDate : 마지막 수정일자를 반환한다
- @Column(nullabl=false) : null값을 갖지 못하도록 한다
(7) modifiedBy 객체
- @LastModifiedBy : 마지막 수정자를 반환한다
- @Column(nullable=false, length=100) : null값을 갖지 못하도록 한다, 길이를 100으로 제한한다
코드(접은 글)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Setter @ManyToOne(optional = false) private Article article; // 게시글 (ID)
@Setter @Column(nullable = false, length = 500) private String content; // 본문
@CreatedDate @Column(nullable = false) private LocalDateTime createdAt; // 생성일시
@CreatedBy @Column(nullable = false, length = 100) private String createdBy; // 생성자
@LastModifiedDate @Column(nullable = false) private LocalDateTime modifiedAt; // 수정일시
@LastModifiedBy @Column(nullable = false, length = 100) private String modifiedBy; // 수정자
[2. Entity 기본 기능 구현]
1. Article 기본 기능 구현
(1) 기본 생성자를 제작한다
- 평소에 열어보지 않으므로 protected로 만들어준다 (기본 생성자는 private을 사용할 수 없다)
(2) 속성을 생성하는 생성자를 만든다
- 아이디와 메타데이터를 제외한, 사용자가 작성하는 title, content, hashtag만 생성하도록 구현한다
- private으로 막아두고 팩토리로 제공한다
(3) 팩토리 메소드 작성
- Article을 생성하고자 할 때 필요한 도메인을 명시
코드(접은 글)
protected Article() {}
//기본 생성자
private Article(String title, String content, String hashtag) {
this.title = title;
this.content = content;
this.hashtag = hashtag;
}
//속성 생성자
public static Article of(String title, String content, String hashtag) {
return new Article(title, content, hashtag);
}
//팩토리 메소드
2. ArticleComment 기본 기능 구현
protected ArticleComment() {}
private ArticleComment(Article article, String content) {
this.article = article;
this.content = content;
}
public static ArticleComment of(Article article, String content) {
return new ArticleComment(article, content);
}
[3. 동일성 검사]
1. Article
- 후에 게시판 화면을 게시글로 구성할 때, List 등 컬렉션을 사용할 수 있음
- 컬렉션으로 게시글을 사용하려면 중복 제거, 정렬 등의 기능이 필요
- equal hashcode 필요
- lombok 사용시 모든 필드를 비교하므로 비효율적 → id만 사용해서 비교
- 따라서 java.util.Objects.equals and hashCode 기능 사용
코드 (접은 글)
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Article article)) return false;
return id != null && id.equals(article.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
2. ArticleComment
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ArticleComment that)) return false;
return id != null && id.equals(that.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
[4. 실행]
여기까지 작성 후 실행하면 board 데이터베이스에 테이블이 생성된다
이렇게!!
마무리~~~
최종 코드 (접은 글)
Article
package com.fastcampus.projectbord.domain;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
@Getter
@ToString
@Table(indexes = {
@Index(columnList = "title"),
@Index(columnList = "hashtag"),
@Index(columnList = "createdAt"),
@Index(columnList = "createdBy")
})
@EntityListeners(AuditingEntityListener.class)
@Entity
public class Article {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Setter
@Column(nullable = false)
private String title; // 제목
@Setter
@Column(nullable = false, length = 10000)
private String content; // 본문
@Setter
private String hashtag; // 해시태그
//양방향 바인딩
@ToString.Exclude
@OrderBy("id")
@OneToMany(mappedBy = "article", cascade = CascadeType.ALL)
private final Set<ArticleComment> articleComments = new LinkedHashSet<>();
@CreatedDate
@Column(nullable = false)
private LocalDateTime createdAt; // 생성일시
@CreatedBy
@Column(nullable = false, length = 100)
private String createdBy; // 생성자
@LastModifiedDate
@Column(nullable = false)
private LocalDateTime modifiedAt; // 수정일시
@LastModifiedBy
@Column(nullable = false, length = 100)
private String modifiedBy; // 수정자
protected Article() {
}
private Article(String title, String content, String hashtag) {
this.title = title;
this.content = content;
this.hashtag = hashtag;
}
public static Article of(String title, String content, String hashtag) {
return new Article(title, content, hashtag);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Article article)) return false;
return id != null && id.equals(article.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
ArticleComment
package com.fastcampus.projectbord.domain;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.Objects;
@Getter
@ToString
@Table(indexes = {
@Index(columnList = "content"),
@Index(columnList = "createdAt"),
@Index(columnList = "createdBy")
})
@EntityListeners(AuditingEntityListener.class)
@Entity
public class ArticleComment {
JpaConfig
package com.fastcampus.projectbord.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import java.util.Optional;
@EnableJpaAuditing
@Configuration
public class JpaConfig {
@Bean
public AuditorAware<String> auditorAware() {
return () -> Optional.of("uno"); // TODO: 스프링 시큐리티로 인증 기능을 붙이게 될 때, 수정하자
}
}
'프로젝트 > 게시판 만들기' 카테고리의 다른 글
[Board Project] 7. 데이터베이스 연결 테스트 (JPA/ assertj) (2) | 2023.10.09 |
---|---|
[Board Project] 6. 데이터베이스 연결 테스트 준비 (mokaroo/ MySQL/ JPA) (0) | 2023.10.09 |
[Board Project] 4. 데이터베이스 연동하기 (MySQL/ JPA) (0) | 2023.09.05 |
[Board Project] 3. 도메인 설계 (ERD작성/ 자바 코드 작성) (0) | 2023.09.05 |
[Board Project] 2. 프로젝트 생성 (스프링 이니셜라이저/ UML/ api 설계) (0) | 2023.09.05 |