일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 제이피큐엘쿼리
- 자바제너릭
- JPA Hint & Lock
- springboot기본설정
- JPA값타입
- 스프링부트기본설정
- dockercmd
- 데이터베이트h2
- JPAproxy
- JDBC connection pool
- spring
- gitinitial
- 스프링부트
- Git
- springbootproxy
- OSIV
- 임베디드타입
- jpa
- 이해와 원리
- embededtype
- springbootH2
- MySqlType
- JPA프록시
- jpqlquery
- sql
- 에이치투데이터베이스
- Open EntityManager
- JPAmapping
- javageneric
- httppie
- Today
- Total
빡코
[스프링 데이터 JPA] 예제 도메인, 공통인터페이스?, 쿼리 메서드, 반화타입 본문
OverView
package study.datajpa.repository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.*;
import org.springframework.data.repository.query.Param;
import study.datajpa.dto.MemberDto;
import study.datajpa.entity.Member;
import javax.persistence.Entity;
import javax.persistence.LockModeType;
import javax.persistence.QueryHint;
import java.util.List;
import java.util.Optional;
public interface MemberRepository extends JpaRepository<Member, Long> {
List<Member> findByUsernameAndAgeGreaterThan(String username, int age);
//NamedQuery
@Query(name = "Member.findByUserName")
List<Member> findByUsername(@Param("username") String username);
@Query("select m from Member m where m.username = :username and m.age =:age")
List<Member> findUser(@Param("username") String username, @Param("age") int age);
//
@Query("select m.username from Member m")
List<String> findUserNameList();
//DTO 조회
@Query("select new study.datajpa.dto.MemberDto(m.id, m.username, t.name) from Member m join m.team t")
List<MemberDto> findMemberDto();
List<Member> findListByUsername(String username); //컬렉션
Member findMemberByUsername(String Username);//단건
//Optional<Member> findByUsername2(String username); // 단건 optional
//페이징
@Query(value = "select m from Member m left join m.team t",
countQuery = "select count(m) from Member m")
Page<Member> findByAge(int age, Pageable pageable);
//스프링 데이터 JPA 벌크성 업데이트 쿼리
@Modifying(clearAutomatically = true)
@Query("update Member m set m.age = m.age + 1 where m.age >= :age")
int bulkAgeQueryPlus(@Param("age") int age);
@Query("select m from Member m left join fetch m.team") //member와 연관딘 team을 한번에 끍어 온다.
List<Member> findMemberFetchJoin();
//공통메서드오버라이드
@Override
@EntityGraph(attributePaths ={"team"})
List<Member>findAll();
@EntityGraph(attributePaths = {"team"})
@Query("select m from Member m")
List<Member> findMemberEntityGraph();
//@EntityGraph(attributePaths = {"team"})
@EntityGraph("Member.all")
List<Member> findEntityGraphByUsername(@Param("username") String username);
//readOnly는 변경 감치 체크를 하지 않기 때문에, 업데이트를 진행하지 않는다.
@QueryHints(value = @QueryHint( name = "org.hibernate.readOnly", value = "true"))
Member findReadOnlyByUsername(String username);
//Lock 힌트
@Lock(LockModeType.PESSIMISTIC_WRITE)
List<Member> findLockByUsername(String username);
}
샘플 도메인 설계
공통 인터페이스
스프링 부트 사용시 @SpringBootApplication 위치를 지정( 해당패키지와 하위 패키지 인식) 만약 위치가 달라지면@EnableJpaRepositories 필요
@Configuration
@EnableJpaRepositories(basePackages ="jpabook.jpashop.repository")
publicclassAppConfig{
}
JpaRepository<Member, Long> : GenericT: 엔티티타입ID: 식별자타입(PK)
package study.datajpa.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import study.datajpa.entity.Member;
public interface MemberRepository extends JpaRepository<Member, Long> {
}
MemberRepository에는 구현 메서드가 전혀 없는데 어떻게 실행이 되는 걸까? 스프링 데이터 JPA가 구현 클래스 대신 생성
@Repository 어노테이션 생략 가능
컴포넌트 스캔을 스프링 데이터 JPA가 자동으로 처리
JPA 예외를 스프링 예외로 변환하는 과정도 자동으로 처리
주요 메서드
save(S) : 새로운 엔티티는 저장하고 이미 있는 엔티티는 병합한다.
delete(T) : 엔티티 하나를 삭제한다. EntityManager.remove() 내부에서 호출
findById(ID) : 엔티티 하나를 조회한다. EntityManager.find()내부에서 호출
getOne(ID) : 엔티티를 프록시로 조회한다. EntityManager.getReference()내부에서 호출
findAll(…) : 모든 엔티티를 조회한다. 정렬( Sort )이나 페이징( Pageable ) 조건을 파라미터로 제공할
수 있다.
공통인터페이스라는 제약 아래서 내가 원하는 특정 쿼리를 만들고 싶다면?
1. 쿼리 메서드 기능
스프링 데이터 JPA는 메소드 이름을 분석해서 JPQL을 생성하고 실행
쿼리 메소드 필터 조건
스프링 데이터 JPA 공식 문서 참고: (https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation )
스프링 데이터 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
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.limit-query-result
순수 JPA
public List<Member> findByUsernameAndAgeGreaterThan(String username, int age) {
return em.createQuery("select m from Member m where m.username = :username" +
" and m.age > :age")
.setParameter("username",username)
.setParameter("age",age)
.getResultList();
}
@Test
public void findByUsernameAndAgeGreaterThan() throws Exception {
Member m1 = new Member("AAA", 10);
Member m2 = new Member("AAA", 20);
memberJpaRepository.save(m1);
memberJpaRepository.save(m2);
List<Member> result = memberJpaRepository.findByUsernameAndAgeGreaterThan("AAA", 15);
assertThat(result.get(0).getUsername()).isEqualTo("AAA");
assertThat(result.get(0).getAge()).isEqualTo(20);
assertThat(result.size()).isEqualTo(1);
}
스프링 데이터 JPA
public interface MemberRepository extends JpaRepository<Member, Long> {
List<Member> findByUsernameAndAgeGreaterThan(String username, int age);
}
select member0_.member_id as member_i1_0_, member0_.age as age2_0_, member0_.team_id as team_id4_0_, member0_.username as username3_0_
from member member0_
where member0_.username=? and member0_.age>?
select member0_.member_id as member_i1_0_, member0_.age as age2_0_, member0_.team_id as team_id4_0_, member0_.username as username3_0_
from member member0_
where member0_.username='AAA' and member0_.age>15;
2. JPA NamedQuery (실무 사용x)
-쿼리의 이름을 부여하고 호출함
//Meber Entity
@Entity
@NamedQuery(
name = "Member.findByUsername",
query = "select m from Member m where m.username =:username")
public class Member {
}
//순수 JPA Repository
//JPA Namedquery
public List<Member> findByUsername(String username) {
return em.createNamedQuery("Member.findByUsername", Member.class)
.setParameter("username", username)
.getResultList();
}
//스프링 데이터 JPA로 NamedQuery 호출
public interface MemberRepository extends JpaRepository<Member, Long> {
//NamedQuery
@Query(name = "Member.findByUserName")
List<Member> findByUsername(@Param("username") String username);
}
//TEST 코드
@Test
public void testNamedQuery() throws Exception {
Member m1 = new Member("AAA", 10);
Member m2 = new Member("AAA", 20);
memberRepository.save(m1);
memberRepository.save(m2);
List<Member> result = memberRepository.findByUsername("AAA");
Member findMember = result.get(0);
assertThat(findMember).isEqualTo(findMember);
}
//실행 쿼리
select
member0_.member_id as member_i1_0_,
member0_.age as age2_0_,
member0_.team_id as team_id4_0_,
member0_.username as username3_0_
from
member member0_
where
member0_.username=?;
@Query를 생략하고 메서드 이름만으로 Named 쿼리를 호출할 수있다.
3. @Query, 리포지토리 메소드에 쿼리 정의하기 (실무에서 활용)
-동적 쿼리의 경우 querydsl 권장
public interface MemberRepository extends JpaRepository<Member, Long> {
@Query("select m from Member m where m.username = :username and m.age =:age")
List<Member> findUserName(@Param("username") String username, @Param("age") int age);
}
실행쿼리
select
member0_.member_id as member_i1_0_,
member0_.age as age2_0_,
member0_.team_id as team_id4_0_,
member0_.username as username3_0_
from
member member0_
where
member0_.username=?
and member0_.age=?
4. @Query, 값, DTO 조회하기
public interface MemberRepository extends JpaRepository<Member, Long> {
@Query("select m.username from Member m")
List<String> findUserNameList();
//DTO 조회
@Query("select new study.datajpa.dto.MemberDto(m.id, m.username, t.name) from Member m join m.team t")
List<MemberDto> findMemberDto();
}
package study.datajpa.dto;
import lombok.Data;
@Data
public class MemberDto {
private Long id;
private String username;
private String name;
public MemberDto(Long id, String username, String name) {
this.id = id;
this.username = username;
this.name = name;
}
}
5. 파라미터 바인딩
public interface MemberRepository extends JpaRepository <Member, Long> {
@Query("select m from Member m where m.username = :name")
Member findMembers(@Param("name") String username);
}
반환 타입
List<Member> findListByUsername(String username); //컬렉션
Member findMemberByUsername(String Username);//단건
Optional<Member> findByUsername2(String username); // 단건 optional
Supported Query Return Types
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repository-query-return-types
조회결과가 많거나 없으면?
컬렉션
결과 없음: 빈컬렉션반환
단건조회
결과 없음:null반환
결과가 2건 이상:javax.persistence.NonUniqueResultException 예외 발생
참고: 단건으로 지정한 메서드를 호출하면 스프링데이터 JPA는 내부에서 JPQL의 Query.getSingleResult() 메서드를 호출한다. 이 메서드를 호출했을 때 조회 결과가 없으면 javax.persistence.NoResultException 예외가 발생하는데 개발자입장에서 다루기가 상당히 불편하다. 스프링데이터 JPA는 단건을조회할 때 이 예외가 발생하면 예외를 무시하고 대신에 null을 반환한다.
'Java > JPA' 카테고리의 다른 글
[스프링 데이터 JPA] fetchJoin과 EntityGrap (0) | 2023.05.11 |
---|---|
[스프링 데이터 JPA] 벌크성 수정 쿼리 (0) | 2023.05.11 |
[스프링 데이터 JPA] 기초 설정 (0) | 2023.05.10 |
[JPA][개념] 값 타입 (0) | 2023.03.23 |
[JPA][개념] 프록시와 연관관계 정리 (0) | 2023.03.23 |