FruitController
package com.group.libraryapp.controller.fruit;
import com.group.libraryapp.dto.fruit.request.FruitCreateRequest;
import com.group.libraryapp.dto.fruit.request.FruitUpdateRequest;
import com.group.libraryapp.dto.fruit.response.FruitResponse;
import com.group.libraryapp.service.fruit.FruitService;
import org.springframework.web.bind.annotation.*;
@RestController
public class FruitController {
private final FruitService fruitService;
public FruitController(FruitService fruitService) {
this.fruitService = fruitService;
}
@PostMapping("/api/v1/friut")
public void saveFruit(@RequestBody FruitCreateRequest request){
fruitService.saveFruit(request);
}
@PutMapping("/api/v1/friut")
public void updateFruit(@RequestBody FruitUpdateRequest request){
fruitService.updateFruit(request);
}
@GetMapping("/api/v1/friut/stat")
public FruitResponse statFruit(@RequestParam String name){
return fruitService.statFruit(name);
}
}
FruitService
package com.group.libraryapp.service.fruit;
import com.group.libraryapp.dto.fruit.request.FruitCreateRequest;
import com.group.libraryapp.dto.fruit.request.FruitUpdateRequest;
import com.group.libraryapp.dto.fruit.response.FruitResponse;
import com.group.libraryapp.repository.fruit.FruitRepository;
import org.springframework.stereotype.Service;
@Service
public class FruitService {
private final FruitRepository fruitRepository;
public FruitService(FruitRepository fruitRepository) {
this.fruitRepository = fruitRepository;
}
public void saveFruit(FruitCreateRequest request){
fruitRepository.saveFruits(request.getName(), request.getWarehousingDate(), request.getPrice());
}
public void updateFruit(FruitUpdateRequest request){
if (fruitRepository.isFruitNotExist(request.getId())) {
throw new IllegalArgumentException("Fruit with id " + request.getId() + " does not exist");
}
}
public FruitResponse statFruit(String name){
if (fruitRepository.isFruitNotExist(name)) {
throw new IllegalArgumentException("Fruit with name " + name + " does not exist");
}
return fruitRepository.statFruit(name);
}
}
FruitRepository
package com.group.libraryapp.repository.fruit;
import com.group.libraryapp.dto.fruit.response.FruitResponse;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.time.LocalDate;
@Repository
public class FruitRepository {
private final JdbcTemplate jdbcTemplate;
public FruitRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void saveFruits(String name, LocalDate warehousingDate,long price) {
String sql = "INSERT INTO fruits (name, warehousingDate, price) VALUES (?, ?, ?)";
jdbcTemplate.update(sql,name,warehousingDate,price);
}
public boolean isFruitNotExist(long id) {
String readSql = "SELECT * FROM fruits WHERE id = ?";
return jdbcTemplate.query(readSql, (rs, rowNum) -> 0, id).isEmpty();
}
public void updateFruits(long fruitId) {
String sql = "UPDATE fruits SET isSold = 1 WHERE id = ?";
jdbcTemplate.update(sql,fruitId);
}
public boolean isFruitNotExist(String name) {
String readSql = "SELECT * FROM user WHERE name = ?";
return jdbcTemplate.query(readSql, (rs, rowNum) -> 0, name).isEmpty();
}
public FruitResponse statFruit(String name) {
String salesSql = "SELECT sum(price) FROM fruits WHERE isSold = true GROUP BY name HAVING name = ?";
String notSalesSql = "SELECT sum(price) FROM fruits WHERE isSold = false GROUP BY name HAVING name = ?";
long salesAmount = jdbcTemplate.queryForObject(salesSql, Long.class, name);
long notSalesAmount = jdbcTemplate.queryForObject(notSalesSql, Long.class, name);
return new FruitResponse(salesAmount,notSalesAmount);
}
}
=> repository 분리하기
interface를 활용한다!!
FruitRepository
public interface FruitRepository {
void saveFruit(String name, LocalDate warehousingDate, long price);
void updateFruit(long id);
FruitAmountResponse statFruit(String name);
boolean isFruitNotExist(long id);
boolean isFruitNotExist(String name);
}
FruitMemoryRepository
@Primary
@Repository
public class FruitMemoryRepository implements FruitRepository {
private final JdbcTemplate jdbcTemplate;
public FruitMemoryRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public void saveFruit(String name, LocalDate warehousingDate, long price) {
String sql = "INSERT INTO fruit (name, warehousingDate, price) VALUES (?, ?, ?)";
jdbcTemplate.update(sql, name, warehousingDate, price);
}
@Override
public boolean isFruitNotExist(long id) {
String readSql = "SELECT * FROM fruit WHERE id = ?";
return jdbcTemplate.query(readSql, (rs, rowNum) -> 0, id).isEmpty();
}
@Override
public void updateFruit(long id) {
String updateSql = "UPDATE fruit SET is_sold = true WHERE id = ?";
jdbcTemplate.update(updateSql, id);
}
@Override
public boolean isFruitNotExist(String name) {
String readSql = "SELECT * FROM user WHERE name = ?";
return jdbcTemplate.query(readSql, (rs, rowNum) -> 0, name).isEmpty();
}
@Override
public FruitAmountResponse statFruit(String name) {
String salesSql = "SELECT sum(price) FROM fruit WHERE is_sold = true GROUP BY name HAVING name = ?";
String notSalesSql = "SELECT sum(price) FROM fruit WHERE is_sold = false GROUP BY name HAVING name = ?";
long salesAmount = jdbcTemplate.queryForObject(salesSql, long.class, name);
long notSalesAmount = jdbcTemplate.queryForObject(notSalesSql, long.class, name);
return new FruitAmountResponse(salesAmount, notSalesAmount);
}
}
FruitMySqlRepository
@Repository
public class FruitMySqlRepository implements FruitRepository {
}
💡 많은 코드를 다루기 위해서는 기능 분리가 매우 중요하다!!
이런 기능 분리를 Layered Architecture 라고 한다
Controller ← Service ← Repository (DTO는 계층 간의 정보 전달 역할)