Spring Data JPA란?
JPA는 JdbcTemplate, MyBatis 등의 SQL Mapper 기술의 많은 단점들을 보완한 획기적인 기술이었다.
하지만 JPA를 사용하기 위해선 많은 설정과 많은 코드를 필요로 했다.
따라서 Spring 프레임워크는 JPA를 적은 설정과 적은 코드로 사용할 수 있도록 별도의 라이브러리를 만들었다.
그것이 바로 Spring Data JPA이다.
Spring Data JPA는 다음의 기능을 제공한다.
1. 공통 인터페이스 기능
Spring Data JPA는 기본적으로 사용하는 CRUD 기능을 하나의 공통 인터페이스로 만들어서 제공한다.
그 인터페이스가 바로 JpaRepository이다.
JpaRepository는 다양한 인터페이스를 상속받아 일반적으로 사용되는 CRUD기능을 제공한다.
JpaRepository 사용법
public interface Repository extends JpaRepository<T, ID> {
}
- T: 엔티티 객체
- ID: 기본키의 타입
이렇게 인터페이스를 선언하면 JpaRepository가 기본적으로 제공하는 CRUD 기능을 모두 사용할 수 있다.
Spring Data JPA가 구현클래스를 대신 선언
- Spring Data JPA는 JpaRepository를 상속받은 인터페이스의 구현체를 프록시 기술을 통해 자동으로 만들어준다.
- 그렇게 만든 구현체를 스프링 빈으로 등록한다.
- 이렇게 되면 개발자는 JpaRepository를 상속받은 인터페이스만 만들면 JpaRepository의 CRUD 기능을 사용할 수 있다.
2. 쿼리 메서드 기능
Spring Data JPA는 기본적으로 제공되는 CRUD외에도 메서드의 이름을 통해 쿼리를 생성하는 기능을 제공한다.
원하는 기능을 인터페이스에 적어두기만 하면 알아서 메서드 이름을 분석해 쿼리를 만들어 실행시켜준다.
물론 아무이름이나 다 해석해주는 것은 아니고 일정한 규칙을 따라야 한다
스프링 데이터 JPA가 제공하는 쿼리 메소드 기능
- 조회: 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
쿼리 메서드 기능을 사용하지 않고 직접 JPQL을 작성해 사용할 수도 있다.
그럴땐 @Query를 사용하면 된다.
예시:
public interface SpringDataJpaItemRepository extends JpaRepository<Item, Long> {
//쿼리 메서드 기능
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);
}
JPQL말고도 일반 SQL도 사용할 수 있다.
그럴땐 @Query의 nativeQuery 속성을 true로 설정하면 된다.
[JPA네이티브쿼리지원]
public interfaceUserRepositoryextendsJpaRepository<User,Long>{
@Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS=?0", nativeQuery=true)
UserfindByEmailAddress(String emailAddress);
}
Spring Data JPA 사용하기
사용법을 이해하기 위한 테스트케이스는 다음과 같다.
1. 아이템 도메인(Item)
@Data
@Entity
public class Item {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String itemName;
private Integer price;
private Integer quantity;
public Item() {
}
public Item(String itemName, Integer price, Integer quantity) {
this.itemName = itemName;
this.price = price;
this.quantity = quantity;
}
}
2. 아이템 리포지토리(ItemRepository)
public interface ItemRepository {
Item save(Item item);
void update(Long itemId, ItemUpdateDto updateParam);
Optional<Item> findById(Long id);
List<Item> findAll(ItemSearchCond cond);
}
- Item save(Item item): 아이템 객체를 데이터베이스에 저장
- void update(Long itemId, ItemUpdateDto updateParam): 아이템 객체를 id를 통해 업데이트
- Optional<Item> findById(Long id): id를 통해 아이템 객체 찾기
- List<Item> findAll(ItemSearchCond cond): 특정 조건의 Item 모두찾기
3. 아이템 검색 조건(ItemSearchCond)
@Data
public class ItemSearchCond {
private String itemName;
private Integer maxPrice;
public ItemSearchCond() {
}
public ItemSearchCond(String itemName, Integer maxPrice) {
this.itemName = itemName;
this.maxPrice = maxPrice;
}
}
설정하기
1) build.gradle
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
- 일반 JPA를 사용할 때의 라이브러리와 똑같다.
JPARepository
Spring Data JPA를 사용하기 위해서는 JPARepository 인터페이스를 상속받은 인터페이스를 선언해야 한다.
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);
}
- extends JpaRepository<Item, Long>
- 엔티티 객체인 Item과 Item의 기본키인 id의 타입인 Long이 제너릭 인자로 들어가야 한다.
그 다음 이렇게 선언한 인터페이스를 실제 리포지토리에서 의존해 사용하면 된다.
@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();
}
}
}
1. @Transactional
- Spring Data JPA도 JPA와 마찬가지로 데이터를 변경시에는 반드시 트랜잭션 안에서 실행돼야 한다.
2. SpringDataJpaItemRepository
private final SpringDataJpaItemRepository repository;
- 앞서 선언했던 인터페이스를 의존한다.
3. save()
@Override
public Item save(Item item) {
return repository.save(item);
}
- save()
- JpaRepository가 기본적으로 제공하는 저장기능이다.
실행되는 쿼리
insert into item (item_name,price,quantity,id) values (?,?,?,default)
4. findById()
@Override
public Optional<Item> findById(Long id) {
return repository.findById(id);
}
- findById()
- JpaRepository가 기본적으로 제공하는 조회기능이다.
- 기본키를 통해 데이터를 조회한다.
jpa가 생성한 쿼리
select i1_0.id,i1_0.item_name,i1_0.price,i1_0.quantity from item i1_0 where i1_0.id=?
5. update()
@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());
}
- itemId로 Item을 조회한뒤 setter를 통해 데이터를 수정해주었다.
jpa가 생성한 쿼리
update item set item_name=?,price=?,quantity=? where id=?
- JPA의 영속성 컨텍스트 특성으로 인해 별도의 코드 없이 자동으로 업데이트 되는 모습이다.
6. findAll()
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);
}
@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();
}
}
- findAll()에선 4가지의 경우가 존재한다.
- 아무 조건도 존재하지 않는 경우
➡️ findAll(): JpaRepository 기본 제공 - 가격 조건만 존재하는 경우
➡️ findByPriceLessThanEqual(): 메서드 쿼리 기능 사용(SpringDataJpaItemRepository에 선언) - 상품 이름 조건만 존재하는 경우
➡️ findByItemNameLike(): 메서드 쿼리 기능 사용(SpringDataJpaItemRepository에 선언) - 둘 다 존재하는 경우
➡️ findByItemNameLikeAndPriceLessThanEqual(): 메서드 쿼리 기능 사용(SpringDataJpaItemRepository에 선언)
➡️ findItems(): JPQL 사용
❗️이렇게 조건이 여러 개인 경우에 메서드 쿼리 기능을 사용하면 메서드의 이름이 상당히 길어지기 때문에 JPQL을 사용하는 것이 좋다.
- 아무 조건도 존재하지 않는 경우
정리
1. Spring Data JPA: JPA를 편리하게 사용할 수 있게 해주는 스프링 라이브러리
2. 주요 기능
1) 공통 인터페이스 제공
- JpaRepository: 대부분의 CRUD기능 제공
- JpaRepository를 상속받은 인터페이스를 만들면 스프링이 자동으로 구현체를 만들어 스프링 빈으로 등록함
2) 쿼리 메서드
- JpaRepository가 기본으로 제공하는 CRUD외에도 개발자가 원하는 기능을 인터페이스에서 이름만 선언
- 메서드 이름만 보고 알아서 쿼리를 만들어 실행시켜줌
- JPQL이나 SQL도 직접 사용가능(@Query)
해당 포스트는 김영한 님의 스프링 DB 2편 - 데이터 접근 활용 기술 강의를 기반으로 작성되었습니다.
출처: https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-2
'Spring > Spring Data' 카테고리의 다른 글
[Spring Data] Spring Transaction의 이해 (0) | 2024.06.08 |
---|---|
[Spring Data] QueryDSL (0) | 2024.06.07 |
[Spring Data] JPA (0) | 2024.06.07 |
[Spring Data] MyBatis (0) | 2024.06.06 |
[Spring Data] JdbcTemplate (0) | 2024.06.06 |