Spring Data JPA
Repository
Controller - Service - Repository - DataBase 의 Repository가 맞다
JPA를 사용하지 않았을 때의 Repository 작성은 매우 힘들었다.
직렬화, 반직렬화, stream에서 map()을 사용할때 이 map에 뭐가 많이 들어가고 등등..
공부는 많이 되었고 머릿속에도 조금 남아있지만 이 Repository를 계속 작성하는데에는 수고가 많이 든다.
그래서 Spring Data JPA에서는 DB - Repository 접근에 대한 인터페이스, JpaRepository를 제공하고 있다.
이 인터페이스는
CrudRepository - > PagingAndSortingRepository - > JpaRepository로 이어지는 상속구조를 가진다.
package org.springframework.data.jpa.repository;
import java.util.List;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.ListCrudRepository;
import org.springframework.data.repository.ListPagingAndSortingRepository;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.query.QueryByExampleExecutor;
@NoRepositoryBean
public interface JpaRepository<T, ID>
extends ListCrudRepository<T, ID>,
ListPagingAndSortingRepository<T, ID>,
QueryByExampleExecutor<T> {
// 생략
}
위 코드는 JpaRepository 인터페이스다.
import로 Sort, Crudrepository, ListPagingAndSortingRepository 를 받고 있으며
마찬가지로 extends를 통해 ListCrudRepository, ListPagingAndSortingRepository를 상속받고있다.
ListCrudRepository는 CrudRepository를 상속받고 있으며 얘네들은 기본적인 CRUD를 제공해준다.
ListPagingAndSortingRepository는 PagingAndSortingRepository를 상속받고 있으며 얘네들을 통해 페이징, 정렬을 할 수 있다.
따라서 Repository 계층에서 JpaRepository를 상속받으면 CRUD + 페이징 + 정렬 기능을 쓸 수 있다!
Entity
@Entity
@Getter
@Setter
@Table(name = "member")
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column
private String username;
@Column
private int age;
}
Repository, 상속만 해도 기본적인 CRUD를 사용할 수 있다
public interface MemberRepository extends JpaRepository<Member, Long> {
}
※ extends JpaRepository<Entity 클래스명, PK의 타입>
PK의 타입을 적을때 기본형을 적으면 안되고 참조형을 적어야한다. 가령 entity에서는 long타입이면 참조형인 Long을 써야함
null값의 유무를 판단하기 위해 고려한 것으로 보인다.
기본형은 null값이 들어올 수 없지만 참조형은 가능하니까
동작 원리에 대해서
1. @EnableJpaRepositories가 자동으로 Spring Bean으로 등록된다
- Spring Boot에서는 @SpringBootApplication 내부에서 @EnableJpaRepositories가 자동으로 활성화된다
- @EnableJpaRepositories가 동작하면 JpaRepositoriesRegistrar가 import되어서 Repository관련 Bean 등록이 시작된다..
2. SimpleJpaRepository가 내부적으로 구현체로 사용된다.
- Repository의 내부 구현체로 기본적으로 제공된다.
- JpaRepositoryFactory는 인터페이스를 보고 내부적으로 SimpleJpaRepository로 연결한다고 한다.(Factory -> Repository)
- **SimpleJpaRepository는 EntityManager를 사용해서 모든 DB 접근 로직을 수행한다고 함.
- save(), findById(), delete() 등이 SimpleJpaRepository에 정의되어있다.
3. 프록시 패턴으로 런타임에 Spring이 구현체를 만들어서 주입한다.
※프록시 패턴(Proxy Pattern)에 대해서는 컴퓨터 프로그래밍이므로 따로 공부해야겠음
- Spring AOP 기반 동적 프록시를 생성한다
- JpaRepositoryFactory가 만든 프록시 객체가 실제 개발자???에게 주입된다
- 프록시 내부에서는 SimpleJpaRepository를 위임해서 실행한다
- 개발자는 인터페이스만 정의하고 실제 로직은 프록시 + SimpleJpaRepository가 처리한다!!
Service
@Service
@RequiredArgsConstructor
public class BasicIndexDataService implements IndexDataService {
private final IndexInfoRepository infoRepository;
private final IndexDataRepository dataRepository;
private final IndexDataMapper mapper;
}
이런식으로 Repository를 바로 쓸 수 있다
JpaRepository의 기본 제공 메서드
| save() | 저장 및 수정에 쓰임(PK 유무에 따라 구분가능?) |
| findById() | ID로 단건(단 하나)을 조회한다, ID로 찾기 |
| findAll() | 전체 조회하기. 보통은 쓰지 않고 조건문을 달아서 쓰던지 한다. |
| count() | 전체 갯수를 조회한다, 엔티티의 갯수라던지 |
| delete() | 단건 삭제 |
| deleteAll() | 전체를 삭제한다 |
| existsById() | 존재 여부를 확인한다. 보통 dataRepository.existsById().orElseThrow(() -> new Exception()); 형태로 쓴다 |
| findAll(Pageable) | 조회를 하는데, 페이징 처리된 조회를 한다 |
| finaAll(sort) | 정렬된 조회를 한다 |
예시
Member member = new Member("B1uffer", 30);
memberRepository.save(member); // 저장
Optional<Member> result = memberRepository.findById(1L); // 해당 id를 가진 member을 찾는다
List<Member> members = memberRepository.findAll(); // 모~든 member를 찾는다
long count = memberRepository.count(); // memberRepository에 있는 갯수를 조회한다
boolean exists = memberRepository.existsById(1L); true 혹은 false, id를 가진 member가 있는지 조회
memberRepository.deleteById(1L); // id에 해당하는 member를 삭제한다
// 아래는 페이징과 정렬임
// PageRequest 타입의 객체를 만드는데 PageRequest로 만듬, 정렬도 안에서 해버림
PageRequest pageRequest = PageRequest.of(0, 10, Sort.by("username"));
Page<Member> page = memberRepository.findAll(pageRequest);
쿼리 메서드 작성 규칙 : Repository에서 사용할 수 있음
Spring Data JPA에서는 메서드명을 통해 SQL을 자동으로 생성하는 기능을 제공한다.
메서드 이름이 곧 쿼리임
다르게 말하면, 메서드 명을 다르게 적으면 쿼리가 맞질 않아서 Repository와 DB가 서로 연결되지 않는다.
| 키워드 | 의미 |
| findBy | ~를 통해 조회한다 |
| countBy | 뒤에 오는 무언가가 들어있는 갯수를 조회함 |
| deleteBy | ~가 들어있는 것을 삭제함 |
| existsby | 있는가? 없는가? 의 존재여부(boolean) |
| TopN | 상위 N개 쿼리문 |
| First | 첫번째 |
예시
// username을 포함하고 있는 Member들을 찾는다
List<Member> findByUsername(String username);
// SELECT m FROM Member AS m WHERE m.username = ?1 < - ?1이 뭐임
// GraterThan도 쿼리문이다. 이런게 있음
List<Member> findByAgeGreaterThan(int age);
// SELECT m FROM Member AS m WHERE m.age > ?1
// 인자로 받은 username과 age가 모두 같은 Member를 찾는다. 홍길동 30살 이런거
List<Member> findByUsernameAndAge(String username, int age);
// SELECT m FROM Member AS m WHERE m.username = username, m.age = age;
// 맨 위에 있는 member 3개를 찾는데, 나이에 대해서 내림차순으로 정렬한다.
List<Member> findTop3ByOrderByAgeDesc();
// SELECT m FROM Member AS m ORDER BY m.age DESC LIMIT 3;
* JpaRepository만 써도 기본적인 CRUD는 충분하다
* 복합쿼리는 QueryDSL, @Query()로 해결할 수 있다. << 공부가 더 필요함
* 메서드명으로 해결 가능한 범위 : 단순한 조건 + And/Or/GreaterThan정도.. 그 이상은 위의 복합쿼리처리를 해야함
* 페이징, 정렬은 Pageable, sort를 적극적으로 활용한다 << 페이지네이션도 있고, 슬라이도 있다고 합니다.
* Optional 반환 구현을 통해서 NPE를 방지할 수 있음. << 이거 매우 중요해보임. NPE 발생했었음.. ㅋㅋ
'Spring Boot' 카테고리의 다른 글
| 즉시 로딩(EAGER), 지연 로딩(LAZY) (3) | 2025.07.26 |
|---|---|
| Spring Data JPA : 영속성 전이(Cascade) (0) | 2025.07.25 |
| Entity : 연관관계 맵핑 (3) | 2025.07.25 |
| Spring Data JPA : 구조 이해 (3) | 2025.07.23 |
| Spring Data JPA : 프로젝트 설정 (0) | 2025.07.22 |