Spring Boot - JPA를 사용한 데이터 조작
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