

Fruit API를 Controller, Service, Repository로 분리하고, Repository는 과일 정보를 저장할 때 MySQL DB를 사용하도록 하는 FruitMySqlRepository, 메모리를 사용하도록 하는 FruitMemoryRepository를 각각 구현하여 Service에 스프링 컨테이너를 통해 의존성이 주입되게 해야한다.
이를 구현하기 위해 두 Repository를 바꿀 수 있어야 하므로 인터페이스 FruitRepository를 만들고, 이 인터페이스를 Repository들이 오버라이딩해서 사용할 수 있도록 구현하고 @Repository를 사용하여 스프링 빈으로 등록했다. 이때, 스프링 빈으로 등록된 Repository가 2개이므로 @Qualifier를 사용하여 Service가 원하는 Repository를 의존할 수 있게 구현하였다.
import java.time.LocalDate;
public class Fruit {
private long id;
private String name;
private LocalDate warehousingDate;
private long price;
private boolean sell;
public Fruit(long id, String name, LocalDate warehousingDate, long price) {
this.id = id;
this.name = name;
this.warehousingDate = warehousingDate;
this.price = price;
}
public long getId() {
return id;
}
public String getName() {
return name;
}
public long getPrice() {
return price;
}
public boolean isSell() {
return sell;
}
public void setSell(boolean sell) {
this.sell = sell;
}
}
@RestController
public class FruitController {
private final FruitService fruitService;
public FruitController(FruitService fruitService) {
this.fruitService = fruitService;
}
@PostMapping("/api/v1/fruit")
public void saveFruit(@RequestBody FruitRequest request){
fruitService.saveFruit(request);
}
@GetMapping("api/v1/fruit/stat")
public FruitResponse getFruitTotalPrice(@RequestParam String name){
return fruitService.getFruit(name);
}
@PutMapping("api/v1/fruit")
public void putFruit(@RequestBody FruitUpdateRequest request){
fruitService.putFruit(request.getId());
}
}
@Service
public class FruitService{
private final FruitRepository fruitRepository;
public FruitService(@Qualifier("SQL") FruitRepository fruitRepository) {
this.fruitRepository = fruitRepository;
}
public void saveFruit(FruitRequest fruitRequest){
if (fruitRequest.getName() == null || fruitRequest.getWarehousingDate() == null || fruitRequest.getPrice() == null) {
throw new IllegalArgumentException();
}
fruitRepository.saveFruit(fruitRequest);
}
public FruitResponse getFruit(String name){
return fruitRepository.getFruit(name);
}
public void putFruit(long id){
if(fruitRepository.isNotFruitExist(id)){
throw new IllegalArgumentException();
}
fruitRepository.putFruit(id);
}
}
public interface FruitRepository {
void saveFruit(FruitRequest fruitRequest);
FruitResponse getFruit(String name);
void putFruit(long id);
boolean isNotFruitExist(long id);
}
@Qualifier("SQL")
@Repository
public class FruitMySqlRepository implements FruitRepository{
private final JdbcTemplate jdbcTemplate;
public FruitMySqlRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public void saveFruit(FruitRequest fruitRequest) {
String sql = "INSERT INTO warehouse (name, warehousingDate, price) VALUES (?,?,?)";
jdbcTemplate.update(sql, fruitRequest.getName(),fruitRequest.getWarehousingDate(), fruitRequest.getPrice());
}
@Override
public FruitResponse getFruit(String name) {
String sql = "SELECT sell, SUM(price) AS TotalPrice FROM warehouse WHERE name = ? GROUP BY sell;";
AtomicLong sellPrice = new AtomicLong();
AtomicLong notSellPrice = new AtomicLong();
List<Object> results = jdbcTemplate.query(sql, (rs, rowNum)->{
boolean sell = rs.getBoolean("sell");
if (rs.wasNull()){
System.out.println("error");
throw new IllegalArgumentException();
}
else if (sell){
sellPrice.addAndGet(rs.getLong("TotalPrice"));
}
else{
notSellPrice.addAndGet(rs.getLong("TotalPrice"));
}
return null;
},name);
if (results.isEmpty()) {
throw new IllegalArgumentException("해당하는 이름의 과일이 없습니다.");
}
return new FruitResponse(sellPrice.get(), notSellPrice.get());
}
@Override
public void putFruit(long id) {
String sql = "UPDATE warehouse SET sell = true WHERE id = ?";
jdbcTemplate.update(sql, id);
}
@Override
public boolean isNotFruitExist(long id) {
String readSql = "SELECT * FROM warehouse WHERE id = ?";
return jdbcTemplate.query(readSql, (rs, rowNum)-> 0, id).isEmpty();
}
}
@Repository
public class FruitMemoryRepository implements FruitRepository{
private List<Fruit> fruitList;
private long id = 1;
public FruitMemoryRepository(List<Fruit> fruitList) {
this.fruitList = fruitList;
}
@Override
public void saveFruit(FruitRequest fruitRequest) {
Fruit fruit = new Fruit(id++, fruitRequest.getName(), fruitRequest.getWarehousingDate(), fruitRequest.getPrice());
fruitList.add(fruit);
}
@Override
public FruitResponse getFruit(String name){
long sellPrice = 0, notSellPrice = 0;
boolean checkNoFruit = false;
for (Fruit fruit : fruitList){
if ( fruit.getName().equals(name) ){
if ( fruit.isSell() ){
sellPrice += fruit.getPrice();
}
else{
notSellPrice += fruit.getPrice();
}
checkNoFruit = true;
}
}
if (!checkNoFruit){
throw new IllegalArgumentException("해당하는 이름의 과일이 없습니다.");
}
return new FruitResponse(sellPrice, notSellPrice);
}
@Override
public void putFruit(long id) {
for ( Fruit fruit : fruitList ){
if (fruit.getId() == id){
fruit.setSell(true);
}
}
}
@Override
public boolean isNotFruitExist(long id) {
for(Fruit fruit : fruitList){
if ( fruit.getId() == id ){
return false;
}
}
return true;
}
}