์ถ์ฒ
https://inf.run/XKQg
Spring Data JPA๋ฅผ ์ฌ์ฉํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์กฐ์์ ๋ํด ํ์ตํ์ฌ ๊ณผ์ ๋ฅผ ์ํํ๋ค.
JPA๋ ์๋ฐ Object์ Realational(๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค) ๊ฐ์ ๋งคํ์ ์ํ ํ์ค ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ๋ค.
Spring Data JPA๋ ์ด๋ฌํ JPA๋ฅผ ๋ ์ฝ๊ฒ ์ฌ์ฉํ ์ ์๋๋ก ๋์์ฃผ๋ฉฐ ๋ฐ๋ณต์ ์ธ ์ฝ๋๋ฅผ ์ต์ํํ๋ค.
Spring Data JPA์์ ์ฌ์ฉ๋๋ Entity ํด๋์ค์ด๋ค.
Entity ํด๋์ค๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํ
์ด๋ธ๊ณผ ๋งคํ๋๋ ์๋ฐ ๊ฐ์ฒด๋ฅผ ์๋ฏธํ๋ฉฐ, ์ด ํด๋์ค๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ fruit ํ
์ด๋ธ๊ณผ ์ํธ์์ฉํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ค.
๋ฐ์ดํฐ๋ฒ ์ด์ค์์๋ Snake Case๋ฅผ ์ฌ์ฉํ๋ค.
Snake Case๋ ๋จ์ด ์ฌ์ด๋ฅผ ์ธ๋์ค์ฝ์ด(_)๋ก ๊ตฌ๋ถํ๋ ๋ช
๋ช
๊ท์น์ผ๋ก, ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ
์ด๋ธ์ ์ปฌ๋ผ๋ช
์ ์ผ๋ฐ์ ์ผ๋ก ์ง์ ๋ ์ฌ์ฉ๋๋ ๋ฐฉ์ ์ค ํ๋์ด๋ค.
์๋ฐ๋ Camel Case๋ฅผ ์ฌ์ฉํ๋ฏ๋ก @Column(name = "warehousing_date")
์ @Column(name = "is_sold")
์ ํตํด ํ
์ด๋ธ์ ์ปฌ๋ผ๋ช
์ ์ง์ ์ง์ ํ๋ค.
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import java.time.LocalDate;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@Entity
public class Fruit {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Column(name = "warehousing_date")
private LocalDate warehousingDate;
private Long price;
@Column(name = "is_sold")
private Boolean isSold = false;
protected Fruit() {
}
public Fruit(String name, LocalDate warehousingDate, Long price) {
this.name = name;
this.warehousingDate = warehousingDate;
this.price = price;
}
public void changeStatus() {
this.isSold = true;
}
}
FruitRepository
์ธํฐํ์ด์ค๋ Spring Data JPA์์ ์ ๊ณตํ๋ JpaRepository๋ฅผ ํ์ฅํ์ฌ ๊ณผ์ผ(Fruit) ์ํฐํฐ์ ๋ํ ๋ฐ์ดํฐ ์ก์ธ์ค ๊ธฐ๋ฅ์ ์ ์ํ๋ค.
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
public interface FruitRepository extends JpaRepository<Fruit, Long> {
// ๋ฌธ์ 1
Optional<Fruit> findById(Long id);
List<Fruit> findByNameAndIsSoldIsFalse(String name);
List<Fruit> findByNameAndIsSoldIsTrue(String name);
// ๋ฌธ์ 2
long countByName(String name);
// ๋ฌธ์ 3
List<Fruit> findAllByPriceGreaterThanEqualAndIsSoldIsFalse(Long price);
List<Fruit> findAllByPriceLessThanEqualAndIsSoldIsFalse(Long price);
}
findById(Long id)
: ๊ณผ์ผ์ ID๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ณผ์ผ์ ์กฐํํ๋ค.
๋ฐํ ํ์
์ผ๋ก Optional์ ์ฌ์ฉํ์ฌ ํด๋น ID์ ๋ํ ๊ณผ์ผ์ด ์กด์ฌํ์ง ์๋ ๊ฒฝ์ฐ์๋ NPE๋ฅผ ๋ฐฉ์งํ๋ค.
findByNameAndIsSoldIsFalse(String name)
: ์ด๋ฆ๊ณผ ํ๋งค ์ฌ๋ถ๊ฐ 'false'์ธ ๊ณผ์ผ์ ์ฐพ๋๋ค.
์ฌ๋ฌ ๊ฐ์ Fruit์ด ์กฐํ๋ ์ ์์ผ๋ฏ๋ก List๋ก ๋ฐํํ๋ค.
findByNameAndIsSoldIsTrue(String name)
: ์ด๋ฆ๊ณผ ํ๋งค ์ฌ๋ถ๊ฐ 'true'์ธ ๊ณผ์ผ์ ์ฐพ๊ณ , List๋ก ๋ฐํํ๋ค.
countByName(String name)
: ํน์ ์ด๋ฆ์ ๊ฐ์ง ๊ณผ์ผ์ ์๋ฅผ ๊ตฌํ์ฌ ๋ฐํํ๋ค.
findAllByPriceGreaterThanEqualAndIsSoldIsFalse(Long price)
: ๊ฐ๊ฒฉ์ด ํน์ ๊ฐ ์ด์์ด๊ณ ํ๋งค ์ฌ๋ถ๊ฐ 'false'์ธ ๊ณผ์ผ์ ์ฐพ๊ณ , List๋ก ๋ฐํํ๋ค.
findAllByPriceLessThanEqualAndIsSoldIsFalse(Long price)
: ๊ฐ๊ฒฉ์ด ํน์ ๊ฐ ์ดํ์ด๊ณ ํ๋งค ์ฌ๋ถ๊ฐ 'false'์ธ ๊ณผ์ผ์ ์ฐพ๊ณ , List๋ก ๋ฐํํ๋ค.
๊ฐ๋จํ๊ฒ ์ฟผ๋ฆฌ ๋ฉ์๋๋ฅผ ์ ์ํ๋ฉด ๊ฐ๋ฐ์๋ ๋ณต์กํ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ์ง ์์๋ ๋๋ฉฐ, ๋์ ์์ค์ ์ถ์ํ๋ฅผ ํตํด ๊ฐํธํ๊ฒ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ํธ ์์ฉํ ์ ์๊ฒ ๋๋ค.
import java.time.LocalDate;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class FruitServiceV2 {
private final FruitRepository fruitRepository;
public void saveFruit(String name, LocalDate warehousingDate, long price) {
fruitRepository.save(new Fruit(name, warehousingDate, price));
}
public void updateFruit(Long id) {
Fruit fruit = fruitRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("ํด๋นํ๋ ๊ณผ์ผ์ด ์กด์ฌํ์ง ์์ต๋๋ค."));
fruit.changeStatus();
fruitRepository.save(fruit);
}
public FruitAmountResponse getAmount(String name) {
List<Fruit> notSalesFruits = fruitRepository.findByNameAndIsSoldIsFalse(name);
List<Fruit> salesFruits = fruitRepository.findByNameAndIsSoldIsTrue(name);
long salesAmount = salesFruits.stream().mapToLong(Fruit::getPrice).sum();
long notSalesAmount = notSalesFruits.stream().mapToLong(Fruit::getPrice).sum();
return new FruitAmountResponse(salesAmount, notSalesAmount);
}
public FruitCountResponse getSoldFruitCount(String name) {
long count = fruitRepository.countByName(name);
return new FruitCountResponse(count);
}
public FruitsInfoResponse getFruitInfo(FruitsInfoRequest request) {
if ("GTE".equals(request.option())) {
return getFruitsInfoByPriceGreaterThanEqual(request.price());
}
if ("LTE".equals(request.option())) {
return getFruitsInfoByPriceLessThanEqual(request.price());
}
throw new IllegalArgumentException("๊ณผ์ผ ๋ชฉ๋ก์ ๋ถ๋ฌ์ฌ ์ ์์ต๋๋ค.");
}
private FruitsInfoResponse getFruitsInfoByPriceGreaterThanEqual(Long price) {
List<Fruit> fruits = fruitRepository.findAllByPriceGreaterThanEqualAndIsSoldIsFalse(price);
return FruitsInfoResponse.mapTo(fruits);
}
private FruitsInfoResponse getFruitsInfoByPriceLessThanEqual(Long price) {
List<Fruit> fruits = fruitRepository.findAllByPriceLessThanEqualAndIsSoldIsFalse(price);
return FruitsInfoResponse.mapTo(fruits);
}
}
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class FruitController {
private final FruitServiceV2 fruitServiceV2;
@PostMapping("/api/v1/fruit")
public void saveFruit(@RequestBody FruitInfo request) {
fruitServiceV2.saveFruit(request.name(), request.warehousingDate(), request.price());
}
@PutMapping("/api/v1/fruit")
public void updateFruit(@RequestBody FruitIdRequest request) {
fruitServiceV2.updateFruit(request.id());
}
@GetMapping("/api/v1/fruit/stat")
public FruitAmountResponse getAmount(@RequestParam String name) {
return fruitServiceV2.getAmount(name);
}
@GetMapping("/api/v1/fruit/count")
public FruitCountResponse getSoldFruitCount(@RequestParam String name) {
return fruitServiceV2.getSoldFruitCount(name);
}
@GetMapping("/api/v1/fruit/list")
public FruitsInfoResponse getFruitInfo(FruitsInfoRequest request) {
return fruitServiceV2.getFruitInfo(request);
}
}
Spring Data JPA๋ ๊ฐํธํ๊ฒ ์ฟผ๋ฆฌ ๋ฉ์๋๋ฅผ ์ ์ํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์กฐ์์ ํ ์ ์์ด ๊ฐ๋ฐ ์์ฐ์ฑ์ ํฅ์ํ ์ ์์๋ค.
ํนํ, Repository ์ธํฐํ์ด์ค๋ฅผ ํตํด SQL ์ฟผ๋ฆฌ ์์ฑ์ ๋ํ ๋ถ๋ด์ด ์ค์ด๋ ๊ฒ ๊ฐ๋ค. ๋ํ, Optional์ ์ฌ์ฉํจ์ผ๋ก์จ ์์ธ ์ฒ๋ฆฌ๋ฅผ ๊ฐํธํ๊ฒ ํ ์ ์์๊ณ , ํ์ ์ธ์ดํํ ์ฟผ๋ฆฌ ๋ฉ์๋๋ฅผ ํตํด ๋ฐํ์ ์๋ฌ๋ฅผ ๋ฐฉ์งํ ์ ์์๋ค.