코딩공작소

JPA 프로그래밍 학습 정리 (9) - 스프링 데이터 JPA(1) 본문

어플리케이션개발/JPA

JPA 프로그래밍 학습 정리 (9) - 스프링 데이터 JPA(1)

안잡아모찌 2024. 2. 19. 16:19

스프링 데이터 JPA

기존의 CRUD문제를 세련된 방법으로 해결한다. 구현 클래스 없이 인터페이스만으로 데이터 접근계층에 접근할 수 있는 개발방법이다.

public interface MemberRepository extends JpaRepository<Member, Long>{
	Member findByUsername(String username);
}

public interface ItemRepository extends JpaRepository<Item, Long> {}

스프링 프레임워크와 JPA를 함께 사용한다면 스프링 데이터 JPA 사용을 적극 추천한다.

 

스프링 데이터 JPA 설정

<!-- 스프링 데이터 JPA 라이브러리 -->
<dependency>
	<groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
</dependency>


<!-- 환경설정 XML 설정 -->
<jpa:repositories base-package = "jpabook.jpashop.repository" />
@Configuration
@EnableJpaRepositories(basePackages = "jpabook.jpashop.repository")
public class AppConfig {}

스프링 설정에 JavaConfig를 사용해줘야 한다.

 

 

공통 인터페이스 기능

1
2
3
4
public interface JpaRepository<T, ID extends Serializable> extends
PagingAndSortingRepository<T, ID>{...}
 
public interface MemberRepository extends JpaRepository<Member, Long> {}
cs

회원 엔티티와 회원 엔티티의 식별자 타입을 지정해준다.

JpaRepository 인터페이스의 주요 메소드
  • save(S) : 새로운 엔티티는 저장, 이미 있는 엔티티는 수정
  • delete(T) : 엔티티 하나를 삭제
  • findOne(ID) : 엔티티 하나를 조회
  • getOne(ID) : 엔티티를 프록시로 조회한다
  • findAll() : 모든 엔티티를 조회한다

 

 

쿼리 메소드 기능

메소드 이름만으로 쿼리를 생성하는 기능이 있다.

  • 메소드 이름으로 쿼리 생성
  • 메소드 이름으로 JPA NamedQuery 호출
  • @Query 어노테이션을 사용해서 리포지토리 인터페이스에 쿼리 직접 정의

 

메소드 이름으로 쿼리 생성

정해진 규칙에 의해 이름을 정하면 자동으로 쿼리가 생성된다.

https://docs.spring.io/spring-data/jpa/docs/current/api/index.html

JPA 공식 문서에서 해당 쿼리들을 확인할 수 있다.

 

