세 줄 요약
- DB에서 조회할 데이터가 있는 경우 BeanPropertyRowMapper를 통해 받아온다.
- query() 메소드는 List 타입을 리턴하고, queryForObject() 메소드는 객체를 리턴한다.
- MapSqlParameterSource를 통해 쿼리 파라미터를 Key-Value 형식으로 매핑할 수 있다.
기능 구현 (상품 조회)
해당 게시글에서 이어집니다.
GetMapping
조회(Read)시에 HTTP 요청은 GET 메소드를 통해 진행됩니다.
query( ) / queryForObject( )
CRUD 중에서 Read를 위한 메소드입니다.
query( )는 List 형태로 리턴하고, queryForObject( )는 객체를 리턴합니다.
update() 메소드와 마찬가지로 모두 DataAccessException 예외를 던집니다.
MapSqlParameterSource
BeanPropertySqlParameterSource는 객체 자체를 매핑했다면 MapSqlParameterSource는 Key-Value 형태로 매핑합니다.
생성자의 첫 번째 인자가 Key가 되고, 두 번째 인자가 Value가 됩니다.
BeanPropertyRowMapper
DB에서 조회된 정보를 토대로 인자로 넘겨주는 클래스의 인스턴스를 생성합니다.
인자로 넘겨주는 클래스는 다음과 같은 이유로 해당 조건을 만족해야 합니다.
인자가 없는 생성자를 통해 인스턴스를 생성 → 인자 없는 생성자가 반드시 필요하다.
생성된 인스턴스는 setter로 필드를 초기화 → setter가 반드시 필요하다.
상품조회 메소드
1) findById : id를 기준으로 특정 상품의 정보 조회
→ id만 매핑하기 위해 BeanPropertySqlParameterSource 대신 MapSqlParameterSource 사용
public Product findById(Long id) {
SqlParameterSource namedParameter = new MapSqlParameterSource("id", id);
Product product = null;
try {
// Product 클래스에는 인자 없는 생성자와 setter가 있어야 함
product = namedParameterJdbcTemplate.queryForObject(
"SELECT * FROM products WHERE id = :id",
namedParameter,
new BeanPropertyRowMapper<>(Product.class)
);
} catch(EmptyResultDataAccessException ex) {
// 이전에 만들어 둔 전역 예외 핸들러를 통해 예외처리
throw new EntityNotFoundException("Product를 찾지 못했습니다");
}
return product;
}
2) findAll : 모든 상품의 정보 조회
→ query() 메소드를 통해 List 형태로 받아오며, 매핑시킬 쿼리 파라미터가 없는 것이 특징
public List<Product> findAll() {
List<Product> products = namedParameterJdbcTemplate.query(
"SELECT * FROM products",
new BeanPropertyRowMapper<>(Product.class)
);
return products;
}
3) findByNameContaining : name을 기준으로 특정 상품의 정보 조회
→ name만 매핑하기 위해 BeanPropertySqlParameterSource 대신 MapSqlParameterSource 사용
public List<Product> findByNameContaining(String name) {
// 와일드 카드인 LIKE를 통해 조회할 땐 '%' 기호를 붙인다.
// %name% : name 앞뒤로 어떤 문자가 와도 상관 없다는 의미
SqlParameterSource namedParameter = new MapSqlParameterSource("name", "%" + name + "%");
List<Product> products = namedParameterJdbcTemplate.query(
"SELECT * FROM products WHERE name LIKE :name",
namedParameter,
new BeanPropertyRowMapper<>(Product.class)
);
return products;
}
Controller & Service
Controller
@GetMapping("/products/{id}")
public ProductDTO findProductById(@PathVariable Long id) {
return productService.findById(id);
}
// findAll() 메소드와 findByNameContaining() 메소드를 하나로 구성
// 넘겨주는 매개변수가 없으면 findAll() 리턴, 있으면 findByNameContaining() 리턴
@GetMapping("/products")
public List<ProductDTO> findProducts(@RequestParam(required = false) String name) {
if(name == null) return productService.findAll();
return productService.findByNameContaining(name);
}
Service
public ProductDTO findById(Long id) {
Product product = productRepository.findById(id);
ProductDTO productDTO = ProductDTO.toDTO(product);
return productDTO;
}
public List<ProductDTO> findAll() {
List<Product> products = productRepository.findAll();
List<ProductDTO> productDTOs = products.stream()
.map(product -> ProductDTO.toDTO(product))
.toList();
return productDTOs;
}
public List<ProductDTO> findByNameContaining(String name) {
List<Product> products = productRepository.findByNameContaining(name);
List<ProductDTO> productDTOs = products.stream()
.map(product -> ProductDTO.toDTO(product))
.toList();
return productDTOs;
}
Postman 전송 결과
1) id를 통한 조회
2) name 파라미터 없이 조회 (전체 목록)
3) name 파라미터를 통한 조회
기능 구현 (상품 수정)
PutMapping
Create와 마찬가지로 Update또한 JdbcTemplate 메소드 중 update() 메소드를 사용합니다.
하지만 HTTP 요청은 PUT 메소드를 통해 진행됩니다.
ProductDatabaseRepository
public Product update(Product product) {
SqlParameterSource namedParameter = new BeanPropertySqlParameterSource(product);
namedParameterJdbcTemplate.update(
"UPDATE products SET name = :name, price = :price, amount = :amount WHERE id = :id",
namedParameter);
return product;
}
Controller & Service
Controller
@PutMapping("/products/{id}") // PUT 방식을 통한 HTTP 요청
public ProductDTO updateProduct(@PathVariable Long id, @RequestBody ProductDTO productDTO) {
productDTO.setId(id); // 요청 본문에는 id값이 없으므로 PathVariable의 id값을 DTO에 따로 set해준다.
return productService.update(productDTO);
}
Service
public ProductDTO update(ProductDTO productDTO) {
Product product = ProductDTO.toEntity(productDTO);
Product updatedProduct = productRepository.update(product);
ProductDTO updatedProductDTO = ProductDTO.toDTO(updatedProduct);
return updatedProductDTO;
}
Postman 전송 결과
기능 구현 (상품 삭제)
DeleteMapping
Create, Update와 마찬가지로 Delete또한 JdbcTemplate 메소드 중 update() 메소드를 사용합니다.
HTTP 요청은 DELETE 메소드를 통해 진행됩니다.
ProductDatabaseRepository
public void delete(Long id) {
SqlParameterSource namedParameter = new MapSqlParameterSource("id", id);
namedParameterJdbcTemplate.update(
"DELETE FROM products WHERE id = :id",
namedParameter);
}
Controller & Service
Controller
@DeleteMapping("/products/{id}")
public void deleteProduct(@PathVariable Long id) {
productService.delete(id);
}
Service
public void delete(Long id) {
productRepository.delete(id);
}
Postman 전송 결과
'Spring-Java > Spring' 카테고리의 다른 글
통합 테스트(@SpringBootTest) (0) | 2024.05.24 |
---|---|
인터페이스에 의존하는 Repository (0) | 2024.05.09 |
DB를 사용하는 Repository 구성 1 (0) | 2024.05.03 |
전역 예외 핸들러와 예외 처리 전략 (@ControllerAdvice, @ExceptionHandler) (1) | 2024.04.26 |
@Valid, @Validated (0) | 2024.04.25 |