Skip to content

Commit

Permalink
feat: ERD 기반 Entity 정의 (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
kimminkyeu authored Sep 16, 2024
1 parent 7a89b31 commit 89f07d7
Show file tree
Hide file tree
Showing 20 changed files with 719 additions and 4 deletions.
15 changes: 14 additions & 1 deletion backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,24 @@ repositories {
}

dependencies {
// spring boot
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'

// lombok annotation
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'

// Sql logging formatter
// reference: https://www.baeldung.com/java-p6spy-intercept-sql-logging
implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.9.2' //이쁘게

// logback logger
implementation 'ch.qos.logback:logback-classic:1.4.12'
implementation 'org.slf4j:slf4j-api:2.0.3'

// test environment
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package kernel360.techpick.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@Configuration
@EnableJpaAuditing
public class JpaAuditingConfig {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package kernel360.techpick.config;

import java.sql.SQLException;
import java.util.Locale;
import static org.springframework.util.StringUtils.hasText;

import org.hibernate.engine.jdbc.internal.FormatStyle;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;

import com.p6spy.engine.common.ConnectionInformation;
import com.p6spy.engine.event.JdbcEventListener;
import com.p6spy.engine.logging.Category;
import com.p6spy.engine.spy.P6SpyOptions;
import com.p6spy.engine.spy.appender.MessageFormattingStrategy;

/**
* Jdbc가 DB Connection을 얻은 이후에 로깅 포맷을 P6SpyOptions가 가로채도록 하는 Bean입니다.
*/
@Profile({"default", "local", "dev"}) // WARN: Do not use in production mode.
@Component
public class P6SpySqlLoggerConfig extends JdbcEventListener implements MessageFormattingStrategy {

@Override
public void onAfterGetConnection(ConnectionInformation connectionInformation, SQLException e) {
P6SpyOptions.getActiveInstance().setLogMessageFormat(this.getClass().getName());
}

@Override
public String formatMessage(int connectionId, String now, long elapsed, String category,
String prepared, String sql, String url) {
return highlight(format(category, sql));
}

private String highlight(String sql) {
return FormatStyle.HIGHLIGHT.getFormatter().format(sql);
}

private String format(String category, String sql) {
if (hasText(sql) && isStatement(category)) {
if (isDdl(trim(sql))) {
return FormatStyle.DDL.getFormatter().format(sql);
}
return FormatStyle.BASIC.getFormatter().format(sql);
}
return sql;
}

private static boolean isDdl(String trimmedSql) {
return trimmedSql.startsWith("create")
|| trimmedSql.startsWith("alter")
|| trimmedSql.startsWith("drop")
|| trimmedSql.startsWith("comment");
}

private static String trim(String sql) {
return sql.trim().toLowerCase(Locale.ROOT);
}

private static boolean isStatement(String category) {
return Category.STATEMENT.getName().equals(category);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package kernel360.techpick.entity.admin;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Table(name = "administrator")
@Entity
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Administrator {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "administrator_id")
private Long administratorId;

// 관리자 로그인 이메일
@Column(name = "email", nullable = false, unique = true)
private String email;

// 비밀번호
@Column(name = "password", nullable = false)
private String password;

// 관리자 이름
@Column(name = "name", nullable = false)
private String name;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package kernel360.techpick.entity.admin;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

/**
* NOTE: 이 부분은 좀 더 토의가 필요 합니다.
*
*/
@Table(name = "crawling_topic_mapping")
@Entity
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class CrawlingTopicMapping {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "crawling_topic_mapping_id")
private Long crawlingTopicMappingId;

// TODO: 태그 종류는 블로그별로 다 달라질 것이다.
// 아래 처럼 하면, 블로그가 추가될 때마다 우리가 직접 매핑해줘야 한다.

// TODO: 아래와 같이 하면, 블로그별 태그 mapper를 구현할 필요 없음.
// 아래 mappingString이 공용이기 때문.
// ex. "#Spring#스프링#spring" --> 모든 블로그에 적용
@Column(name = "mapping_string", nullable = false)
private String mappingString;
}
32 changes: 32 additions & 0 deletions backend/src/main/java/kernel360/techpick/entity/admin/Topic.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package kernel360.techpick.entity.admin;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

// NOTE: 관리자만 수정 가능한 테이블 입니다.
// TODO: 기본 값은 "분류 없음" 레코드 입니다.
// '분류 없음'을 테이블에 반드시 존재하도록 설정해야 합니다.
@Table(name = "topic")
@Entity
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Topic {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "topic_id")
private Long topicId;

// 주제명 (중복된 주제 명은 없어야 합니다.)
@Column(name = "topic_name", nullable = false, unique = true)
private String topicName;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package kernel360.techpick.entity.article;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import kernel360.techpick.entity.blog.Blog;
import kernel360.techpick.entity.common.CreatedAndUpdatedTimeColumn;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Table(name = "article")
@Entity
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Article extends CreatedAndUpdatedTimeColumn {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "article_id")
private Long articleId;

// 원문 제목
@Column(name = "title", nullable = false)
private String title;

// 원문 링크
@Column(name = "url", nullable = false)
private String url;

// 원문 작성 일자
@Column(name = "published_at", nullable = false)
private LocalDateTime publishedAt;

// 원문 작성자
@Column(name = "author") // nullable (수집 안될 수도 있음)
private String author;

// 원문 출처 블로그
@ManyToOne
@JoinColumn(name = "blog_id", nullable = false)
private Blog blog;

// 수집 날짜 (크롤링 시각)
@Column(name = "crawled_at", nullable = false)
private LocalDateTime crawled_at;

// 대표 이미지 CDN url
@Column(name = "image_url")
private String imageUrl; // nullable

// [사용자 - 관심 토픽] 1:N 테이블
@OneToMany(mappedBy = "article")
private List<ArticleTopic> articleTopics = new ArrayList<>();

// 게시글-토픽 관계 테이블
// @ManyToMany
// @JoinTable(
// name = "article_topic",
// joinColumns = @JoinColumn(name = "article_id", nullable = false),
// inverseJoinColumns = @JoinColumn(name = "topic_id", nullable = false)
// )
// private List<Topic> topics = new ArrayList<>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package kernel360.techpick.entity.article;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import kernel360.techpick.entity.admin.Topic;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Table(name = "article_topic")
@Entity
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ArticleTopic {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "article_topic_id")
private Long articleTopicId;

@ManyToOne
@JoinColumn(name = "article_id")
private Article article;

@ManyToOne
@JoinColumn(name = "topic_id")
private Topic topic;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package kernel360.techpick.entity.article;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import kernel360.techpick.entity.common.CreatedAndUpdatedTimeColumn;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

/**
* NOTE: 크롤링한 Raw 데이터를 삽입하는 테이블입니다.
* 데이터 형식은 바뀔 수 있습니다.
*/
@Table(name = "raw_crawled_article")
@Entity
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class RawCrawledArticle extends CreatedAndUpdatedTimeColumn {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "raw_crawled_article_id")
private Long rawCrawledArticleId;

// TODO: 아래 크롤링 데이터 칼럼은 토의 후 바뀔 예정 입니다.
// 크롤링 데이터
@Column(name = "data", nullable = false)
private String data;
}
Loading

0 comments on commit 89f07d7

Please sign in to comment.