JPA NamedQuery

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public interface MemberRepository extends JpaRepository<Member, Long>{
    List<Member> findByUsername(@Param("username"String username);
}
 
 
//같은 방식
@Entity
@NamedQuery(
    name = "Member.findByUsername",
    query = "select m from Member m where m.username = :username"
)
public class Member {...}
 
<named-query name = "Member.findByUsername">
    <query>...</query>
 
 
 
// 쿼리 호출
Member.findByUsername
cs

스프링 데이터 JPA에서 실행할 Named쿼리가 없으면 메소드 이름으로 쿼리 생성 전략을 사용한다.

 

@Query, 리포지토리 메소드에 쿼리 정의

실행 시점에 문법 오류를 발견할 수 있는 장점이 있다.

1
2
3
4
5
6
7
8
9
10
11
12
public interface MemberRepository extends JpaRepository<Member, Long>{
 
    @Query("select m from Member m where m.username = ?1")
    Member findByUsername(String username);
}
 
public interface MemberRepository extends JpaRepository<Member, Long>{
 
    @Query(value = "SELECT * FROM MEMBER WHERE USERNAME = ?0",
        nativeQuery = true)
    Member findByUsername(String username);
}
cs

직접 쿼리를 정의할수도 있고 nativeSQL도 nativeQuery 속성여부를 통해 사용할 수 있다.

📌 파라미터 바운딩은 위치기반 보다는 이름기반 바인딩을 사용하자

 

 

벌크성 수정 쿼리

기존에 executeUpdate() 함수를 통해 수행했던 쿼리를 스프링 데이터 JPA를 통해서 할 수 있다.

1
2
3
4
@Modifying
@Query("update Product p set p.price = p.price * 1.1
        where p.stockAmount < :stockAmount")
int bulkPriceUp(@Param("stockAmount"String stockAmount);
cs

벌크성 쿼리를 실행하고 나서 영속성 컨텍스트를 초기화하고 싶으면 @Modifying(clearAutomatically = true)처럼 옵션을 사용해주면 된다.

 

반환 타입
  • List<Member> findByName(String name) : 한 건이상 컬렉션, 조회 결과가 없으면 빈 컬렉션을 반환
  • Member findByEmail(String email) : 단건이면 반환타입지정, 조회 결과가 없으면 null을 반환

 

 

페이징과 정렬

  • org.springframework.data.domain.Sort : 정렬 기능
  • org.springframework.data.domain.Pageable : 페이징 기능
1
2
3
4
5
6
7
//count 쿼리 사용
Page<Member> findByName(String name, Pageable pageble);
 
//count 쿼리 사용 안함
List<Member> findByName(String name, Pageble pageable);
 
List<Member> findByName(String name, Sort sort);
cs

 

  • 검색 조건 : 이름이 김으로 시작하는 회원
  • 정렬 조건 : 이름으로 내림차순
  • 페이징 조건 : 첫 번째 페이지, 페이지당 보여줄 데이터는 10건
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public interface MemberRepository extends Repository<Member, Long> {
 
    Page<Member> findByStartingWith(String name,
        Pageable Pageable);
}
 
//페이징 조건과 정렬 조건 설정
PageRequest pageRequest =
    new PageRequest(010new Sort(Direction.DESC, "name"));
 
Page<Member> result = 
    memberRepository.findByNameStartingWith("김"pageRequest);
 
 
List<Member> members = result.getContent(); //조회된 데이터
int totalPages = result.getTotalPages(); //전체 페이지 수
boolean hasNextPage = result.hasNextPage(); //다음 페이지 존재 여부
cs

Page인터페이스도 다양한 메소드를 제공하니 공식문서를 찾아보자

이로써 Pageable, Page를 통해 페이징 처리를 개발할 수 있다.

 

사용자 정의 리포지토리 구현

1
2
3
4
5
6
7
8
9
10
11
12
public interface MemberRepositoryCustom{
    public List<Member> findMemberCustom();
}
 
public class MemberRepositoryImpl implements MemberRepositoryCustom {
 
    @Override
    public List<Member> findMemberCustom(){...}
}
 
public interface MemberRepository
    extends JpaRepository<Member, Long>, MemberRepositoryCustom {}
cs

 

 

WEB 확장

스프링 데이터가 제공하는 Web 확장 기능을 활성화하려면 @EnableSpringDataWebSupport 어노테이션을 사용해야 한다.

@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
public class WebAppConfig {...}

 

 

도메인 클래스 컨버터 기능

도메인 클래스 컨버터는 HTTP 파라미터로 넘어온 엔티티의 아이디로 엔티티 객체를 찾아서 바인딩해준다.

1
2
3
4
5
6
7
8
9
10
11
12
@RequestMapping()
public String memberUpdateForm(@RequestParam("id") Long id, Model model){
    Member member = memberRepository.findOne(id); // 회원을 찾는다.
    ...   
}
 
 
@RequestMapping()
public String memberUpdateForm(@RequestParam("id") Member member, Model model){
    model.addAttribute("member", member);
    ... 
}
cs

 

 

페이징과 정렬 기능

HandlerMethodArgumentResolver 를 제공한다.

  • 페이징 기능 : PageableHandlerMethodArgumentResolver
  • 정렬 기능 : SortHandlerMethodArgumentResolver
1
2
3
4
5
6
@RequestMapping()
public String list(Pageable pageble, Model model) {
    Page<Member> page = memberService.findMembers(pageable);
    model.addAtrribute("members", page.getContent());
    return "members/memberList";
}
cs
  • page : 현재 페이지, 0부터 시작
  • size : 한 페이지에 노출할 데이터 건수
  • sort : 정렬 조건을 정의한다.

/members?page=0&size=name,desc&sort=address.city

 

접두사

사용해야 할 페이징 정보가 둘 이상이면 접두사 @Qualifier 어노테이션을 사용한다.

public String list(
	@Qualifier("member") Pageable memberPageable,
    @Qualifier("order") Pageable orderPageber, ...

/members?member_page=0&order_page=1

 

기본값

Pageable의 기본값은 page=0, size=20이다.

@PageableDefault 어노테이션을 통해 기본값을 변경할 수 있다.

@RequestMapping()
public String list(@PageableDefault(size = 12, sort = "name",
	direction = Sort.Direction.DESC) pageable pageable) { ... }

 

 

스프링 데이터 JPA는 simpleJpaRepository 클래스가 구현한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID extends Serializable>
    implements JpaRepository<T, ID>, JpaSpecificationExecutor<T> {
 
    @Transactional
    public <extends T> S save(S entity){
        if(entityInformation.isNew(entity)){
            em.persist(entity);
            return entity;
        } else {
            return em.merge(entity);
        }
    }
}
cs

 

 

 

https://taehoon9393.tistory.com/376

 

JPA 프로그래밍 학습 정리 (9) - 스프링 데이터 JPA(2)

JPA 샵에 적용해보자 환경설정 Repository Refactoring 명세 적용 기타 환경설정 1 2 3 4 5 6 7 8 9 10 org.springframework.data spring-data-jpa 1.8.0.RELEASE

taehoon9393.tistory.com