Spring Data JPA란?
- Spring Data JPA는 Spring에서 제공하는 모듈이다.
- JPA, 하이버네이트를 몰라도 되어야 한다. -> 추상화의 이점
- JPA를 한 단계 추상화시킨
Repository
라는 인터페이스를 제공함으로써 이루어진다. - Spring Data JPA의
Repository
의 구현에서 JPA를 사용하고 있다. - JPA 구현체의 사용을 감추고, 다양한 지원과 설정 방법을 제공한다.
- JPA 기본 구현체로 Hibernate 사용한다.
- Querydsl 지원를 지원한다.
- 아래와 같은 것들을 사용할 필요가 없다.
- EntityManager 를 직접 사용하지 않는다.
- JPQL을 직접 사용하지 않는다.
- persist(), merge(), close() 를 직접 사용하지 않는다.
- 트랜잭션을 getTransaction(), commit(), rollback() 으로 관리하지 않는다.
- CRUD 처리를 위한 공통 인터페이스 제공한다.
- 공통 인터페이스 종류는 대략 아래와 같다.
- count, delete, deleteAll, deleteAll, deleteById, existsById, findById, save.
- 공통 메소드가 아닐 경우에도 스프링 데이터 JPA가 메소드 이름을 분석해서 JPQL을 실행할 수 있다.
JPA vs Data JPA
JPA만으로 Repository를 구현한다면?
@Repository
public class TeamJpaRepository {
@PersistenceContext
private EntityManager em;
public Team save(Team team) {
em.persist(team);
return team;
}
public void delete(Team team) {
em.remove(team);
}
public List<Team> findAll() {
return em.createQuery("select t from Team t", Team.class)
.getResultList();
}
public Optional<Team> findById(Long id) {
Team team = em.find(Team.class, id);
return Optional.ofNullable(team);
}
public long count() {
return em.createQuery("select count(t) from Team t", Long.class)
.getSingleResult();
}
}
- 위와 같이 JPA만으로 Repository를 구현할 수 있다.
- 이 방식도 나쁘진 않다.
- 하지만 Data JPA의 힘을 빌리면...
Data Jpa를 쓴다면
public interface TeamRepository extends JpaRepository<Team, Long> {
}
위의 한 문장으로 끝...
Data JPA는 더 많은 기능을 제공한다.
JPA만 쓴다면?
@Repository
public class MemberJpaRepository {
@PersistenceContext
private EntityManager em;
public List<Member> findByPage(int age, int offset, int limit){
return em.createQuery("select m from Member m where m.age = :age order by m.username desc")
.setParameter("age", age)
.setFirstResult(offset)
.setMaxResults(limit)
.getResultList();
}
public long totalCount(int age){
return em.createQuery("select count(m) from Member m where m.age = :age", Long.class)
.setParameter("age", age)
.getSingleResult();
}
public int bulkAgePlus(int age){
int resultCount = em.createQuery(
"update Member m set m.age = m.age + 1" +
" where m.age >= :age")
.setParameter("age", age)
.executeUpdate();
return resultCount;
}
}
Data JPA 사용 예시
//현재 실무에서 가장 많이 씀 --> 로딩시점에서 파싱이 이루어지므로 개발할때 안전함
@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();
//컬랙션 바인딩 받을때 (바인딩은 주로 이름기반!)
@Query("select m from Member m where m.username in :name")
List<Member> findByNames(@Param("name") Collection<String> names);
@Query(value = "select m from Member m",countQuery = "select count(m.username) from Member m") //count 쿼리를 따로 정의 해서 최적화할 수 있음
Page<Member> findByAge(int age, Pageable pageable); //count 쿼리 사용
Slice<Member> findByAge(int age, Pageable pageable); //count 쿼리 사용 안함
List<Member> findByAge(int age, Pageable pageable); //count 쿼리 사용 안함
@Modifying(clearAutomatically = true)
@Query("update Member m set m.age = m.age + 1 where m.age >= :age")
int bulkAgePlus(@Param("age") int age);
//@Query(name = "Member.findByUsername") //우선순위가 얘가 더 높아서 굳이 안적어도 됨
List<Member> findByUsername(@Param("username") String username);
List<Member> findByUsernameAndAgeGreaterThan(String username, int age);
Fetch 예시
@Query("select m from Member m left join fetch m.team")
List<Member> findMemberFetchJoin();
@Override
@EntityGraph(attributePaths = {"team"})
List<Member> findAll();
//JPQL + 엔티티 그래프
@EntityGraph(attributePaths = {"team"})
@Query("select m from Member m")
List<Member> findMemberEntityGraph();
//메서드 이름으로 -> 쿼리에서 특히 편리하다
@EntityGraph(attributePaths = {"team"})
List<Member> findByUsername(String username);
@EntityGraph("Member.all")
@Query("select m from Member m")
List<Member> findMemberEntityGraph();
//읽기만 할때는 성능 최적화됨 --> 하지만 그렇게 최적화에 도움을 주진 않음
@QueryHints(value = @QueryHint(name = "org.hibernate.readOnly", value = "true"))
//for update를 날림 --> 아직 몰라도 되는 개념인듯
@Lock(LockModeType.PESSIMISTIC_WRITE)
List<Member> findByUsername(String name);
// == projections == interface로 하는방법과 class로하는 방법이 있음 --> 객체말고 원하는 값만 뽑아 내고싶을 때
List<UsernameOnly> findProjectionsByUsername(@Param("username") String username);
List<UsernameOnlyDto> findProjectionsByUsername(@Param("username") String username);
//네이티브 쿼리
@Query(value = "SELECT m.member_id as id, m.username, t.name as teamName " + "FROM member m left join team t",countQuery ="SELECT count(*) from member",nativeQuery = true)
Page<MemberProjection> findByNativeProjection(Pageable pageable);
사용자 정의 (커스터 마이징)
- 입맛에 맞게 커스터 마이징 할 수 있다.
MemberRepositoryCustom
//사용자 정의 인터페이스
public interface MemberRepositoryCustom {
List<Member> findMemberCustom();
}
MemberRepositoryCustomImpl
@RequiredArgsConstructor
public class MemberRepositoryCustomImpl implements MemberRepositoryCustom{
private final EntityManager em;
@Override
public List<Member> findMemberCustom() {
return em.createQuery("select m from Member m")
.getResultList();
}
}
Controller
- Controller는 다음과 같이 구성된다.
@RestController
@RequiredArgsConstructor
public class MemberController {
private final MemberRepository memberRepository;
@GetMapping("/members/{id}")
public String findMember(@PathVariable("id") Long id){
Member member = memberRepository.findById(id).get();
return member.getUsername();
}
//도메인 클래스 컨버터 --> 복잡해지면 쓰기 어렵고 --> 조회 용도로만 사용해야댐
@GetMapping("/members2/{id}")
public String findMember2(@PathVariable("id") Member member){
return member.getUsername();
}
@GetMapping("/members")
public Page<MemberDto> List(@PageableDefault(size = 5, sort = "username") Pageable pageable){
Page<Member> page = memberRepository.findAll(pageable);
Page<MemberDto> map = page.map(member -> new MemberDto(member.getId(), member.getUsername(), null));
return map;
}
//@PostConstruct
public void init(){
for(int i = 0; i<100; i++){
memberRepository.save(new Member("username"+1,1));
}
}
}
Reference
- 김영한님의 Spring Data JPA
- 김은호님의 Spting Data JPA
'Spring Jpa' 카테고리의 다른 글
조회 성능 최적화 (0) | 2022.02.27 |
---|---|
값타입 (0) | 2022.02.27 |
OSIV (0) | 2022.02.27 |
JPA 란 (0) | 2022.02.27 |
영속성 전이 (CASCADE) (0) | 2022.02.27 |