


<configuration>
<property name="LOG_PATH" value="./logs"/>
<!-- Appenders -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%thread] %logger %msg%n</pattern>
</encoder>
</appender>
<appender name="INFO_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<file>${LOG_PATH}/info.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/info_${type}.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%thread] %logger %msg%n</pattern>
</encoder>
</appender>
<!-- TRACE > DEBUG > INFO > WARN > ERROR > OFF -->
<!-- Root Logger -->
<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="INFO_LOG"/>
</root>
</configuration>
spring.application.name=DBTest
spring.datasource.driverClassName=org.mariadb.jdbc.Driver
spring.datasource.url=jdbc:mariadb://localhost:3306/springboot
spring.datasource.username=root
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
DB 테이블 설계 - @Entity
- 스프링 프레임웍 구동할때 @Entity속성의 객체를 로드해서 DB 테이블로 생성해 준다
물리적인 DB에 데이터를 조작할수 있는 스프링에서 제공하는 기능
- DB를 좀더 체계적으로 조작 - ORM(MyBatis, JPA....) - JPA
- JPA를 사용하려면 JpaRepository 인터페이스를 상속받은 인터페이스가 필요
---------- DB를 조작할수 있는 인터페이스를 제공 ------------------
실제 DB조작이 이루어지는 DAO ( 복잡한(비지니스로직) )
DAO 로직구현할때 필요한게--> 데이터인데.. 데이터를 체계적으로 관리하기 위해서 객체형태로..--> DTO
DTO는 데이터로만 구성된 클래스
-- 필요한 패키지
- dao
- dto
- entity
- repository


[DB테이블이 생성 됨]
package com.example.jpa.data.entity;
import jakarta.persistence.*;
import org.hibernate.annotations.CreationTimestamp;
import java.time.LocalDateTime;
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long number;
@Column(nullable = false, unique = true)
private String name;
@Column(nullable = false)
private int price;
@Column(nullable = false)
private int stock;
@CreationTimestamp
@Column(updatable = false)
private LocalDateTime createAt;
private LocalDateTime modifyAt;
}
JpaApplication 실행(run)하면
MariaDB에 product 테이블 생성된다.

