반응형
해당 글은 김영한 님의 querydsl을 수강하며 정리하려고 적는 포스팅입니다.
실무 활용 - 순수 JPA와 Querydsl
- 순수 JPA 리포지토리와 Querydsl
- 동적쿼리 Builder 적용
- 동적쿼리 Where 적용
- 조회 API 컨트롤러 개발
순수 JPA 리포지토리
package study.querydsl.repository;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import study.querydsl.entity.Member;
import java.util.List;
import java.util.Optional;
import static study.querydsl.entity.QMember.member;
@Repository
public class MemberJpaRepository {
private final EntityManager em;
private final JPAQueryFactory queryFactory;
//테스트 코드짤때 주입할게 하나만 있어서 편하다.
public MemberJpaRepository(EntityManager em) {
this.em = em;
this.queryFactory = new JPAQueryFactory(em);
}
public void save(Member member) {
em.persist(member);
}
public Optional<Member> findById(Long id) {
Member findMember = em.find(Member.class, id);
return Optional.ofNullable(findMember);
}
public List<Member> findAll() {
return em.createQuery("select m from Member m", Member.class)
.getResultList();
}
public List<Member> findByUsername(String username) {
return em.createQuery("select m from Member m where m.username = :username", Member.class)
.setParameter("username", username)
.getResultList();
}
}
순수 JPA 리포지토리 테스트
package study.querydsl.repository;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import study.querydsl.entity.Member;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
@Transactional
class MemberJpaRepositoryTest {
@PersistenceContext
EntityManager em;
@Autowired
MemberJpaRepository memberJpaRepository;
@Test
public void basicTest() {
Member member = new Member("member1", 10);
memberJpaRepository.save(member);
Member findMember = memberJpaRepository.findById(member.getId()).get();
assertThat(findMember).isEqualTo(member);
List<Member> result1 = memberJpaRepository.findAll();
assertThat(result1).containsExactly(member);
List<Member> result2 = memberJpaRepository.findByUsername("member1");
assertThat(result2).containsExactly(member);
}
}
Querydsl 사용
JPAQueryFactory 스프링 빈 등록
- 아래와 같이 JPAQueryFactory를 스프링 빈으로 등록해서 주입받아 사용해도 된다.
package study.querydsl;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class QuerydslApplication {
public static void main(String[] args) {
SpringApplication.run(QuerydslApplication.class, args);
}
@Bean
JPAQueryFactory jpaQueryFactory(EntityManager em) {
return new JPAQueryFactory(em);
}
}
- 동시성 문제는 없다. Bean으로 등록하여 모든 멀티 쓰레드에서 동시 사용하지만 문제는 없다.
- 왜냐하면 여기서 스프링이 주입해주는 EntityManager는 실제 동작 시점에 진짜를 주입받는 프록시용 가짜이다.
- 프록시용 가짜 EntityManager는 실제 사용 시점에 트랜젝션 단위로 모두 다른 실제 EntityManager(영속성 컨텍스트)에 바인딩되도록 라우팅 해준다. 즉, 동시성 문제와 관계없이 트랜잭션 단위로 따로 분리되어 동작한다는 것이다.
- 정리하자면 JPAQueryFactory의 동시성 문제는 entityManager에 의존하기에 Spring과 엮어서 사용하게 되면 동시성 문제과 관계없이 트랜잭션 단위로 분리되어 동작한다고 보면 된다.
순수 JPA 리포지토리 - Querydsl 추가
package study.querydsl.repository;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import study.querydsl.entity.Member;
import java.util.List;
import java.util.Optional;
import static study.querydsl.entity.QMember.member;
@Repository
@RequiredArgsConstructor
public class MemberJpaRepository {
private final EntityManager em;
private final JPAQueryFactory queryFactory;
//@RequiredArgsConstructor 써서 코드를 간결하게 할 수도 있고 좋다.
// public MemberJpaRepository(EntityManager em, JPAQueryFactory queryFactory) {
// this.em = em;
// this.queryFactory = queryFactory;
// }
...
public List<Member> findAll_Querydsl() {
return queryFactory
.selectFrom(member)
.fetch();
}
...
public List<Member> findByUsername_Querydsl(String username) {
return queryFactory
.selectFrom(member)
.where(member.username.eq(username))
.fetch();
}
}
package study.querydsl.repository;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import study.querydsl.entity.Member;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
@Transactional
class MemberJpaRepositoryTest {
@PersistenceContext
EntityManager em;
@Autowired
MemberJpaRepository memberJpaRepository;
@Test
public void basicQuerydslTest() {
Member member = new Member("member1", 10);
memberJpaRepository.save(member);
Member findMember = memberJpaRepository.findById(member.getId()).get();
assertThat(findMember).isEqualTo(member);
List<Member> result1 = memberJpaRepository.findAll_Querydsl();
assertThat(result1).containsExactly(member);
List<Member> result2 = memberJpaRepository.findByUsername_Querydsl("member1");
assertThat(result2).containsExactly(member);
}
}
반응형
'Java' 카테고리의 다른 글
[Querydsl] 동적 쿼리와 성능 최적화 조회 - where 조건 절 파라미터 (0) | 2023.01.17 |
---|---|
[Querydsl] 동적 쿼리 성능 최적화 조회 - DTO, Builder 사용 (0) | 2023.01.16 |
[JPA] SQL function 호출하기 (0) | 2023.01.16 |
[JPA] Querydsl 수정, 삭제 벌크 연산 (0) | 2023.01.15 |
[JPA] 동적 쿼리 - BooleanBuilder 사용 (0) | 2023.01.14 |