본문 바로가기
Spring-Java/Spring

DB를 사용하는 Repository 구성 2

by 현대타운301 2024. 5. 8.

 

세 줄 요약

- DB에서 조회할 데이터가 있는 경우 BeanPropertyRowMapper를 통해 받아온다.

- query() 메소드는 List 타입을 리턴하고, queryForObject() 메소드는 객체를 리턴한다.

- MapSqlParameterSource를 통해 쿼리 파라미터를 Key-Value 형식으로 매핑할 수 있다.

 


 

기능 구현 (상품 조회)

 

해당 게시글에서 이어집니다.

 

GetMapping

조회(Read)시에 HTTP 요청은 GET 메소드를 통해 진행됩니다.

 

 

query( ) / queryForObject( )

CRUD 중에서 Read를 위한 메소드입니다.

query( )는 List 형태로 리턴하고, queryForObject( )는 객체를 리턴합니다.

update() 메소드와 마찬가지로 모두 DataAccessException 예외를 던집니다.

List 타입의 query
객체 타입의 queryForObject

 

 

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 전송 결과