JPQL( Java Persistence Query Language )
。특정DB에 종속되지 않는Entity중심의객체지향 쿼리 언어
。JPA의 일부로서DB Table이 아닌영속성 컨텍스트내DB Entity를 대상으로SQL Query를 작성
▶Native SQL과 거의 동일하나,Entity Class를 기준으로컴파일러의 도움을 받을 수 있다.
。간단하게Query를 정의하여CRUD를 수행 시 주로 사용하는 방식
▶ 복잡한Query의 경우MyBatis등을 사용.
JPQL특징
JPQL은조회에 특화된쿼리로서쓰기 작업에는 비효율적
。보통조회시에JPQL을 사용하고,쓰기 작업의 경우EntityManager.persist()를 활용해서영속성 컨텍스트에영속화후작업을 수행
▶QueryDSL은JPQL을 생성하는 기능이므로, 마찬가지로조회 목적으로만 사용
。JPQL로쓰기 작업자체는 가능하지만,EntityManager 방식에 비해비효율적이므로 권고하지는 않는다.
JPQL 문을 작성 시Entity의Alias를 지정하여Query를 작성
。DB Table명으로Query하는게 아닌DB Entity명으로Query를 수행SELECT m.id, m.name, m.password, m.email, m.balance FROM Member m▶
DB Table의컬럼이 아닌,Entity Class의필드를 기준으로 조회
JPQL내인자 전달시매개변수로 입력 시 순서대로JPQL내?1,?2...으로 전달
。 또는WHERE m.loginId = :loginId로:매개변수명으로 직접 명시적으로 할당가능
JPQL의그래프 탐색
。JPQL에서Entity 클래스의연관관계 Entity 필드를 기반으로조건설정 시 다음처럼엔티티별칭.연관관계Entity.필드로참조
▶ 해당연관관계 Entity를그래프 자료구조 탐색처럼탐색을 수행하여참조@Query( value = """ SELECT oi FROM OrderItem oi WHERE oi.orders.code = :orderCode """ ) List<OrderItem> findAllByOrderCode(String orderCode);
JPA에서의JPQL
。EntityFactoryManager을 통해EntityManager 객체를 생성 후JPQL을 사용하여 데이터 조회하기
JPA 설정 XML파일정의
。src/main/resources/META-INF/persistence.xml로 생성
。<class>를 통해영속성 컨텍스트에 등록할DB Entity 클래스를 정의
▶ 이후Hibernate에 의해 생성된ddl이DB로 전송되어 자동으로매핑된DB Table을 생성<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0"> <persistence-unit name="hibernate-exp-6"> <class>model.s03.Member</class> <class>model.s03.Address</class> <class>model.s03.Product</class> <class>model.s03.Category</class> <class>model.s03.OrderItem</class> <class>model.s03.Order</class> <properties> <!-- DB 접속정보 정의 --> <property name="jakarta.persistence.jdbc.url" value="jdbc:h2:mem:test-db;" /> <property name="jakarta.persistence.jdbc.user" value="sa" /> <property name="jakarta.persistence.jdbc.password" value="" /> <property name="jakarta.persistence.schema-generation.database.action" value="create" /> <!-- SQL 로깅 --> <property name="hibernate.show_sql" value="true" /> <property name="hibernate.format_sql" value="true" /> <property name="hibernate.highlight_sql" value="true" /> <property name="hibernate.hbm2ddl.auto" value="create"/> <property name="elipselink.logging.level" value="INFO"/> <property name="elipselink.logging.level.sql" value="FINE"/> <property name="elipselink.logging.parameters" value="true"/> </properties> </persistence-unit> </persistence>
EntityManager 객체생성 및실습을 위한 초기화 코드 작성
。EntityManagerFactory생성 시<persistence-unit> 태그의name 속성을 입력
。EntityManager 객체는 한 작업 당 한객체씩 생성 후 작업을 수행하며, 작업이 끝난 경우자원 해제를 해야한다.
▶ 내부Session에DB Connection을 포함하므로 사용 후자원 해제를 통해DB Connection Pool로 반환
。EntityTransaction 객체를 통해JPA에 의한DB 트랜잭션을 직접 제어
▶Spring에서는@Transactional에 의해추상화됨public class JpqlTests { private static EntityManagerFactory emf; Member m; @BeforeAll public static void setUpStatic() { emf = Persistence.createEntityManagerFactory("hibernate-exp-6"); } @AfterAll public static void tearDownStatic() { emf.close(); } @BeforeEach public void setUp(){ EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); try{ tx.begin(); m = new Member( "정수", "wjd747", "wjdtn%d@naver.com".formatted(747), new Address( "2", "3", "4" ) ); em.persist(m); tx.commit(); } catch(Exception e){ tx.rollback(); throw new RuntimeException("Error Occur",e); } finally { em.close(); } } }▶ 실습을 위한
Member 객체생성 및EntityManager의영속성 컨텍스트에 반영 후EntityManager 객체의자원 해제
JPQL문을 통한SELECT문으로데이터조회
。Query객체.setParameter("변수명","값"):JPQL문내매개변수에변수를바인딩시:변수명으로 지정
。다건조회시Query객체.getResultList()를 통해다건조회된Entity클래스 객체를List<클래스> 객체로 반환@Test void 멤버객체찾기_성공() { EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); try{ tx.begin(); String JPQLTEXT = """ SELECT m FROM Member m WHERE m.email = :email """; TypedQuery<Member> query = em.createQuery(JPQLTEXT, Member.class); query.setParameter("email", "'%s'".formatted(m.getEmail())); Member foundedMember = query.getSingleResult(); Assertions.assertEquals(m.getId(), foundedMember.getId()); tx.commit(); } catch (Exception e) { tx.rollback(); throw new RuntimeException(e); } finally { em.close(); } }
페이지네이션구현하기
페이지네이션
。Query 객체에서offset과limit을 지정하여페이징처리된 결과 도출
▶ 결과 도출 시 우선order by를 통해정렬을 수행해야한다.
query.setFirstResult(pageNum * pageSize).setMaxResults(pageSize);
▶setFirstResult : offset지정 :limit * 페이지번호
▶setMaxResults:limit지정@Test void 페이징_연습(){ int pageSize = 10; int pageNum = 5; EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); String jpqlText = """ SELECT m FROM Member m ORDER BY m.id desc """; try{ tx.begin(); TypedQuery<Member> query = em.createQuery(jpqlText, Member.class); TypedQuery<Member> pagedQuery = query.setFirstResult(pageNum * pageSize) .setMaxResults(pageSize); List<Member> members = pagedQuery.getResultList(); members.stream().map(s -> s.getName()).forEach(System.out::println); tx.commit(); } catch (Exception e){ tx.rollback(); throw new RuntimeException(e); } finally { em.close(); } }
Spring JPA에서의JPQL
。JpaRepository를상속하는인터페이스내@Query 어노테이션을 선언 후 내부에JPQL문을 선언// JPQL 방식 : Entity의 alias를 설정 @Query(""" SELECT EXISTS ( SELECT m FROM MemberEntity m WHERE m.loginId = ?1 ) """) Boolean existsByLoginIdByJPQL(String loginId);@Query(""" SELECT EXISTS ( SELECT m FROM MemberEntity m WHERE m.loginId = :loginId ) """) Boolean existsByLoginIdByJPQL(String loginId);