<aside> 📌 우리는 스프링 컨테이너의 개념을 배우고, 기존에 작성했던 Controller 코드를 3단 분리해보았습니다. 앞으로 API를 개발할 때는 이 계층에 맞게 각 코드가 작성되어야 합니다! 🙂
과제 #4 에서 만들었던 API를 분리해보며, Controller - Service - Repository 계층에 익숙해져 봅시다! 👍
</aside>

Controller
@RestController
public class FruitController {
private final FruitService fruitService;
public FruitController(FruitService fruitService) {
this.fruitService = fruitService;
}
@PostMapping("/api/v1/fruit")
public void saveFruit(@RequestBody FruitCreateRequest request)
{
fruitService.saveFruit(request);
}
@PutMapping("/api/v1/fruit")
public void updateFruit(@RequestBody FruitUpdateRequest request) {
fruitService.updateFruit(request);
}
@GetMapping("/api/v1/fruit/stat")
public FruitAmountResponse calculateAmountFruit(@RequestParam String name) {
FruitAmountResponse response = fruitService.calculateAmountFruit(name);
return response;
}
}
Service
@Service
public class FruitService {
private final FruitRepository fruitRepository;
public FruitService(FruitRepository fruitRepository) {
this.fruitRepository = fruitRepository;
}
public void saveFruit(FruitCreateRequest request) {
fruitRepository.saveFruit(request.getName(), request.getWarehousingDate(), request.getPrice());
}
public void updateFruit(FruitUpdateRequest request) {
if (fruitRepository.isFruitExist(request.getId())) {
throw new IllegalArgumentException();
}
fruitRepository.updateFruit(request.getId());
}
public FruitAmountResponse calculateAmountFruit(String name) {
if (fruitRepository.isFruitExist(name)) {
throw new IllegalArgumentException();
}
return fruitRepository.calculateAmountFruit(name);
}
}
Repository
@Repository
public class FruitRepository {
private final JdbcTemplate jdbcTemplate;
public FruitRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void saveFruit(String name, LocalDate warehousingDate, long price) {
String sql = "INSERT INTO fruit(name, warehousing_date, price) VALUES(?, ? ,?)";
jdbcTemplate.update(sql, name, warehousingDate, price);
}
public boolean isFruitExist(long id) {
String readSql = "SELECT * FROM fruit WHERE id = ?";
return jdbcTemplate.query(readSql, (rs, rowNum) -> 0, id).isEmpty();
}
public void updateFruit(long id) {
String sql = "UPDATE fruit SET is_sold = true WHERE id = ?";
jdbcTemplate.update(sql, id);
}
public boolean isFruitExist(String name) {
String readSql = "SELECT * FROM fruit WHERE name = ?";
return jdbcTemplate.query(readSql, (rs, rowNum) -> 0, name).isEmpty();
}
public FruitAmountResponse calculateAmountFruit(String name) {
String soldSql = "SELECT SUM(price) FROM fruit WHERE name = ? AND is_sold = true";
String notSoldSql = "SELECT SUM(price) FROM fruit WHERE name = ? AND is_sold = false";
Long salesAmount = jdbcTemplate.queryForObject(soldSql, Long.class, name);
Long notSalesAmount = jdbcTemplate.queryForObject(notSoldSql, Long.class, name);
return new FruitAmountResponse(salesAmount, notSalesAmount);
}
}
@Primary 활용]FruitRepository 생성 및 FruitMemoryRepository, FruitMysqlRepository 분리
public interface FruitRepository {
void saveFruit(String name, LocalDate warehousingDate, long price);
boolean isFruitExist(long id);
void updateFruit(long id);
boolean isFruitExist(String name);
FruitAmountResponse calculateAmountFruit(String name);
}
@Repository
public class FruitMysqlRepository implements FruitRepository{
...
}
@Repository
public class FruitMemoryRepository implements FruitRepository{
...
}
@Primary를 사용하지 않았을 경우
Parameter 0 of constructor in com.example.tasks.task4.FruitService required a single bean, but 2 were found:
- fruitMemoryRepository: defined in file [/Users/heek/inflearn-study-be/project/tasks/build/classes/java/main/com/example/tasks/task4/FruitMemoryRepository.class]
- fruitMysqlRepository: defined in file [/Users/heek/inflearn-study-be/project/tasks/build/classes/java/main/com/example/tasks/task4/FruitMysqlRepository.class]
@Primary 를 사용하여 스위칭
@Primary
@Repository
public class FruitMysqlRepository implements FruitRepository{
...
}
@Primary
@Repository
public class FruitMemoryRepository implements FruitRepository{
...
}