JPQL

엔티티 객체를 대상으로 쿼리를 작성하는 객체지향 쿼리언어

 

 

 

 

JPQL은 왜 등장했을까?

JPA를 사용하면 엔티티 객체를 중심으로 개발하게 됩니다.

검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색하게 되죠.

 

데이터가 수 만, 수십 만개가 있을 때 JPA만으로 모든 DB데이터를 객체로 변환해서 검색하는 것은 사실상 불가능합니다.

그래서 애플리케이션이 필요한 데이터만 DB에 불러오려면 결국 검색 조건이 포함된 SQL이 필요했습니다.

그래서 JPQL이 등장하게 되었습니다.

 

 

JPQL 특징
  1. JPA에서 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 제공합니다.
  2. SQL 문법과 유사합니다 ( SELECT ,FROM, WHERE, GROUP BY, HAVING, JOIN 지원 )
  3. JPQL은 엔티티 객체를 대상으로 쿼리합니다.
  4. 반면에 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

 

EntityManager

Entity Manger Entity를 관리하는 역할을 수행하는 클래스 Entity Jpa가 관리하는 객체 EntityMangaerFactory (emf) 고객의 요청이 올때마다 엔티티 매니저를 하나씩 만들어 줍니다. 이 엔티티 매니저는 내부적

tjdwns4537.tistory.com

 

[ 의문점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

 

Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoUnique

 

tjdwns4537.tistory.com

 

'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

+ Recent posts