Framework & Library

Spring Boot - JPA를 사용한 데이터 조작

임빈영 2022. 2. 4. 00:00

JPA를 사용한 데이터 조작

✔️ Entity 생성

@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Table(name = "product")
public class ProductEntity {

    @Id
    private String productId;

    private String productName;

    private int productPrice;

    private int productStock;
}

ㆍ DB의 테이블과 직접적으로 매핑되는 클래스이다.

 

애너테이션 설명
@Entity 해당 애너테이션을 클래스에 붙이면 JPA가 해당 클래스를 관리하게 된다.
@Builder 해당 애너테이션을 사용하게 되면 객체를 생성할 때 생성자를 통해서 객체를 생성하는 것이 아니라 Entity이름.builder( ).필드(값).필드(값).build( );를 통해 필드에 대한 값을 좀 더 명시적으로 지정할 수 있다.
@Table 매핑할 테이블을 지정하기 위한 애너테이션이다.
@Id Primary Key를 지정하기 위한 애너테이션이다.

ㆍ JPA 사용 시 위와 같은 여러 가지 애너테이션을 사용하여 테이블, 필드, 필드 옵션 등을 설정할 수 있다.

 

✔️ DTO 생성

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Builder
public class ProductDTO {

    private String productId;

    private String productName;

    private int productPrice;

    private int productStock;
}

ㆍ Service나 Controller에서 DB에 접근할 때 사용하는 클래스이다.

ㆍ 위에서 설명한 Entity와 다른 점은 Entity는 DB 테이블에 대한 정보를 가지고 있는 클래스이고, DTO는 해당 테이블에서 실제로 CRUD를 할 필드를 정의해둔 것이라고 보면 된다.

ㆍ 이렇게 Entity와 DTO를 분리해서 사용하는 이유는 소스코드 작성 중 DB에 접근할 필드의 변경이 생겼을 경우 Entity를 변경하여 DB에 접근하면 DB 테이블의 내용이 변경되어 큰 문제를 일으킬 수 있기 때문이다. 따라서, 테이블에 대한 정보를 작성하는 Entity 클래스와 DB에 접근하는 필드에 대한 DTO 클래스를 분리해서 사용하는 것이다.

 

✔️ Repository 생성

public interface ProductRepository extends JpaRepository<ProductEntity, String> {
}

ㆍ JPA를 사용하게 되면 JpaRepository 인터페이스를 상속받아 제네릭을 통해 관리하고 하는 클래스, Id의 타입을 <ProductEntity, String>와 같이 작성하면 자동으로 DB와 CRUD 연결을 할 수 있는 메서드를 사용할 수 있다.

 

✔️ DAO 생성

public interface ProductDAO {

    ProductEntity saveProduct(ProductEntity productEntity);

    ProductEntity getProduct(String productId);
}

 Service가 DB에 연결할 수 있게 도와주는 역할을 수행하는 DAO 인터페이스를 생성한다.

ㆍ 해당 인터페이스는 상품을 저장하기 위한 saveProduct( ) 메서드와 상품을 조회하는 getProduct( ) 메서드가 정의되어 

 

@Service
public class ProductDAOImpl implements ProductDAO {

    ProductRepository productRepository;

    @Autowired
    public ProductDAOImpl(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }

    @Override
    public ProductEntity saveProduct(ProductEntity productEntity) {
        productRepository.save(productEntity);

        return productEntity;
    }

    @Override
    public ProductEntity getProduct(String productId) {
        ProductEntity productEntity = productRepository.getById(productId);

        return productEntity;
    }
}

ㆍ ProductDAO 인터페이스의 구현체인 ProductDAOImpl 클래스이다.

ㆍ ProductRepository 객체에 대한 의존성을 주입받고 해당 객체를 통해 DB에 접근하는 방식으로 동작한다.

 

✔️ Handler 생성

public interface ProductDataHandler {

    ProductEntity saveProductEntity(String productId, String productName, int productPrice, int productStock);

    ProductEntity getProductEntity(String prodyctId);
}

ㆍ Handler는 필수적으로 구현하는 것이 아닌, 필요에 따라 구현을 하는 영역이다. 데이터를 핸들링할 필요가 있을 때 해당 영역을 구현한다.

ㆍ Handler 영역 역시 인터페이스와 클래스로 분리되어 있다.

ㆍ 해당 인터페이스는 상품을 저장하기 위한 saveProductEntity( ) 메서드와 상품을 조회하는 getProductEntity( ) 메서드가 정의되어 있다.

 

@Service
@Transactional
public class ProductDataHandlerImpl implements ProductDataHandler {

