본문 바로가기
Back/DB

[DB] JPA

by 오엥?은 2023. 5. 12.
반응형

✔ JPA

• CRUD + 쿼리

 : 단순히 CRUD 만 하는 것이 아니고, 쿼리에 대한 부분도 어느정도 지원이 된다.

• 동일한 인터페이스

 : CRUD Repository (저장소)

• 페이징 처리

• 메서드 이름으로 쿼리 생성

• 스프링 MVC 에서 id 값만 넘겨도 도메인 클래스로 바인딩

 

✔ JPA 의 기능

➕ Spring Data JPA

public interface MemberRepository extends JpaRepository<Member, Long> {
    // 실제 아무것도 없음
}

위의 MemberRepository가 Spring Data가 제공하는 JpaRepository만 상속받으면 

 

 <S extends T> S save(S entity)

• void delete(ID id)

• Optional<T> findById(ID id)

• Iterable<T> findAll

• long count()

• 기타 등등...

 

이것들이 다 기본으로 제공된다.

 

 

 메서드 이름으로 쿼리 생성

public interface MemberRepository extends JpaRepository<Member, Long> {
    List<User> findByEmailAndName(String email, String name);
}

이름을 findByEmailAndName으로 하고 파라미터로 email, name 을 넘기면

// [생성된 JPQL]
select m from Member m
where m.email = ?1
  and m.name = ?2

이름만으로 분석을 해서 자동으로 위의 JPQL이 생성된다.

 

 

@Query (JPA 네이티브 쿼리)

// 인터페이스에 쿼리 작성 기능
public interface UserRepository extends JpaRepository<User, Long> {
    @Query("select u from User u where u.emailAddress = ?1")
    User findByEmailAddress(String emailAddress);
}
// JPA 네이티브 쿼리 지원
public interface UserRepository extends JpaRepository<User, Long> {
    @Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?0", nativeQuery = true)
    User findByEmailAddress(String emailAddress);
}
반응형

 

✔ JPA  장점

• 코딩량이 줄어듦

• 도메인 클래스를 중요하게 다룸 (엔티티를 사용하기 때문에)

• 비지니스 로직 이해 쉬움 

• SQL을 작성할 시간에 더 많은 테스트 케이스 작성 가능

 

  • build.gradle 에 추가
// JPA, 스프링 데이터 JPA 추가
implementation 'org.springframework.boot:spring-boot-starter-data-jpa';
  • application.properties 에 추가
#JPA log
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

 

  • 스프링 데이터 JPA
public interface MemberRepository extends JpaRepository<Member, Long> {
    List<Member> findByUsernameAndAgeGreaterThan(String username, int age);
}

- 스프링 데이터 JPA는 메서드 이름을 분석해서 필요한 JPQL을 만들고 실행해 준다. 

 (JPQL은 JPA가 SQL로 번역해서 실행한다.)

 

 

📌 다음과 같은 규칙을 따라야 한다.

 

조회 : find...By, read...By, query...By, get...By

 예) findHelloBy 처럼 ...에 식별하기 위한 내용(설명)이 들어가도 된다.

COUNT : count...By 반환타입 long

EXSITS : exists...By 반환타입 boolean

삭제 : delete...By, remove...By 반환타입 long

DISTINCT : findDistinct, findMemberDistinctBy

LIMIT : findFirst3, findFirst, findTop, findTop3

 

 

📌 스프링 데이터 JPA도 Example이라는 기능으로 약간의 동적 쿼리를 지원하지만, 실무에서 사용하기는 기능이 빈약하다. 실무에서 JPQL 동적 쿼리는 Querydsl에서 동적 쿼리로 깔끔하게 해결할 수 있다. 

 

• findAll()

 - 코드에는 보이지 않지만, JpaRepository 공통 인터페이스가 제공하는 기능이다.

 - 모든 Item을 조회한다.

 - select i from Item i 가 실행된다.

• findByItemNameLike()

 - 이름 조건만 검색했을 때 사용하는 쿼리 메서드이다.

 - select i from Item i where i.name like ? 가 실행된다.

