| 종류 | 환경 |
|---|---|
| IDE | Intellij IDEA 2023.1.2 (Ultimate Edition) |
| 언어 | SpringBoot 3.1.0 |
| 타입 | Gradle - Groovy |
| JDK | corretto-17 |
| 패키지 생성 | Jar |
| 버전관리 | Github |
| 종류 | 이름 |
|---|---|
| Web | Spring web |
| Developer Tools | Spring Processor / Lombok |
| SQL | Spring Data JPA / MySQL Driver |
| 기타 | OpenAPI:2.0.2 |
엔터티(Entity)란 데이터 모델링에서 사용되는 객체


참조 : 관계형 데이터베이스란?
https://computer-science-student.tistory.com/194
| 상품 테이블 | |
|---|---|
| 상품 번호 (기본키) | int |
| 상품 이름 | varchar |
| 상품 가격 | int |
| 상품 재고 | int |
| 상품 생성 일자 | DateTime |
| 상품 정보 변경 일자 | DateTime |
package com.springboot.hello.data.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Entity // 해당 클래스가 엔티티임을 명시(* 필수)
@Table(name = "product") //DB Table 이름 지정(생략 가능, 생략시 클래스 이름으로 생성)
@Getter //Lombok Getter 메소드 자동 생성(편의성)
@Setter //Lombok Setter 메소드 자동 생성(편의성)
public class Product {
@Id //테이블 기본키 지정
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long number;
@Column(nullable = false) //테이블 컬럼 지정(별다른 설정 없을 시 생략 가능)
private String name;
@Column(nullable = false)
private Integer price;
@Column(nullable = false)
private Integer stock;
//@Transient //데이터베이스에서 필요 없을 경우 사용하는 어노테이션
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
Repository 인터페이스에 정해진 규칙대로 메소드를 입력하면, Spring에서 자동으로 해당 메소드 이름에 적합한 쿼리를 날리는 구현체를 만들어 Bean으로 등록

package com.springboot.hello.data.repository;
import com.springboot.hello.data.entity.Product;
import org.springframework.data.jpa.repository.JpaRepository;
//ProductRepository가 JpaRepository를 상속받을 때는 대상 엔티티(Product)와 기본키 필드타입(Long number)을 지정해야함
public interface ProductRepository extends JpaRepository<Product, Long> {
}
DAO는 Data Access Object의 약자로 DB의 data에 접근하기 위한 객체
참조 : JPA 영속성이란?
https://velog.io/@sooyoungh/%EC%98%81%EC%86%8D%EC%84%B1%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C
package com.springboot.hello.data.dao;
import com.springboot.hello.data.entity.Product;
//interface = 설계도
//implement = 구현체(설계도를 따라서)
public interface ProductDAO {
Product insertProduct(Product product);
Product selectProduct(Long number);
Product updateProductName(Long number, String name) throws Exception;
void deleteProduct(Long number) throws Exception;
}
package com.springboot.hello.data.dao.impl;
import com.springboot.hello.data.dao.ProductDAO;
import com.springboot.hello.data.entity.Product;
import com.springboot.hello.data.repository.ProductRepository;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.Optional;
//interface = 설계도
//implement = 구현체(설계도를 따라서)
@Component //스프링이 관리하는 빈으로 등록
public class ProductDAOImpl implements ProductDAO {
private final ProductRepository productRepository;
public ProductDAOImpl(ProductRepository productRepository) {
this.productRepository = productRepository;
}
@Override
public Product insertProduct(Product product) {
return productRepository.save(product);
}
@Override
public Product selectProduct(Long number) {
return productRepository.getReferenceById(number);
}
// JPA에 update라는 키워드는 없음
// find() 메서드를 통해 데이터베이스에서 값을 가져오고
// 객체의 값을 변경한 다음 ** save()를 실행하면 **
// JPA에서 더티 체크(Dirty Check)라는 변경감지를 수행함.
// @Transactional 어노테이션이 지정되어 있으면(save 메소드안에 있음) 메소드 내 자동으로 flush() 메소드를 실행
// 이 과정에서 변경이 감지되면 대상 객체에 해당하는 데이터베이스의 레코드를 업데이트하는 쿼리가 실행됨
@Override
public Product updateProductName(Long number, String name) throws Exception {
Optional<Product> selectedProduct = productRepository.findById(number);
Product updatedProduct;
if (selectedProduct.isPresent()) {
Product product = selectedProduct.get();
product.setName(name);
product.setUpdatedAt(LocalDateTime.now());
updatedProduct = productRepository.save(product);
} else {
throw new Exception();
}
return updatedProduct;
}
@Override
public void deleteProduct(Long number) throws Exception {
Optional<Product> selectedProduct = productRepository.findById(number);
if (selectedProduct.isPresent()) {
Product product = selectedProduct.get();
productRepository.delete(product);
} else {
throw new Exception();
}
}
}
package com.springboot.hello.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
@AllArgsConstructor //Lombok 모든 인수를 받는 생성자 자동 생성
@Getter //Lombok Getter 클래스 자동 생성
@Setter //Lombok Setter 클래스 자동 생성
public class ProductDto {
private String name;
private int price;
private int stock;
}
package com.springboot.hello.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public class ChangeProductNameDto {
private Long number;
private String name;
}
package com.springboot.hello.dto;
import lombok.*;
@Builder //Lombok Builder 패턴 메서드 자동 생성
@Getter //Lombok Getter 클래스 자동 생성
@Setter //Lombok Setter 클래스 자동 생성
public class ProductResponseDto {
private Long number;
private String name;
private int price;
private int stock;
}
package com.springboot.hello.service;
import com.springboot.hello.dto.ProductDto;
import com.springboot.hello.dto.ProductResponseDto;
public interface ProductService {
ProductResponseDto getProduct(Long number);
ProductResponseDto saveProduct(ProductDto productDto);
ProductResponseDto changeProductName(Long number, String name) throws Exception;
void deleteProduct(Long number) throws Exception;
}
package com.springboot.hello.service.impl;
import com.springboot.hello.data.dao.ProductDAO;
import com.springboot.hello.data.entity.Product;
import com.springboot.hello.dto.ProductDto;
import com.springboot.hello.dto.ProductResponseDto;
import com.springboot.hello.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@Service //스프링이 관리하는 빈 등록 어노테이션
public class ProductServiceImpl implements ProductService {
private final ProductDAO productDAO;
@Autowired
public ProductServiceImpl(ProductDAO productDAO) {
this.productDAO = productDAO;
}
@Override
public ProductResponseDto getProduct(Long number) {
Product product = productDAO.selectProduct(number);
return ProductResponseDto.builder()
.number(product.getNumber())
.name(product.getName())
.price(product.getPrice())
.stock(product.getStock())
.build();
}
@Override
public ProductResponseDto saveProduct(ProductDto productDto) {
Product product = new Product();
product.setName(productDto.getName());
product.setPrice(productDto.getPrice());
product.setStock(productDto.getStock());
product.setCreatedAt(LocalDateTime.now());
product.setUpdatedAt(LocalDateTime.now());
Product savedProduct = productDAO.insertProduct(product);
return ProductResponseDto.builder()
.number(savedProduct.getNumber())
.name(savedProduct.getName())
.price(savedProduct.getPrice())
.stock(savedProduct.getStock())
.build();
}
@Override
public ProductResponseDto changeProductName(Long number, String name) throws Exception {
Product changedProduct = productDAO.updateProductName(number, name);
return ProductResponseDto.builder()
.number(changedProduct.getNumber())
.name(changedProduct.getName())
.price(changedProduct.getPrice())
.stock(changedProduct.getStock())
.build();
}
@Override
public void deleteProduct(Long number) throws Exception {
productDAO.deleteProduct(number);
}
}
package com.springboot.hello.controller;
import com.springboot.hello.dto.ChangeProductNameDto;
import com.springboot.hello.dto.ProductDto;
import com.springboot.hello.dto.ProductResponseDto;
import com.springboot.hello.service.ProductService;
import io.swagger.v3.oas.annotations.Parameter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/product")
public class ProductController {
private final ProductService productService;
@Autowired
public ProductController(ProductService productService) {
this.productService = productService;
}
@GetMapping
public ResponseEntity<ProductResponseDto> getProduct(Long number) {
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<ProductResponseDto> changeProductName(
@RequestBody ChangeProductNameDto changeProductNameDto
) throws Exception {
ProductResponseDto productResponseDto = productService.changeProductName(changeProductNameDto.getNumber(), changeProductNameDto.getName());
return ResponseEntity.status(HttpStatus.OK).body(productResponseDto);
}
@DeleteMapping
public ResponseEntity<String> deleteProduct(Long number) throws Exception {
productService.deleteProduct(number);
return ResponseEntity.status(HttpStatus.OK).body("정상적으로 삭제되었습니다.");
}
}
http://localhost:8080/swagger-ui/index.html




출처 : https://dahye-jeong.gitbook.io/spring/spring/2020-04-11-jpa-basic