JPA 기본 CRUD

코딩하는 하늘토끼·2023년 6월 21일

스프링부트 공부

목록 보기
10/15

프로젝트 환경

종류환경
IDEIntellij IDEA 2023.1.2 (Ultimate Edition)
언어SpringBoot 3.1.0
타입Gradle - Groovy
JDKcorretto-17
패키지 생성Jar
버전관리Github

의존성

종류이름
WebSpring web
Developer ToolsSpring Processor / Lombok
SQLSpring Data JPA / MySQL Driver
기타OpenAPI:2.0.2

목차

  1. 엔티티 설계
  2. 리포지토리 인터페이스 설계
  3. DAO 설계
  4. Dto 설계
  5. 서비스 설계
  6. 컨트롤러 설계
  7. 실행 결과

1.엔티티 설계

엔터티(Entity)란 데이터 모델링에서 사용되는 객체

참조 : 관계형 데이터베이스란?
https://computer-science-student.tistory.com/194

테이블 표

상품 테이블
상품 번호 (기본키)int
상품 이름varchar
상품 가격int
상품 재고int
상품 생성 일자DateTime
상품 정보 변경 일자DateTime

코드

data/entity/Product.java

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;

}

2.리포지토리 인터페이스 설계

Repository 인터페이스에 정해진 규칙대로 메소드를 입력하면, Spring에서 자동으로 해당 메소드 이름에 적합한 쿼리를 날리는 구현체를 만들어 Bean으로 등록

코드

data/repository/ProductRepository.java

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> {
    
}

3.DAO 설계

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

코드

data/dao/ProductDAO.java

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;

}

data/dao/impl/ProductDAOImpl

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();
        }

    }
}

4.Dto 객체 설계

Browser -> Service Dto 객체 코드

data/dto/ProductDto.java

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;

}

data/dto/ChangeProductNameDto.java

package com.springboot.hello.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public class ChangeProductNameDto {

    private Long number;
    private String name;

}

Brower <- Service Dto 객체 코드

data/dto/ProductResponseDto.java

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;

}

5.서비스 설계

코드

service/ProductService.java

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;

}

service/impl/ProductServiceImpl.java

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);
    }
}

6.컨트롤러 설계

코드

controller/ProductController.java

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("정상적으로 삭제되었습니다.");
    }

}

7.실행 결과

http://localhost:8080/swagger-ui/index.html

Create (Post)

Read (Get)

Update (Put)

Delete (Delete)


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

0개의 댓글