Skip to content

[4기 김희빈] Weekely Mission 1, 2, 3 PR 제출합니다. #276

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: kmebin
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
HELP.md

### Gradle ###
gradle/
gradlew
gradlew.bat
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/

### VS Code ###
.vscode/

### Mac OS ###
.DS_Store

### Log files ###
logs/
*.log
38 changes: 38 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
plugins {
id 'java'
id 'org.springframework.boot' version '2.7.14'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3.0.0 이상이 아닌 2.7.14 버전을 쓴 이유가 있을까요?

id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}

group = 'com.programmers'
version = '0.0.1-SNAPSHOT'

java {
sourceCompatibility = '17'
}

configurations {
compileOnly {
extendsFrom annotationProcessor
}
}

repositories {
mavenCentral()
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'

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

runtimeOnly 'com.h2database:h2'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
useJUnitPlatform()
}
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'jpa-basic'
14 changes: 14 additions & 0 deletions src/main/java/com/programmers/jpabasic/JpaBasicApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.programmers.jpabasic;

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

@EnableJpaAuditing
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

따로 Config Class를 만들지 않고 main 에서 설정한 이유가 있을까요~?

@SpringBootApplication
public class JpaBasicApplication {

public static void main(String[] args) {
SpringApplication.run(JpaBasicApplication.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.programmers.jpabasic.domain.customer.entity;

import javax.persistence.Embedded;
import javax.persistence.Entity;

import com.programmers.jpabasic.global.common.BaseEntity;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Customer extends BaseEntity {

@Embedded
private Name name;

public static Customer create(String firstName, String lastName) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이펙티브 자바 - 아이템 1 : 생성자 대신 정적 팩터리 메서드를 고려하라 를 잘 활용하네요!

return Customer.builder()
.name(Name.of(firstName, lastName))
.build();
}

public void updateName(String firstName, String lastName) {
this.name = Name.of(firstName, lastName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.programmers.jpabasic.domain.customer.entity;

import javax.persistence.Column;
import javax.persistence.Embeddable;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder
@Embeddable
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Name {

@Column(nullable = false, length = 30)
private String firstName;

@Column(nullable = false, length = 20)
private String lastName;

public static Name of(String firstName, String lastName) {
return Name.builder()
.firstName(firstName)
.lastName(lastName)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.programmers.jpabasic.domain.customer.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.programmers.jpabasic.domain.customer.entity.Customer;

public interface CustomerRepository extends JpaRepository<Customer, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.programmers.jpabasic.domain.member.entity;

import javax.persistence.Column;
import javax.persistence.Entity;

import com.programmers.jpabasic.global.common.BaseEntity;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member extends BaseEntity {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혹시 Member와 Customer는 어떤 차이가 있을까요 !?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

강의에 맞춰서 미션 1, 2를 Customer로 수행하고, 미션 3에서 Member 사용했던 것입니다! 큰 의미 없습니다 😂


@Column(nullable = false, length = 50)
private String name;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Customer에는 Name 타입으로 받았었는데, Member에서는 다시 String으로 받은 이유가 있을까요 ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Member 테이블은 컬럼으로 name만 존재하는 경우라고 가정했습니다! 사실 크게 의미를 두진 않았습니다 허허


@Column(nullable = false, length = 30, unique = true)
private String nickname;

@Column
private int age;

@Column(nullable = false)
private String address;

@Column(length = 100)
private String description;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

description에 대한 부분은 아래에서 사용했던 @lob 와는 다르게 설명이 길지는 않을 것 같아서 length를 100으로 하신걸까요 !?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 맞습니다! 보통 서비스에서 개인 프로필 설정할 때 소개 적는 상황을 생각하고 길이 제한을 걸었습니다 ㅎ.ㅎ

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.programmers.jpabasic.domain.order.entity;

import javax.persistence.Column;
import javax.persistence.Entity;

import com.programmers.jpabasic.global.common.BaseEntity;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Item extends BaseEntity {

@Column(nullable = false)
private int price;

@Column
private int stockQuantity;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

int 타입을 사용하는 부분에 있어서 위의 price와는 다르게 이 부분이나, Member의 age에는 nullable = false를 주지 않았는데, 통일성이 있으면 좋을 것 같아요 ~!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

재고량은 null과 0이 다른 의미를 가질 수 있지 않을까 생각했던 것인데, 또 생각해보니 다른 의미를 가짐으로써 더 혼동을 줄 수 있을 것 같네요..! 감삼당

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.programmers.jpabasic.domain.order.entity;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import com.programmers.jpabasic.domain.member.entity.Member;
import com.programmers.jpabasic.global.common.BaseEntity;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@Builder
@Table(name = "orders")
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Order extends BaseEntity {

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;

@Column(nullable = false)
@Enumerated(value = EnumType.STRING)
private OrderStatus status;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nullable을 사용하신 용도가 값을 무조건 받아야 한다. 라는 의미로 사용하신 건가요??
사용 하신거면 나중에는 따로 초기값을 지정해 주실 건가요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 해당 컬럼에 null이 올 수 없다는 의미로 사용하였습니다! 생성될 때의 status 초기값이 항상 같기 때문에 default를 적용해도 좋을 것 같네요 감사합니당 👍


@Lob
private String memo;

@OneToMany(mappedBy = "order")
private final List<OrderItem> orderItems = new ArrayList<>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.programmers.jpabasic.domain.order.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

import com.programmers.jpabasic.global.common.BaseEntity;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class OrderItem extends BaseEntity {

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id")
private Order order;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

양방향 매핑 하신 이유가 궁금합니다!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

하나의 주문(order)에 주문한 아이템 목록을 조회하는 것은 비지니스 로직 상 자주 사용될 수 있다고 판단해서 양방향을 적용했습니다!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위의 이유라면! 관계테이블(OrderItems)에서 이미 가능한 것 아닌가요??

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

예를 들어, List items = OrderItemRepository.findByOrder(...)


@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "item_id")
private Item item;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

item은 단방향으로 매핑 하셨는데, 단방향 매핑을 하신 이유가 있을까요?

Copy link
Member Author

@kmebin kmebin Jul 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반대로 item은 아이템의 정보로써 의미를 가지고, 해당 아이템이 포함된 주문 아이템 목록을 필요로하는 일은 많지 않다고 판단했습니다. 필요할 때 쿼리를 따로 날리면 되기 때문에 단방향으로도 충분하다고 생각합니다!


@Column(nullable = false)
private int price;

@Column(nullable = false)
private int quantity;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.programmers.jpabasic.domain.order.entity;

public enum OrderStatus {
OPENED, CANCELLED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.programmers.jpabasic.global.common;

import java.time.LocalDateTime;

import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;

import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import lombok.Getter;

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BaseEntity의 역할이 단순히 엔티티가 사용하는 공통적인 정보를 모은다는 점에 있어서, 각 엔티티의 id를 가지고 있는 점이 어색한 것 같아요. Member의 id, Item의 id, Order의 id, OrderItem의 id가 공통적인 id라고 말할 수 있을까요 ?

BaseEntity 관련 게시글 같은 것을 참고해보면 좋을 것 같아요 !

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

id도 createdAt, updatedAt과 같이 모든 테이블에서 공통적으로 가지는 컬럼이라는 관점에서 작성했습니다..!
좀 더 고민해보겠습니다!!


@CreatedDate
@Column(nullable = false, updatable = false, columnDefinition = "TIMESTAMP")
private LocalDateTime createdAt;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 updatable의 default 값이 true 일텐데, createdAt의 updatable이 true여도 괜찮을까요 ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 고려하지 못했던 부분이었는데 감사합니다! 바로 적용하겠습니다~


@LastModifiedDate
@Column(nullable = false, columnDefinition = "TIMESTAMP")
private LocalDateTime updatedAt;
}
5 changes: 5 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
spring:
jpa:
properties:
hibernate:
format_sql: true
Loading