diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..523eb4c48 --- /dev/null +++ b/.gitignore @@ -0,0 +1,202 @@ +# Created by https://www.toptal.com/developers/gitignore/api/intellij+all,macos,gradle,visualstudiocode,java +# Edit at https://www.toptal.com/developers/gitignore?templates=intellij+all,macos,gradle,visualstudiocode,java + +### Intellij+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +gradlew.bat +gradlew +gradle/** + + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + +.idea/* + +!.idea/codeStyles +!.idea/runConfigurations + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Gradle ### +.gradle +**/build/ +!src/**/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Avoid ignore Gradle wrappper properties +!gradle-wrapper.properties + +# Cache of project +.gradletasknamecache + +# Eclipse Gradle plugin generated files +# Eclipse Core +.project +# JDT-specific (Eclipse Java Development Tools) +.classpath + +### Gradle Patch ### +# Java heap dump +*.hprof + +# End of https://www.toptal.com/developers/gitignore/api/intellij+all,macos,gradle,visualstudiocode,java \ No newline at end of file diff --git a/HELP.md b/HELP.md new file mode 100644 index 000000000..59c3de046 --- /dev/null +++ b/HELP.md @@ -0,0 +1,28 @@ +# Getting Started + +### Reference Documentation + +For further reference, please consider the following sections: + +* [Official Gradle documentation](https://docs.gradle.org) +* [Spring Boot Gradle Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/3.1.5/gradle-plugin/reference/html/) +* [Create an OCI image](https://docs.spring.io/spring-boot/docs/3.1.5/gradle-plugin/reference/html/#build-image) +* [Spring Data JPA](https://docs.spring.io/spring-boot/docs/3.1.5/reference/htmlsingle/index.html#data.sql.jpa-and-spring-data) +* [Spring Web](https://docs.spring.io/spring-boot/docs/3.1.5/reference/htmlsingle/index.html#web) + +### Guides + +The following guides illustrate how to use some features concretely: + +* [Accessing data with MySQL](https://spring.io/guides/gs/accessing-data-mysql/) +* [Accessing Data with JPA](https://spring.io/guides/gs/accessing-data-jpa/) +* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/) +* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/) +* [Building REST services with Spring](https://spring.io/guides/tutorials/rest/) + +### Additional Links + +These additional references should also help you: + +* [Gradle Build Scans – insights for your project's build](https://scans.gradle.com#gradle) + diff --git a/build.gradle b/build.gradle new file mode 100644 index 000000000..3ee78e16a --- /dev/null +++ b/build.gradle @@ -0,0 +1,39 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.1.5' + id 'io.spring.dependency-management' version '1.1.3' +} + +group = 'com.example' +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' + runtimeOnly 'com.mysql:mysql-connector-j' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +tasks.named('bootBuildImage') { + builder = 'paketobuildpacks/builder-jammy-base:latest' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..74fcbe45c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +version: "3.8" + +services: + mysql: + build: + context: ./mysql + dockerfile: Dockerfile + ports: + - "3300:3306" + volumes: + - "data_volume:/var/lib/mysql" + environment: + MYSQL_PASSWORD: "test" + MYSQL_ROOT_PASSWORD: "test" + MYSQL_DATABASE: "test" +volumes: + data_volume: \ No newline at end of file diff --git a/mysql/Dockerfile b/mysql/Dockerfile new file mode 100644 index 000000000..89e1e729b --- /dev/null +++ b/mysql/Dockerfile @@ -0,0 +1,6 @@ +# Use the official MySQL 8 image as the base image +FROM mysql:8 +ENV MYSQL_ROOT_PASSWORD=test +ENV MYSQL_DATABASE=test +ENV MYSQL_ROOT_USER=root +COPY init.sql /docker-entrypoint-initdb.d/ \ No newline at end of file diff --git a/mysql/init.sql b/mysql/init.sql new file mode 100644 index 000000000..8484bdd67 --- /dev/null +++ b/mysql/init.sql @@ -0,0 +1,27 @@ +CREATE +DATABASE IF NOT EXISTS test; + +USE test; + +create table customers +( + id bigint primary key , + username varchar(20), + name varchar(20), + password varchar(50), + nickname varchar(20) +); + + +CREATE +DATABASE IF NOT EXISTS test_code; +USE test_code; + +create table customers +( + id bigint primary key , + username varchar(20), + name varchar(20), + password varchar(50), + nickname varchar(20) +); \ No newline at end of file diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 000000000..b9c66803a --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'jpa' diff --git a/src/main/java/com/example/jpa/JpaApplication.java b/src/main/java/com/example/jpa/JpaApplication.java new file mode 100644 index 000000000..2f5b36153 --- /dev/null +++ b/src/main/java/com/example/jpa/JpaApplication.java @@ -0,0 +1,13 @@ +package com.example.jpa; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class JpaApplication { + + public static void main(String[] args) { + SpringApplication.run(JpaApplication.class, args); + } + +} diff --git a/src/main/java/com/example/jpa/customer/Customer.java b/src/main/java/com/example/jpa/customer/Customer.java new file mode 100644 index 000000000..dc9a6757c --- /dev/null +++ b/src/main/java/com/example/jpa/customer/Customer.java @@ -0,0 +1,57 @@ +package com.example.jpa.customer; + +import com.example.jpa.order.Order; +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter +@Setter +@NoArgsConstructor +@Table(name = "customers") +public class Customer { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String username; + private String password; + private String name; + private String nickname; + + @OneToMany(mappedBy = "customer", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true) + private List orders = new ArrayList<>(); + + private Customer(String name, String nickname, String password, String username) { + this.name = name; + this.nickname = nickname; + this.password = password; + this.username = username; + } + + public static Customer of(CustomerSaveRequestDto requestDto) { + return new Customer( + requestDto.name(), + requestDto.nickname(), + requestDto.password(), + requestDto.username() + ); + } + + public Long updateInfo(CustomerUpdateRequestDto requestDto) { + this.username = requestDto.username(); + this.password = requestDto.password(); + this.nickname = requestDto.nickname(); + return this.id; + } + + public void addOrder(Order order) { + order.setMember(this); + } +} diff --git a/src/main/java/com/example/jpa/customer/CustomerController.java b/src/main/java/com/example/jpa/customer/CustomerController.java new file mode 100644 index 000000000..016ee704e --- /dev/null +++ b/src/main/java/com/example/jpa/customer/CustomerController.java @@ -0,0 +1,39 @@ +package com.example.jpa.customer; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/customers") +public class CustomerController { + + private final CustomerService customerService; + + @GetMapping() + public List getCustomerList() { + return customerService.getCustomers(); + } + + @GetMapping("/{customerId}") + public CustomerResponseDto getCustomer(@PathVariable Long customerId) { + return customerService.getCustomer(customerId); + } + + @PostMapping() + public Long saveNewCustomer(@RequestBody CustomerSaveRequestDto requestDto) { + return customerService.saveCustomer(requestDto); + } + + @PatchMapping("/{customerId}") + public Long updateCustomerInfo(@RequestBody CustomerUpdateRequestDto requestDto, @PathVariable Long customerId) { + return customerService.updateCustomer(requestDto, customerId); + } + + @DeleteMapping("/{customerId}") + public Long deleteCustomer(@PathVariable Long customerId){ + return customerService.deleteCustomer(customerId); + } +} diff --git a/src/main/java/com/example/jpa/customer/CustomerRepository.java b/src/main/java/com/example/jpa/customer/CustomerRepository.java new file mode 100644 index 000000000..ad0813c40 --- /dev/null +++ b/src/main/java/com/example/jpa/customer/CustomerRepository.java @@ -0,0 +1,7 @@ +package com.example.jpa.customer; + + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CustomerRepository extends JpaRepository { +} diff --git a/src/main/java/com/example/jpa/customer/CustomerResponseDto.java b/src/main/java/com/example/jpa/customer/CustomerResponseDto.java new file mode 100644 index 000000000..0e218a7ab --- /dev/null +++ b/src/main/java/com/example/jpa/customer/CustomerResponseDto.java @@ -0,0 +1,15 @@ +package com.example.jpa.customer; + +public record CustomerResponseDto ( + Long customerId, + String username, + String nickname +){ + + public static CustomerResponseDto of(Customer customer) { + return new CustomerResponseDto( + customer.getId(), + customer.getUsername(), + customer.getNickname()); + } +} diff --git a/src/main/java/com/example/jpa/customer/CustomerSaveRequestDto.java b/src/main/java/com/example/jpa/customer/CustomerSaveRequestDto.java new file mode 100644 index 000000000..baa7378f3 --- /dev/null +++ b/src/main/java/com/example/jpa/customer/CustomerSaveRequestDto.java @@ -0,0 +1,9 @@ +package com.example.jpa.customer; + +public record CustomerSaveRequestDto ( + String username, + String nickname, + String password, + String name +){ +} diff --git a/src/main/java/com/example/jpa/customer/CustomerService.java b/src/main/java/com/example/jpa/customer/CustomerService.java new file mode 100644 index 000000000..b74ac1503 --- /dev/null +++ b/src/main/java/com/example/jpa/customer/CustomerService.java @@ -0,0 +1,50 @@ +package com.example.jpa.customer; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.NoSuchElementException; + +@Service +@RequiredArgsConstructor +public class CustomerService { + + private final CustomerRepository customerRepository; + + public List getCustomers() { + return customerRepository.findAll() + .stream() + .map(CustomerResponseDto::of) + .toList(); + } + + public Long saveCustomer(CustomerSaveRequestDto requestDto) { + Customer customer = Customer.of(requestDto); + return customerRepository.save(customer).getId(); + } + + @Transactional + public Long updateCustomer(CustomerUpdateRequestDto requestDto, Long customerId) { + Customer findCustomer = findCustomer(customerId); + return findCustomer.updateInfo(requestDto); + } + + @Transactional + public Long deleteCustomer(Long customerId) { + Customer findCustomer = findCustomer(customerId); + customerRepository.delete(findCustomer); + return findCustomer.getId(); + } + + public CustomerResponseDto getCustomer(Long customerId) { + return CustomerResponseDto.of(findCustomer(customerId)); + } + + private Customer findCustomer(Long customerId) { + Customer findCustomer = customerRepository.findById(customerId) + .orElseThrow(() -> new NoSuchElementException("해당 유저를 찾을 수 없습니다.")); + return findCustomer; + } +} diff --git a/src/main/java/com/example/jpa/customer/CustomerUpdateRequestDto.java b/src/main/java/com/example/jpa/customer/CustomerUpdateRequestDto.java new file mode 100644 index 000000000..971e0babf --- /dev/null +++ b/src/main/java/com/example/jpa/customer/CustomerUpdateRequestDto.java @@ -0,0 +1,8 @@ +package com.example.jpa.customer; + +public record CustomerUpdateRequestDto( + String username, + String password, + String nickname +) { +} diff --git a/src/main/java/com/example/jpa/customer/JpaCustomerRepository.java b/src/main/java/com/example/jpa/customer/JpaCustomerRepository.java new file mode 100644 index 000000000..6efcbb867 --- /dev/null +++ b/src/main/java/com/example/jpa/customer/JpaCustomerRepository.java @@ -0,0 +1,68 @@ +package com.example.jpa.customer; + +import jakarta.persistence.*; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Profile("test") +@Repository +@RequiredArgsConstructor +public class JpaCustomerRepository { + + private final EntityManager em; + + void save(Customer customer){ + EntityTransaction transaction = em.getTransaction(); + transaction.begin(); + + em.persist(customer); + + transaction.commit(); + } + + List findAll() { + EntityTransaction transaction = em.getTransaction(); + transaction.begin(); + + List customers = em.createQuery("select c from Customer c", Customer.class).getResultList(); + + transaction.commit(); + return customers; + } + + Optional findById(Long id){ + EntityTransaction transaction = em.getTransaction(); + transaction.begin(); + + Customer customerById = em.find(Customer.class, id); + Optional customer = Optional.ofNullable(customerById); + + transaction.commit(); + return customer; + } + + void update(Long id, CustomerUpdateRequestDto updatedCustomer){ + EntityTransaction transaction = em.getTransaction(); + transaction.begin(); + + Customer customerById = em.find(Customer.class, id); + customerById.updateInfo(updatedCustomer); + + transaction.commit(); + } + + void delete(Long id){ + EntityTransaction transaction = em.getTransaction(); + transaction.begin(); + + Customer customerById = em.find(Customer.class, id); + em.remove(customerById); + + transaction.commit(); + } + +} diff --git a/src/main/java/com/example/jpa/order/Order.java b/src/main/java/com/example/jpa/order/Order.java new file mode 100644 index 000000000..f869bdfe8 --- /dev/null +++ b/src/main/java/com/example/jpa/order/Order.java @@ -0,0 +1,49 @@ +package com.example.jpa.order; + +import com.example.jpa.customer.Customer; +import com.example.jpa.orderItem.OrderItem; +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Objects; + +@Entity +@Getter +@Setter +@Table(name = "orders") +public class Order { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String memo; + @Enumerated(EnumType.STRING) + private OrderStatus orderStatus; + private LocalDateTime orderDatetime; + + @Column(name = "customer_id", insertable = false, updatable = false) + private Long customerId; + //customer_fk + @ManyToOne + @JoinColumn(name = "customer_id", referencedColumnName = "id") + private Customer customer; + + @OneToMany(mappedBy = "order") + private List orderItems; + + public void setMember(Customer customer) { + if(Objects.nonNull(this.customer)) { + this.customer.getOrders().remove(this); + } + + this.customer = customer; + customer.getOrders().add(this); + } + + public void addOrderItem(OrderItem orderItem) { + orderItem.setOrder(this); + } +} diff --git a/src/main/java/com/example/jpa/order/OrderRepository.java b/src/main/java/com/example/jpa/order/OrderRepository.java new file mode 100644 index 000000000..da5341fcf --- /dev/null +++ b/src/main/java/com/example/jpa/order/OrderRepository.java @@ -0,0 +1,6 @@ +package com.example.jpa.order; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface OrderRepository extends JpaRepository { +} diff --git a/src/main/java/com/example/jpa/order/OrderStatus.java b/src/main/java/com/example/jpa/order/OrderStatus.java new file mode 100644 index 000000000..4d33957dd --- /dev/null +++ b/src/main/java/com/example/jpa/order/OrderStatus.java @@ -0,0 +1,5 @@ +package com.example.jpa.order; + +public enum OrderStatus { + OPENED, CANCELED +} diff --git a/src/main/java/com/example/jpa/orderItem/BaseEntity.java b/src/main/java/com/example/jpa/orderItem/BaseEntity.java new file mode 100644 index 000000000..f5351662f --- /dev/null +++ b/src/main/java/com/example/jpa/orderItem/BaseEntity.java @@ -0,0 +1,16 @@ +package com.example.jpa.orderItem; + +import jakarta.persistence.Column; +import jakarta.persistence.MappedSuperclass; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@MappedSuperclass +public class BaseEntity { + @Column(name = "created_by") + private String createdBy; + @Column(name = "created_at", columnDefinition = "TIMESTAMP") + private LocalDateTime createdAt; +} \ No newline at end of file diff --git a/src/main/java/com/example/jpa/orderItem/Item.java b/src/main/java/com/example/jpa/orderItem/Item.java new file mode 100644 index 000000000..177336e10 --- /dev/null +++ b/src/main/java/com/example/jpa/orderItem/Item.java @@ -0,0 +1,36 @@ +package com.example.jpa.orderItem; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.util.Objects; + +@Entity +@Getter +@Setter +@Table(name = "item") +@ToString +public class Item extends BaseEntity{ + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String itemName; + private int price; + private int stockQuantity; + + @ManyToOne + @JoinColumn(name = "order_item_id", referencedColumnName = "id") + private OrderItem orderItem; + + public void setOrderItem(OrderItem orderItem) { + if (Objects.nonNull(this.orderItem)) { + this.orderItem.getItems().remove(this); + } + + this.orderItem = orderItem; + orderItem.getItems().add(this); + } +} diff --git a/src/main/java/com/example/jpa/orderItem/ItemRepository.java b/src/main/java/com/example/jpa/orderItem/ItemRepository.java new file mode 100644 index 000000000..353cf1ccd --- /dev/null +++ b/src/main/java/com/example/jpa/orderItem/ItemRepository.java @@ -0,0 +1,6 @@ +package com.example.jpa.orderItem; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ItemRepository extends JpaRepository { +} diff --git a/src/main/java/com/example/jpa/orderItem/OrderItem.java b/src/main/java/com/example/jpa/orderItem/OrderItem.java new file mode 100644 index 000000000..556b96429 --- /dev/null +++ b/src/main/java/com/example/jpa/orderItem/OrderItem.java @@ -0,0 +1,45 @@ +package com.example.jpa.orderItem; + +import com.example.jpa.order.Order; +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.util.List; +import java.util.Objects; + +@Entity +@Getter +@Setter +@ToString +@Table(name = "order_item") +public class OrderItem extends BaseEntity{ + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private int price; + private int quantity; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "order_id", referencedColumnName = "id") + private Order order; + + @OneToMany(mappedBy = "orderItem") + private List items; + + public void setOrder(Order order) { + if (Objects.nonNull(this.order)) { + this.order.getOrderItems().remove(this); + } + + this.order = order; + order.getOrderItems().add(this); + } + + public void addItem(Item item) { + item.setOrderItem(this); + } +} diff --git a/src/main/java/com/example/jpa/orderItem/OrderItemRepository.java b/src/main/java/com/example/jpa/orderItem/OrderItemRepository.java new file mode 100644 index 000000000..9dbc1c6b3 --- /dev/null +++ b/src/main/java/com/example/jpa/orderItem/OrderItemRepository.java @@ -0,0 +1,6 @@ +package com.example.jpa.orderItem; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface OrderItemRepository extends JpaRepository { +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml new file mode 100644 index 000000000..7f1327950 --- /dev/null +++ b/src/main/resources/application.yaml @@ -0,0 +1,23 @@ +spring: + datasource: + url: jdbc:mysql://localhost:3300/test + username: root + password: test + driver-class-name: com.mysql.cj.jdbc.Driver + jpa: + hibernate: + ddl-auto: create +--- +spring: + datasource: + url: jdbc:mysql://localhost:3300/test_code + username: root + password: test + driver-class-name: com.mysql.cj.jdbc.Driver + jpa: + hibernate: + ddl-auto: create + show-sql: true + config: + activate: + on-profile: test \ No newline at end of file diff --git a/src/test/java/com/example/jpa/CustomerServiceTest.java b/src/test/java/com/example/jpa/CustomerServiceTest.java new file mode 100644 index 000000000..cb3dd4000 --- /dev/null +++ b/src/test/java/com/example/jpa/CustomerServiceTest.java @@ -0,0 +1,99 @@ +package com.example.jpa; + +import com.example.jpa.customer.CustomerResponseDto; +import com.example.jpa.customer.CustomerSaveRequestDto; +import com.example.jpa.customer.CustomerService; +import com.example.jpa.customer.CustomerUpdateRequestDto; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.Rollback; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.NoSuchElementException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@ActiveProfiles("test") +@SpringBootTest +@Transactional +class CustomerServiceTest { + + @Autowired + CustomerService customerService; + + @Test + @DisplayName("정상적으로 등록되는 경우의 테스트") + @Rollback + public void insertTest() throws Exception { + + //given + CustomerSaveRequestDto requestDto = new CustomerSaveRequestDto("username", "nickname", "password", "name"); + + //when + Long newCustomerId = customerService.saveCustomer(requestDto); + CustomerResponseDto customer = customerService.getCustomer(newCustomerId); + + //then + assertThat(newCustomerId).isEqualTo(customer.customerId()); + } + + + @Test + @DisplayName("정상적으로 모든 데이터를 불러오는 경우의 테스트") + public void getTest() throws Exception { + + //given + CustomerSaveRequestDto requestDto = new CustomerSaveRequestDto("username", "nickname", "password", "name"); + + //when + Long newCustomerId = customerService.saveCustomer(requestDto); + List customers = customerService.getCustomers(); + + //then + assertThat(customers).hasSize(1); + } + + + @Test + @DisplayName("정상적으로 모든 수정하는 경우의 테스트") + public void updateTest() throws Exception { + + //given + CustomerSaveRequestDto requestDto = new CustomerSaveRequestDto("username", "nickname", "password", "name"); + CustomerUpdateRequestDto updateRequestDto = new CustomerUpdateRequestDto("username update", "nickname update", "password update"); + + //when + Long newCustomerId = customerService.saveCustomer(requestDto); + customerService.updateCustomer(updateRequestDto, newCustomerId); + + CustomerResponseDto customer = customerService.getCustomer(newCustomerId); + //then + assertThat(customer.nickname()).isEqualTo(updateRequestDto.nickname()); + assertThat(customer.username()).isEqualTo(updateRequestDto.username()); + + } + + @Test + @DisplayName("정상적으로 삭제하는 경우의 테스트") + public void deleteTest() throws Exception { + + //given + CustomerSaveRequestDto requestDto = new CustomerSaveRequestDto("username", "nickname", "password", "name"); + CustomerUpdateRequestDto updateRequestDto = new CustomerUpdateRequestDto("username update", "nickname update", "password update"); + + //when + Long newCustomerId = customerService.saveCustomer(requestDto); + customerService.deleteCustomer(newCustomerId); + + assertThatThrownBy(() -> customerService.getCustomer(newCustomerId)) + .isInstanceOf(NoSuchElementException.class) + .hasMessage("해당 유저를 찾을 수 없습니다."); + + } + +} \ No newline at end of file diff --git a/src/test/java/com/example/jpa/JpaApplicationTests.java b/src/test/java/com/example/jpa/JpaApplicationTests.java new file mode 100644 index 000000000..0a3b69476 --- /dev/null +++ b/src/test/java/com/example/jpa/JpaApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.jpa; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class JpaApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/src/test/java/com/example/jpa/MappingTest.java b/src/test/java/com/example/jpa/MappingTest.java new file mode 100644 index 000000000..059dc1ecf --- /dev/null +++ b/src/test/java/com/example/jpa/MappingTest.java @@ -0,0 +1,67 @@ +package com.example.jpa; + +import com.example.jpa.customer.Customer; + +import com.example.jpa.orderItem.Item; +import com.example.jpa.orderItem.OrderItem; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.EntityTransaction; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +import java.util.List; + +@SpringBootTest +@ActiveProfiles("test") +public class MappingTest { + + private static final Logger log = LoggerFactory.getLogger(MappingTest.class); + + @Autowired + EntityManagerFactory emf; + + static Customer customer; + + static OrderItem orderItem; + static Item item; + @BeforeEach + void setUp(){ + customer = new Customer(); + customer.setNickname("nickname"); + customer.setName("name"); + customer.setPassword("password"); + + orderItem = new OrderItem(); + + item = new Item(); + item.setPrice(1000); + item.setStockQuantity(10); + item.setItemName("item"); + + } + + @Test + @DisplayName("연관관계 매핑") + void getMapping(){ + EntityManager em = emf.createEntityManager(); + EntityTransaction transaction = em.getTransaction(); + transaction.begin(); + em.persist(orderItem); + em.persist(item); + em.flush(); + em.clear(); + orderItem.setItems(List.of(item)); + log.info("{}", orderItem); + log.info("{}", item); + transaction.commit(); + em.close(); + } + +} diff --git a/src/test/java/com/example/jpa/PersistenceContextTest.java b/src/test/java/com/example/jpa/PersistenceContextTest.java new file mode 100644 index 000000000..3b5540fa0 --- /dev/null +++ b/src/test/java/com/example/jpa/PersistenceContextTest.java @@ -0,0 +1,161 @@ +package com.example.jpa; + +import com.example.jpa.customer.Customer; +import com.example.jpa.customer.CustomerRepository; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.EntityTransaction; + +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + + +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +@SpringBootTest +@ActiveProfiles("test") +public class PersistenceContextTest { + + @Autowired + EntityManagerFactory emf; + + @Autowired + CustomerRepository jpaCustomerRepository; + + static Customer customer; + static Customer customerSaved; + @BeforeEach + void setup(){ + customerSaved = new Customer(); + customerSaved.setName("name saved"); + customerSaved.setPassword("password saved"); + customerSaved.setNickname("nickname saved"); + + jpaCustomerRepository.save(customerSaved); + + + customer = new Customer(); + customer.setName("name"); + customer.setPassword("password"); + customer.setNickname("nickname"); + } + + @AfterEach + void clear(){ + jpaCustomerRepository.deleteAll(); + } + + @Test + @DisplayName("정상적으로 회원을 조회한다.") + void findSuccess(){ + EntityManager em = emf.createEntityManager(); + EntityTransaction transaction = em.getTransaction(); + transaction.begin(); + + List customers = em.createQuery("SELECT c FROM Customer c", Customer.class) + .getResultList(); + transaction.commit(); + em.close(); + + assertThat(customers).hasSize(1); + } + + @Test + @DisplayName("detach로 인한 update 실패") + void updateFailWithDetach(){ + EntityManager em = emf.createEntityManager(); + EntityTransaction transaction = em.getTransaction(); + transaction.begin(); + + em.persist(customer); + em.detach(customer); + customer.setUsername("test update"); + + transaction.commit(); + em.close(); + + List allCustomers = jpaCustomerRepository.findAll(); + assertThat(allCustomers.get(1).getName()).isEqualTo("name"); + } + + @Test + @DisplayName("정상적으로 회원을 저장한다.") + void saveSuccess(){ + EntityManager em = emf.createEntityManager(); + EntityTransaction transaction = em.getTransaction(); + transaction.begin(); + + em.persist(customer); + + transaction.commit(); + em.close(); + + List allCustomers = jpaCustomerRepository.findAll(); + assertThat(allCustomers).hasSize(2); + } + + + + @Test + @DisplayName("정상적으로 회원을 수정한다.(dirtyChecking)") + void updateSuccessWithDirtyChecking(){ + String updateName = "name update"; + + EntityManager em = emf.createEntityManager(); + EntityTransaction transaction = em.getTransaction(); + transaction.begin(); + + em.persist(customer); + customer.setName(updateName); + + transaction.commit(); + em.close(); + + Customer allCustomers = jpaCustomerRepository.findAll().get(1); + assertThat(allCustomers.getName()).isEqualTo(updateName); + } + @Test + @DisplayName("정상적으로 회원을 수정한다.(merge)") + void updateSuccessWithMerge(){ + String updateName = "name update"; + + EntityManager em = emf.createEntityManager(); + EntityTransaction transaction = em.getTransaction(); + transaction.begin(); + + em.persist(customer); + customer.setName(updateName); + em.merge(customer); + + transaction.commit(); + em.close(); + + Customer allCustomers = jpaCustomerRepository.findAll().get(1); + assertThat(allCustomers.getName()).isEqualTo(updateName); + } + @Test + @DisplayName("정상적으로 회원을 삭제한다.") + void deleteSuccess(){ + + Customer allCustomers = jpaCustomerRepository.findAll().get(0); + + + EntityManager em = emf.createEntityManager(); + EntityTransaction transaction = em.getTransaction(); + transaction.begin(); + + Customer customerById = em.find(Customer.class, allCustomers.getId()); + em.remove(customerById); + + transaction.commit(); + em.close(); + + List afterDelete = jpaCustomerRepository.findAll(); + + assertThat(afterDelete).hasSize(0); + } +}