Sping Data JPA가 제공하는 인터페이스, Entity가 생성한 데이터베이스에 접근하는 데 사용된다.
접근하려는 테이블과 매핑되는 Entity에 대한 인터페이스를 생성하고, JpaRepository를 상속받으면 된다.
상속받을 때는 대상 Entity와 기본값 타입을 지정해야 한다.
EX) Repository생성
public interface ProductRepository extends JpaRepository<Product, Long> {
}
메서드에 이름을 붙일 때는 첫 단어를 제외한 이후 단어들의 첫 글자를 대문자로 설정해야, JPA에서 정상적으로 인식하고 쿼리를 자동으로 만들어 준다.
findBy: SQL의 where절 역할 수행하는 구문
AND, OR: 조건을 여러 개 설정하기 위해 사용
Like/NotLike : SQL의 like와 동일한 기능을 수행, 특정문자를 포함하는지 여부를 조건으로 추가
orderBy : SQL문에서 order by 와 동일한 기능 수행
countBy : SQL문의 count와 동일한 기능 수행
데이터베이스에 접근하기 위한 로직을 관리하기 위한 객체이다.
Repository와 역할이 비슷하다.
DAO클래스는 일반적으로 '인터테이스-구현체'구성으로 생성한다.
일반적으로 데이터베이스에 접근하는 메서드는 리턴값으로 데이터 객체를 전달한다.
(이때 데이터 객체를 Entity객체로 전달할지, DTO객체로 전달할지는 개발자마다 견해 차이가 있음)
EX) ProductDAO인터페이스
public interface ProductDAO {
Product insertProduct(Product product);
Product selectProduct(Long number);
Product updateProductName(Long number, String name) throws Exception;
void deleteProduct(Long number) throws Exception;
}
인터페이스 설계를 마쳤다면, 해당 인터페이스의 구현체를 만들어야 한다.
ProductDAOImpl클래스를 스프링이 관리하는 빈으로 등록하려면, @Component 또는 @Service 어노테이션 지정해야 한다.
빈으로 등록된 객체는 다른 클래스가 인터페이스를 가지고 의존성을 주입 받을때, 이 구현체를 찾아 주입하게 된다.
insertProduct()
- insertProduct()메서드 구현 : Product 엔티티를 데이터베이스에 저장하는 기능을 수행
- .save() : repository생성시, 인터페이스에서 따로 메서드를 구현하지 않아도 JPA에서 기본 메서드 제공하므로 save()메서드 활용 할 수 있다.
selectProduct()
- selectProduct()메서드 구현 : selectProduct()메서드가 사용한 repository메서드는 getById()이다.
repository에서 단건 조회를 위한 기본 메서드로, getById()와 findById()메서드 제공한다.
updateProductName()
- updateProductName()메서드 구현 : JPA는 값을 갱신할때 update키워드 사용하지 않음. find()메서드를 통해 데이터베이스에서 값을 가져오면, 가져온 객체가 영속성 컨텍스트에 추가된다. 영속성 컨텍스트가 유지되는 상황에서 객체의 값을 변경하고, 다시 save()를 하면 JPA에서는 dirty check라고 하는 변경 감지를 수행한다.
deleteProduct()
- deleteProduct()메더드 구현 : 삭제하고자 하는 레코드와 매핑된 영속 객체를 영속성 컨텍스트에 가져와야 한다. findById()메서드를 통해, 객체를 가져오는 작업을 수행하고, delete()메서드를 통해 해당 객체를 삭제하게끔 요청한다.
EX) ProductDAO인터페이스의 구현체 클래스
@Component
public class ProductDAOImpl implements ProductDAO {
private ProductRepository productRepository;
@Autowired
public ProductDAOImpl(ProductRepository productRepository) {
this.productRepository = productRepository;
}
// 예제 6.11
@Override
public Product insertProduct(Product product) {
Product savedProduct = productRepository.save(product);
return savedProduct;
}
// 예제 6.12
@Override
public Product selectProduct(Long number) {
Product selectedProduct = productRepository.getById(number);
return selectedProduct;
}
// 예제 6.15
@Override
public Product updateProductName(Long number, String name) throws Exception {
Optional<Product> selectedProduct = productRepository.findById(number);
Product updatedProduct;
if (selectedProduct.isPresent()) {
Product product = selectedProduct.get();
product.setName(name);
product.setUpdatedAt(LocalDateTime.now());
updatedProduct = productRepository.save(product);
} else {
throw new Exception();
}
return updatedProduct;
}
// 예제 6.17
@Override
public void deleteProduct(Long number) throws Exception {
Optional<Product> selectedProduct = productRepository.findById(number);
if (selectedProduct.isPresent()) {
Product product = selectedProduct.get();
productRepository.delete(product);
} else {
throw new Exception();
}
}
}