임도현의 성장
Spring Data JPA + Query Dsl JPA 본문
Spring Data JPA
JPA는 Java 오프젝트를 관계형 데이터베이스에 매핑하는 Java 표준이다. JPA는 개발자가 데이터베이스에 데이터를 CRUD(Create, Read, Update, Delete)를 위해 SQL을 작성할 필요없이 객체를 조작하는 것만으로 데이터베이스에 CRUD 수행할 수 있게 해준다. 대표적인 JPA 기반 구현체로 Hibernate가 있다. Spring Data JPA는 JPA를 더 쉽게 사용할 수 있도록 도와주는 모듈이다.
Query DSL
QueryDSL은 Hibernate Query Language의 쿼리를 타입에 안전하게 생성 및 관리해주는 Java 프레임워크이다. QueryDSL은 정적 타입을 이용하여 SQL과 같은 쿼리를 생성할 수 있게 해준다.
JPA 장점
- 쿼리를 하나하나 작성할 필요가 없어진다.
- 매서드 호출만으로 쿼리를 수행 SQL 반복 작업을 하지 않아도 됨
- 테이블 칼럼 변경 시 간편하게 수정 가능
JPA 단점
- 매핑 설계를 잘못했을때 서능 저하가 발생함
- JPA를 사용할려면 학습할 것들이 너무 많음
- 테이블 조인시 신경써야 할게 많다.
📌 의존성 주입
build.gradle에 다음 의존성 관계를 추가한다.
dependencies {
//JPA, 스프링 데이터 JPA 추가
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
//Querydsl 추가
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
}
//Querydsl 추가, 자동 생성된 Q클래스 gradle clean으로 제거
clean {
delete file('src/main/generated')
}
📌 Entity 클래스 생성
- JPA에서는 엔티티 클래스를 정의하기 위해 @Entity 애너테이션을 사용합니다.
- @Entity : JPA가 사용하는 객체라는 뜻이다. 이 에노테이션이 있어야 JPA가 인식할 수 있다.
- @Id : 테이블의 PK와 해당 필드를 매핑한다.
- @Table : 데이터베이스의 특정 테이블과 매핑되도록
@Data
@Entity
@Table(name = "Item")
public class Item {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // 자동 증가
private Long id;
@Column(name = "item_name", length = 10)
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;
}
}
🏮 Q클래스 생성 1번째 방법
- Gradle -> Tasks -> build -> clean
- Gradle -> Tasks -> other -> compileJava
Q 타입 생성 확인
- build -> generated -> sources -> annotationProcessor -> java/main 하위에 QItem이 생성되어있다.
🏮 Q클래스 생성 2번째 방법
Preferences => Build, Execution, Deployment => Build Tools => Gradle
밑에 사진처럼 맞추고 실행 시키면 java/main 하위에 QItem이 생성되어있다.
IntelliJ 무료 버전의 경우 해당 설정(Build and run using)을 IntelliJ IDEA가 아니라 Gradle로 설정해야 합니다. Jar 파일의 경우는 문제가 없는데, War의 경우 톰캣이 정상 시작되지 않는 문제가 발생할 수 있습니다.
🤠 서비스 구현
- ItemRepositoryV2는 Spring Data JPA로 구현 Spring Data JPA는 단순한 CRUD 적업을 처리할때 보내주는 메서드
- ItemQueryRepository는 QueryDsl JPA로 구현 QueryDsl JPA는 복잡한 조건을 가진 쿼리를 작성하기 위해 보내주는 메서드
@Service
@RequiredArgsConstructor
public class ItemServiceImpl implements ItemService{
private final ItemRepositoryV2 itemRepositoryV2;
private final ItemQueryRepository itemQueryRepository;
@Override // 아이템 저장
public Item save(Item item) {
return itemRepositoryV2.save(item);
}
@Override // 아이템 수정
public void update(Long itemId, ItemUpdateDto updateParam) {
Item findItem = findById(itemId).orElseThrow();
findItem.setItemName(updateParam.getItemName());
findItem.setPrice(updateParam.getPrice());
findItem.setQuantity(updateParam.getQuantity());
}
@Override // 아이템 찾기
public Optional<Item> findById(Long id) {
return itemRepositoryV2.findById(id);
}
@Override // 검색 조건
public List<Item> findItems(ItemSearchCond itemSearch) {
return itemQueryRepository.findAll(itemSearch);
}
@Override // 아이템 삭제
public void deleteId(Long itemId) {
itemRepositoryV2.deleteById(itemId);
}
}
👀 Spring Data JPA 인터페이스 구현
- 스프링 데이터 JPA가 제공하는 JpaRepository 인터페이스를 인터페이스 상속 받으면 기본적인 CRUD 기능 을 사용할 수 있다.
- itemRepositoryV2.Save(item);을 실행하면 코드에는 보이지 않지만 insert into item (item_name,price,quantity,id) values (?,?,?,default) 라는 JPQL이 실행된다.
- 자세한Spring Data JPA 밑에 표기
public interface ItemRepositoryV2 extends JpaRepository<Item, Long> {
}
👻 QueryDsl JPA 클래스 구현
- Querydsl을 사용하려면 JPAQueryFactory 가 필요하다. JPAQueryFactory 는 JPA 쿼리인 JPQL을 만들 기 때문에 EntityManager 가 필요하다.
- EntityManager : JPA의 모든 동작은 엔티티 매니저를 통해서 이루어진다. 엔티티 매니저는 내부에 데이터소스를 가지고 있고, 데이터베이스에 접근할 수 있다.
- 주의 쿼리 안에 보이는 item은 QItem.item 이다.
import static hello.itemservice.domain.QItem.item;
@Repository
public class ItemQueryRepository { // QueryDsl JPA
private final JPAQueryFactory query;
public ItemQueryRepository(EntityManager em) {
this.query = new JPAQueryFactory(em);
}
public List<Item> findAll(ItemSearchCond cond) {
return query.select(item)
.from(item)
.where(
maxPrice(cond.getMaxPrice()),
likeItemName(cond.getItemName())
)
.fetch();
// fetch(); 실행하여 리스트 반환
// fetchOne(); 단일 결과 반환
}
private BooleanExpression likeItemName(String itemName) {
if (StringUtils.hasText(itemName)) {
return item.itemName.like("%" + itemName + "%");
}
return null;
}
private BooleanExpression maxPrice(Integer maxPrice) {
if (maxPrice != null) {
return item.price.loe(maxPrice);
}
return null;
}
}
👻 자주쓰는 Querydsl 표현식 정리
eq ===
ne !==
like 'abc'
contains '%abc%'
lt <
loe <=
gt >
goe >=
😣 Configuration 빈 등록
@Configuration
@RequiredArgsConstructor
public class QuerydslConfig {
private final EntityManager em;
private final ItemRepositoryV2 itemRepositoryV2; //SpringData JPA
@Bean
public ItemService itemService() {
return new ItemServiceImpl(itemRepositoryV2, itemQueryRepository());
}
@Bean
public ItemQueryRepository itemQueryRepository() {
return new ItemQueryRepository(em);
}
}
정리 및 실행 순서
Controller 👉 Service(Interface) 👉 ServiceImpl 👉 Repository
- 컨트롤러에서 서비스 호출
- 서비스에서 간단한 CRUD쿼리이면 Spring Data JPA 메서드 호출
- 복잡한 조건이있는 쿼리는 QueryDsl JPA 매서드를 호출하여 데이터를 처리한다.
- 이렇게 둘을 분리하면 기본 CRUD와 단순 조회는 스프링 데이터 JPA가 담당하고, 복잡한 조회 쿼리는 Querydsl이 담 당하게 된다.
Spring Data JPA 같은 경우는 JpaRepository 인터페이스 상속받아 간단한 CRUD를 처리 해주는데 눈에는 안보이지만 자동으로 JPQL이 만들어서 실행이 된다.
쿼리 메서드 기능
Spring Data JPA는 인터페이스에 메서드만 적어두면, 메서드 이름을 분석해서 쿼리를 자동으로 만들고 실행해주는 기능을 제공한다. 물론 그냥 아무 이름이나 사용하는 것은 아니고 다음과 같은 규칙을 따라야 한다.
조회 : findBy...
삭제 : deleteBy...
위에 처럼 쿼리 메서드 기능을 사용하지 않고 직접 쿼리를 작성해 실행 하는 방법도 있다.
쿼리 메서드 기능 대신에 직접 JPQL을 사용하고 싶을 때는 @Query 와 함께 JPQL을 작성하면 된다. 이때는 메 서드 이름으로 실행하는 규칙은 무시된다.
//쿼리 직접 실행
@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);
참고자료
스프링 DB 2편 - 데이터 접근 활용 기술
'Spring Boot' 카테고리의 다른 글
Spring-Boot AOP개념 @Aspect Advisor (0) | 2024.10.03 |
---|---|
Spring-Boot @Transactional 트랜잭션 전파 (0) | 2024.09.15 |
Spring-Boot H2 데이터베이스 설정과 연결 (1) | 2024.09.02 |
Spring-Boot MyBatis (0) | 2024.09.01 |
Spring-Boot Cookie Session (1) | 2024.08.13 |