스프링 데이터 JPA란 JPA를 편리하게 사용할 수 있도록 도와주는 라이브러리 이다.
스프링 JPA의 주요 특징
- 인터페이스( JpaRepository )상속을 통한 기본적 CRUD 지원, 구현 클래스 자동 생성
Spring Data JPA에서 프록시 기술을 사용하여 구현 클래스를 자동으로 생성해주므로
개발자는 인터페이스만 생성하고 의존성을 주입받아 편하게 사용하면 된다.
- 쿼리 메서드 기능 : 스프링 데이터 JPA가 메서드 이름을 분석하여 필요한 JPQL을 자동 생성해주어 복잡한 쿼리문을 생략할 수 있다.
단, 아무 이름이나 사용하는 것은 안되고 규칙이 따로 존재한다.
- 조회: find…By, read…By, query…By, get…By
- 예) findHelloBy처럼 …에 식별하기 위한 내용(설명)이 들어가도 된다.
- COUNT: count…By
- 반환타입: long
- EXISTS: exists…By
- 반환타입: boolean
- 삭제: delete…By, remove…By
- 반환타입: long
- DISTINCT: findDistinct, findMemberDistinctBy
- LIMIT: findFirst3, findFirst, findTop, findTop3
Spring Data JPA 장단점
장점
- MyBatis나 JDBC와 같이 SQL을 개발자가 모두 작성할 필요가 없기 때문에 코드량이 엄청 줄어든다.
- 객체 위주로 코드가 작성되니 이해가 쉽고 유지보수도 편하다.
단점
- 쿼리를 직접 작성한 것 보다는 성능이 떨어질 수 있다.
- 복잡하고 무거운 쿼리는 직접 SQL을 작성해야하는 경우가 있다.
- 동적 쿼리 작성이 복잡하다. (이 부분은 Querydsl로 해결 가능 !!)
스프링 데이터 JPA 사용해보기
<Spring Data Jpa 인터페이스>
package hello.itemservice.repository.jpa;
import hello.itemservice.domain.Item;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
public interface SpringDataJpaItemRepository extends JpaRepository<Item, Long> {
List<Item> findByItemNameLike(String itemName);
List<Item> findByPriceLessThanEqual(Integer price);
List<Item> findByItemNameLikeAndPriceLessThanEqual(String itemName, Integer price);
@Query("select i from Item i where i.itemName like :itemName and i.price <= :price")
List<Item> findItems(@Param("itemName") String itemName, @Param("price") Integer price);
}
JpaRepository인터페이스를 상속받아 스프링 데이터 JPA가 제공하는 기본적인 CRUD를 사용할 수 있다.
만약 쿼리를 직접 작성해야 하는 경우 @Query를 이용하여 직접 쿼리를 실행할 수 있다.
<JpaItemRepositoryV2>
package hello.itemservice.repository.jpa;
import hello.itemservice.domain.Item;
import hello.itemservice.repository.ItemRepository;
import hello.itemservice.repository.ItemSearchCond;
import hello.itemservice.repository.ItemUpdateDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.Optional;
@Repository
@Transactional
@RequiredArgsConstructor
public class JpaItemRepositoryV2 implements ItemRepository {
private final SpringDataJpaItemRepository repository;
@Override
public Item save(Item item) {
return repository.save(item);
}
@Override
public void update(Long itemId, ItemUpdateDto updateParam) {
Item findItem = repository.findById(itemId).orElseThrow();
findItem.setItemName(updateParam.getItemName());
findItem.setPrice(updateParam.getPrice());
findItem.setQuantity(updateParam.getQuantity());
}
@Override
public Optional<Item> findById(Long id) {
return repository.findById(id);
}
@Override
public List<Item> findAll(ItemSearchCond cond) {
String itemName = cond.getItemName();
Integer maxPrice = cond.getMaxPrice();
if(StringUtils.hasText(itemName) && maxPrice != null){
//return repository.findByItemNameLikeAndPriceLessThanEqual("%"+itemName + "%",maxPrice);
return repository.findItems("%"+ itemName + "%",maxPrice);
}else if(StringUtils.hasText(itemName)){
return repository.findByItemNameLike("%"+ itemName + "%");
}else if(maxPrice != null){
return repository.findByPriceLessThanEqual(maxPrice);
}else{
return repository.findAll();
}
}
}
- @Repository: Spring이 이 클래스를 리포지토리로 인식하도록 지정.
- @Transactional: 해당 클래스의 모든 메서드는 트랜잭션으로 동작. 기본적으로 읽기-쓰기 트랜잭션으로 설정됨.
- @RequiredArgsConstructor: final 필드를 자동으로 초기화하는 생성자를 생성
< SpringDataJpaConfig >
package hello.itemservice.config;
import hello.itemservice.repository.ItemRepository;
import hello.itemservice.repository.jpa.JpaItemRepositoryV2;
import hello.itemservice.repository.jpa.SpringDataJpaItemRepository;
import hello.itemservice.service.ItemService;
import hello.itemservice.service.ItemServiceV1;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@Configuration
@EnableJpaRepositories(basePackages = "hello.itemservice.repository.jpa") // 리포지토리 패키지 지정
@RequiredArgsConstructor
public class SpringDataJpaConfig {
private final SpringDataJpaItemRepository springDataJpaItemRepository;
@Bean
public ItemService itemService() {
return new ItemServiceV1(itemRepository());
}
@Bean
public ItemRepository itemRepository() {
return new JpaItemRepositoryV2(springDataJpaItemRepository);
}
}
이렇게 하면 런타임 객체 의존관계는 다음과 같다.
itemService는 ItemRepository 인터페이스에 의존을 하고 ItemRepository의 구현체인 JpaItemRepositoryV2를 통해 데이터 작업을 수행하는데,
이때 런타임동안 스프링 Spring Data JPA의 프록시 리포지토리가 생성된다.
이렇게 되면 개발자가 직접 데이터베이스 작업 코드를 작성하지 않아도, Spring이 런타임에 프록시 객체를 생성하여 구현체 역할을 한다.
가장 중요한것은 Spring Data JPA는 런타임 동안 리포지토리에 대한 구현부로 프록시 객체를 생성하기 때문에, 별도의 구현 없이 인터페이스만으로도 데이터에 대한 CRUD 작업이 가능하다는 것이다!!
Spring Data JPA는 프로젝트에서 선택이 아닌 필수 이므로 많은 연습이 필요해 보인다..
Spring Data Jpa연습 프로젝트 리포지토리
https://github.com/yangwoohyeon/db-practice
GitHub - yangwoohyeon/db-practice: JdbcTemplate, MyBatis, JPA, Spring data JPA, Spring Transaction 연습
JdbcTemplate, MyBatis, JPA, Spring data JPA, Spring Transaction 연습 - yangwoohyeon/db-practice
github.com
'BackEnd > Database' 카테고리의 다른 글
Spring 트랜잭션[@Transactional] (0) | 2025.02.12 |
---|---|
QueryDSL (1) | 2025.02.10 |
MyBatis (0) | 2025.02.08 |
DB Test[@Transactional, 임베디드 모드 DB] (0) | 2025.02.07 |
JdbcTemplate (0) | 2025.02.07 |