package com.example.jpa.data.repository;
import com.example.jpa.data.entity.Product;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ProductRepository extends JpaRepository<Product, Long> {
}
import java.time.LocalDateTime;
@Getter
@Setter
@ToString
@AllArgsConstructor
public class ProductDto {
private String name;
private int price;
private int stock;
}
package com.example.jpa.data.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@AllArgsConstructor
public class ProductResponseDto {
private Long number;
private String name;
private int price;
private int stock;
}
package com.example.jpa.data.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@AllArgsConstructor
public class ChangeProductNameDto {
private Long number;
private String name;
}
package com.example.jpa.data.dao;
import com.example.jpa.data.dto.ProductDto;
import com.example.jpa.data.entity.Product;
/*
* C : 생성
* R : 조회
* U : 업데이트
* D : 삭제
* */
public interface ProductDao {
Product insertProduct(Product product);
Product selectProduct(long number) throws Exception;
Product updateProductName(long number, String name) throws Exception;
void deleteProduct(long number) throws Exception;
}
package com.example.jpa.data.dao.impl;
import com.example.jpa.data.dao.ProductDao;
import com.example.jpa.data.entity.Product;
import com.example.jpa.data.repository.ProductRepository;
import lombok.AllArgsConstructor;
import java.time.LocalDateTime;
import java.util.Optional;
@AllArgsConstructor
@Component
public class ProductDaoImpl implements ProductDao {
private final ProductRepository productRepository;
@Override
public Product insertProduct(Product product) {
return productRepository.save(product);
}
@Override
public Product selectProduct(long number) throws Exception {
Optional<Product> selectProduct = productRepository.findById(number);
if (selectProduct.isPresent()){
return selectProduct.get();
}
throw new Exception();
}
// 기존데이터를 가져와서 값을 변경하고 save한다
@Override
public Product updateProductName(long number, String name) throws Exception {
Product product = this.selectProduct(number);
if(product != null){
product.setName(name);
product.setModifyAt(LocalDateTime.now());
productRepository.save(product);
}else{
throw new Exception();
}
return product;
}
@Override
public void deleteProduct(long number) throws Exception {
Product product = this.selectProduct(number);
productRepository.delete(product);
}
}
package com.example.jpa.service;
import com.example.jpa.data.dto.ProductDto;
import com.example.jpa.data.dto.ProductResponseDto;
/*
실제 사용자로부터 데이터를 받고 전달하는 역할
DAO에서는 Entity타입의 데이터를 사용했다면
여기서는 상황에 맞는 데이터 객체 사용
*/
public interface ProductService {
ProductResponseDto saveProduct(ProductDto productDto);
ProductResponseDto getProduct(long number);
ProductResponseDto changeProductName(long number, String name);
void deleteProduct(long number);
}
package com.example.jpa.service.impl;
import com.example.jpa.data.dao.ProductDao;
import com.example.jpa.data.dto.ProductDto;
import com.example.jpa.data.dto.ProductResponseDto;
import com.example.jpa.data.entity.Product;
import com.example.jpa.service.ProductService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
@AllArgsConstructor
@Service
public class ProductServiceImpl implements ProductService {
private final ProductDao productDao;
@Override
public ProductResponseDto saveProduct(ProductDto productDto) {
Product product = new Product();
product.setName(productDto.getName());
product.setPrice(productDto.getPrice());
product.setStock(productDto.getStock());
Product insertedProduct = productDao.insertProduct(product);
ProductResponseDto productResponseDto
= new ProductResponseDto(insertedProduct.getNumber(), insertedProduct.getName(),
insertedProduct.getPrice(), insertedProduct.getStock());
return productResponseDto;
}
@Override
public ProductResponseDto getProduct(long number) throws Exception {
Product product = productDao.selectProduct(number);
ProductResponseDto productResponseDto
= new ProductResponseDto(product.getNumber(), product.getName(),
product.getPrice(), product.getStock());
return productResponseDto;
}
@Override
public ProductResponseDto changeProductName(long number, String name) throws Exception {
Product product = productDao.updateProductName(number, name);
ProductResponseDto productResponseDto
= new ProductResponseDto(product.getNumber(), product.getName(),
product.getPrice(), product.getStock());
return productResponseDto;
}
@Override
public void deleteProduct(long number) throws Exception {
productDao.deleteProduct(number);
}
}
package com.example.jpa.controller;
import com.example.jpa.data.dto.ChangeProductNameDto;
import com.example.jpa.data.dto.ProductDto;
import com.example.jpa.data.dto.ProductResponseDto;
import com.example.jpa.service.ProductService;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/product")
@AllArgsConstructor
public class ProductController {
private final ProductService productService;
@SneakyThrows
@GetMapping
public ResponseEntity<ProductResponseDto> getProduct(long number) {
ProductResponseDto productResponseDto = productResponseDto = productService.getProduct(number);
return ResponseEntity.status(HttpStatus.OK).body(productResponseDto);
}
@PostMapping
public ResponseEntity<ProductResponseDto> createProduct(
@RequestBody ProductDto productDto
) {
ProductResponseDto productResponseDto = productService.saveProduct(productDto);
return ResponseEntity.status(HttpStatus.OK).body(productResponseDto);
}
@PutMapping
public ResponseEntity<String> changeProductName(
@RequestBody ChangeProductNameDto changeProductNameDto
) {
try {
ProductResponseDto productResponseDto
= productService.changeProductName(changeProductNameDto.getNumber(), changeProductNameDto.getName());
return ResponseEntity.status(HttpStatus.OK).body(productResponseDto.toString());
}catch (Exception e){
return ResponseEntity.status(HttpStatus.OK).body("업데이트도중 에러가 발생했습니다.");
}
}
@SneakyThrows
@DeleteMapping
public ResponseEntity<String> deleteProduct(long number) {
productService.deleteProduct(number);
return ResponseEntity.status(HttpStatus.OK).body("정상적으로 삭제 되었습니다.");
}
}
SpringDoc OpenAPI Starter WebMVC UI
<!-- https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.5.0</version>
</dependency>
spring.application.name=jpa
spring.datasource.driverClassName=org.mariadb.jdbc.Driver
spring.datasource.url=jdbc:mariadb://localhost:3306/springboot
spring.datasource.username=root
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
# swagger-ui custom path
springdoc.swagger-ui.path=/swagger-ui.html
package com.example.jpa.config;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
@Configuration
@OpenAPIDefinition
@RequiredArgsConstructor
public class SwaggerConfiguration {
}