    ProductDAO productDAO;

    @Autowired
    public ProductDataHandlerImpl(ProductDAO productDAO) {
        this.productDAO = productDAO;
    }

    @Override
    public ProductEntity saveProductEntity(String productId, String productName, int productPrice, int productStock) {
        ProductEntity productEntity = new ProductEntity(productId, productName, productPrice, productStock);

        return productDAO.saveProduct(productEntity);
    }

    @Override
    public ProductEntity getProductEntity(String productId) {
        return productDAO.getProduct(productId);
    }
}

ㆍ ProductDataHandler 인터페이스의 구현체인 ProductDataHandlerImpl 클래스이다.

ㆍ ProductDAO 객체에 대한 의존성을 주입받고 해당 객체를 통해 DB에 접근하는 방식으로 동작한다.

 

✔️ Service 생성

public interface ProductService {

    ProductDTO saveProduct(String productId, String productName, int productPrice, int productStock);

    ProductDTO getProduct(String productId);
}

ㆍ Service 영역 역시 인터페이스와 클래스로 분리되어 있다.

ㆍ 해당 인터페이스는 상품을 저장하기 위한 saveProduct( ) 메서드와 상품을 조회하는 getProduct( ) 메서드가 정의되어 있다.

 

@Service
public class ProductServiceImpl implements ProductService {

    ProductDataHandler productDataHandler;

    @Autowired
    public ProductServiceImpl(ProductDataHandler productDataHandler) {
        this.productDataHandler = productDataHandler;
    }

    @Override
    public ProductDTO saveProduct(String productId, String productName, int productPrice, int productStock) {

        ProductEntity productEntity = productDataHandler.saveProductEntity(productId, productName, productPrice, productStock);

        ProductDTO productDTO = new ProductDTO(productEntity.getProductId(), productEntity.getProductName(), productEntity.getProductPrice(), productEntity.getProductStock());

        return productDTO;
    }

    @Override
    public ProductDTO getProduct(String productId) {

        ProductEntity productEntity = productDataHandler.getProductEntity(productId);

        ProductDTO productDTO = new ProductDTO(productEntity.getProductId(), productEntity.getProductName(), productEntity.getProductPrice(), productEntity.getProductStock());

        return productDTO;
    }
}

ㆍ ProductService 인터페이스의 구현체인 ProductServiceImpl 클래스이다.

ㆍ 인터페이스에서 정의된 두 메서드의 실질적인 로직을 구현한 클래스이다.

ㆍ ProductDataHandler 객체에 대한 의존성을 주입받고 해당 객체를 통해 데이터를 조작하는 방식으로 동작한다.

 

✔️ Controller 생성

@RestController
@RequestMapping("/api/v1/product-api")
public class ProductController {

    private ProductService productService;

    @Autowired
    ProductController(ProductService productService) {
        this.productService = productService;
    }

    @GetMapping(value = "/product/{productId}")
    public ProductDTO getProduct(@PathVariable String productId) {
        return productService.getProduct(productId);
    }

    @PostMapping(value = "/product")
    public ProductDTO createProduct(@RequestBody ProductDTO productDTO) {
        String productId = productDTO.getProductId();
        String productName = productDTO.getProductName();
        int productPrice = productDTO.getProductPrice();
        int productStock = productDTO.getProductStock();

        return productService.saveProduct(productId, productName, productPrice, productStock);
    }

}

ㆍ 사용자로부터 요청을 받고 그에 대한 응답을 해주는 클래스이다.

ㆍ ProductService 객체에 대한 의존성을 주입받게 된다.

ㆍ Controller 클래스로 요청이 들어오게 되면, 각 요청에 매핑된 메서드들이 호출되고 해당 메서드 내의 Service 객체의 비즈니스 로직들이 동작하는 방식이다.


테스트

✔️ 상품 저장 테스트

ㆍ 컨트롤러와 매핑된 URL로 상품 정보를 http body에 담아 상품 저장 요청을 수행한다.

ㆍ 요청에 대한 응답이 정확하게 출력되는 것을 확인할 수 있다.

 

ㆍ DB 확인 결과 요청한 정보가 정확하게 저장된 것을 확인할 수 있다.

 

✔️ 상품 조회 테스트

ㆍ 기존에 저장된 상품을 조회하기 위해 서버로 요청을 보낸다.

ㆍ 요청한 상품정보가 정확하게 출력되는 것을 확인할 수 있다.


 

GitHub - qlsdud0604/spring-boot-study

Contribute to qlsdud0604/spring-boot-study development by creating an account on GitHub.

github.com

 

728x90