JPQL
엔티티 객체를 대상으로 쿼리를 작성하는 객체지향 쿼리언어
JPQL은 왜 등장했을까?
JPA를 사용하면 엔티티 객체를 중심으로 개발하게 됩니다.
검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색하게 되죠.
데이터가 수 만, 수십 만개가 있을 때 JPA만으로 모든 DB데이터를 객체로 변환해서 검색하는 것은 사실상 불가능합니다.
그래서 애플리케이션이 필요한 데이터만 DB에 불러오려면 결국 검색 조건이 포함된 SQL이 필요했습니다.
그래서 JPQL이 등장하게 되었습니다.
JPQL 특징
- JPA에서 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 제공합니다.
- SQL 문법과 유사합니다 ( SELECT ,FROM, WHERE, GROUP BY, HAVING, JOIN 지원 )
- JPQL은 엔티티 객체를 대상으로 쿼리합니다.
- 반면에 SQL은 데이터베이스 테이블을 대상으로 쿼리합니다.
JPQL 문법
기존 SQL 문법과 똑같습니다.
예제
List<Member> result =
em.createQuery(
SELECT m FROM Member m WHERE m.username like "%kim%",
Member.class
).getResultList();
FROM Member 에서 Member은 테이블이 아닌 엔티티 객체를 의미합니다.
테스트
가장 먼저 main 함수로 테스트를 해보겠습니다.
데이터가 잘 들어옴을 알 수 있습니다.
하지만 인프런의 "김영한" 님이 말씀하시기를 테스트 코드를 필수로 작성할 줄 알아야 한다고 합니다.
그래서 테스트 코드를 통해 해당 쿼리가 정상작동하는지 보겠습니다.
테스트 코드
[ 구성도 ]
1. DIP를 지키기 위해 MemberRepository Interface 상속하여 JPQLMemberRepository 라는 클래스를 만들어서 테스트
2. persistence.xml 에 table = create로 테스트 진행 ( 테스트할 때만 create 해야됨 )
3. save(), findByName() 메소드 테스트
4. 생성자 주입을 이용
[ 의문점1]
- 기존에 배웠던 빈 등록과, 의존성 주입은 아래의 과정을 거칩니다.
1) SpringConfig 라는 IoC컨테이너에 빈을 주입한다.
2) 해당 빈을 사용하는 부분에서 @Autowired 를 통해 의존관계 주입을 한다.
- 그런데 JPQLMemberRepository에서의 EntityManger(em)과 Test Code에서의 em은 같은 객체인가?
에 대한 개념을 이해하기 위해 따로 정리하였습니다.
https://tjdwns4537.tistory.com/66
[ 의문점1 해결완료 ]
즉, em은 쓰레드간에 공유를 하면 안되는다는 의미입니다.
그래서 각각의 em으로 영속해주는 방식을 사용하면 됩니다.
[ 의문점2 ]
EntitnyManger 은 의존관계를 어떻게 하는가?
1) JPQLMemberRepository 에서는 EntityManagerFactory를 직접 생성하지 않습니다.
애플리케이션 로딩 시점에 생성되기 때문에 EntityManager를 생성자에서 피라미터로 받게 하였습니다.
2) IoC컨테이너에서 Repository에 대한 빈을 등록해야하는데, EntityManager를 어떻게 넣어줘야 하는가?
[ 의문점2 의 설명 ]
EntityManager 는 트랜잭션 단위로 생성하고, EntityManagerFactory는 DB당 하나를 만들어야 하는데,
클라이언트 코드에서 enf를 만들 것이니, IoC컨테이너에서는 enf를 만들 수 없다.
그렇다면 IoC컨테이너의 빈을 등록할 때, enf를 넣도록 해야하는가 ?
[ 의문점2의 해결시도1 ]
- EntityManager는 @PersistenceContext로 주입을 해줄 수 있습니다. (최신버전은 Autowired로도 가능)
그래서 Autowired를 이용해 테스트 코드를 작성해봤습니다.
[ JPQLMemberRepository ]
@Transactional
@Repository
public class JPQLMemberRepository implements MemberRepository{
private final EntityManager em;
@Autowired // EntityManger 주입부분
public JPQLMemberRepository(EntityManager em) {
this.em = em;
}
public EntityTransaction getTx() {
return em.getTransaction();
}
public Member save(Member member) {
em.persist(member);
return member;
}
public void emClose() {
em.close();
}
@Override
public Optional<Member> findByid(Long id) {
return Optional.empty();
}
@Override
public Optional<Member> findByname(String name) {
List<Member> result = em.createQuery(
"SELECT m FROM Member m WHERE m.name = :name",
Member.class
).setParameter("name", name).getResultList();
System.out.println("--------------------------------");
for (Member i : result) {
System.out.println("member:" + i);
}
System.out.println("--------------------------------");
return result.stream().findAny();
}
... // 그외 인터페이스 메소드
}
[ SpringConfig ]
@Configuration
public class SpringConfig {
@Bean //EntityManagerFactory Bean 등록
public EntityManagerFactory getEnf() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("5xik");
return emf;
}
@Bean // EntityManager Bean 등록
public EntityManager getEm() {
return getEnf().createEntityManager();
}
@Bean
public MemberServiceInterface memberService() {
return new MemberServiceImp(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new JPQLMemberRepository(getEm());
}
}
[ TestCode ]
SpringBootTest
@Transactional
class JPQLMemberRepositoryTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
JPQLMemberRepository repository = ac.getBean("memberRepository",JPQLMemberRepository.class);
EntityTransaction tx = repository.getTx();
@Test
@Commit
void insertData() {
tx.begin();
try{
Member member = new Member();
member.setName("sungjun");
member.setPhonenumber("010");
repository.save(member);
Optional<Member> result = repository.findByname(member.getName());
Assertions.assertThat(result).isEqualTo(member);
tx.commit();
} catch (Exception e){
tx.rollback();
} finally {
//repository.emClose();
}
//ac.close();
}
}
그런데 아래와 같은 에러가 발생하였습니다.
caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'JPQLMemberRepository' defined in file [/Users/parksungjun/Desktop/창업동아리/ShoppingMall/out/production/classes/xik/ShoppingMall/Repository/JPQLMemberRepository.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.persistence.EntityManager' available: expected single matching bean but found 2: getEm,org.springframework.orm.jpa.SharedEntityManagerCreator#0
글이 길어졌으므로 에러 해결은 다음 블로깅으로 넘어가서 하도록 하겠습니다.
https://tjdwns4537.tistory.com/67
'JPA(MySQL,H2Database)' 카테고리의 다른 글
임베디드 값 타입 (0) | 2022.08.08 |
---|---|
SQL 내장함수 (0) | 2022.08.05 |
EntityManager (0) | 2022.07.29 |
연관 관계 매핑 (0) | 2022.07.15 |
exception just for purpose of providing stack trace (0) | 2022.07.13 |