• findByPriceLessThanEqual()

 - 가격 조건만 검색했을 때 사용하는 쿼리 메서드이다.

 - select i from Item i where i.price <= ? 가 실행된다.

• findByItemNameLikeAndPriceLessThanEqual()

 - 가격 조건만 검색했을 때 사용하는 쿼리 메서드이다.

 - select i from Item i where i.price <= ? 가 실행된다.

 

• findItems()

 - 메서드 이름으로 쿼리를 실행하는 기능은 다음과 같은 단점이 있다.

  ① 조건이 많으면 메서드 이름이 너무 길어진다.

  ② 조인 같은 복잡한 조건을 사용할 수 없다.

    : 메서드 이름으로 쿼리를 실행하는 기능은 유용하지만, 복잡해지면 직접 JPQL 쿼리를 작성하는 것이 좋다.

 - 쿼리를 직접 실행하려면 @Query 어노테이션을 사용하면 된다.

 - 메서드 이름으로 쿼리를 실행할 때는 파라미터를 순서대로 입력하면 되지만, 쿼리를 직접 실행할 때는 파라미터를 명시적으로 바인딩 해야 한다.

 - 파라미터 바인딩은 @Param("itemName") 어노테이션을 사용하고, 어노테이션의 값에 파라미터 이름을 주면 된다.

 

 

  • 예제
package hello.itemservice.domain;

import lombok.Builder;
import lombok.Data;

import javax.persistence.*;

@Data
@Entity
public class Item {

    // pk
    @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;
    }
}

◽ @Entity : JPA가 사용하는 객체라는 뜻이다. 이 에노테이션이 있어야 JPA 가 인식할 수 있다. 이렇게 @Entity 가 붙은 객체를 JPA에서는 엔티티라고 한다.

◽ @ Id : 테이블의 PK와 해당 필드를 매핑한다.

◽ @GenerateValue(strategy = GenerationType.IDENTITY) : PK 생성 값을 데이터베이스에서 생성하는 IDENTITY 방식을 사용한다. 예) MySQL auto increment

◽ @Column : 객체의 필드를 테이블의 컬럼과 매핑한다.

  - name = "item_name" : 객체는 itemName 이지만 테이블의 컬럼은 item_name 이므로 이렇게 매핑했다.

  - length = 10 (varchar 10) :JPA의 매핑 정보로 DDL도 생성할 수 있는데, 그 때 컬럼의 길이 값으로 활용된다.

  - @Column 을 생략할 경우 필드의 이름을 테이블 컬럼 이름으로 사용한다. 참고로 지금처럼 스프링부트와 통합해서 사용하면 필드 이름을 테이블 컬럼 명으로 변경할 때 객체 필드의 카멜 케이스를 테이블 컬럼의 언더스코어로 자동으로 변환해준다. (itemName -> item_name, 따라서 위 예제의 @Column(name= "item_name") 를 생략해도 된다.)

 

  • JPQL 직접 사용하기
public interface SpringDataJpaItemRepository extends JpaRepository<Item, Long> {

    // 쿼리 메서드 기능
    List<Item> findByItemNameLike(String itemName);

    // 쿼리 직접 실행
    @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 을 사용하고 싶을 때는 @Query와 함께 JPQL을 작성하면 된다. 이 때는 메서드 이름으로 실행하는 규칙은 무시된다.

- 스프링 데이터 JPA는 JPQL 뿐만 아니라 JPA의 네이티브 쿼리 기능도 지원하는데, JPQL 대신에 SQL 을 직접 작성할 수 있다.

 

 

 

 

 

 

인프런 ) 스프링DB 2편 - 데이터 접근 활용 기술 (김영한)

반응형

'Back > DB' 카테고리의 다른 글

[DB] 스프링 트랜잭션  (0) 2023.05.18
[DB] QueryDSL  (0) 2023.05.12
[DB] MyBatis  (0) 2023.05.09
[DB] JdbcTemplate  (0) 2023.05.08
[DB] H2 데이터베이스 설치  (0) 2023.05.04