레이블이 JPA동영상인 게시물을 표시합니다. 모든 게시물 표시
레이블이 JPA동영상인 게시물을 표시합니다. 모든 게시물 표시

2022년 2월 6일 일요일

(자바,JPA동영상)Querydsl을 이용한 JPQL 쿼리, 서브쿼리, EXISTS, ORDER BY, WHERE, 메소드기반쿼리, 스프링 Data JPA, 자바교육, 스프링교육, JPS교육, 닷넷교육, C#교육, JAVA교육

 (자바,JPA동영상)Querydsl을 이용한 JPQL 쿼리, 서브쿼리, EXISTS, ORDER BY, WHERE, 메소드기반쿼리, 스프링 Data JPA, 자바교육, 스프링교육, JPS교육, 닷넷교육, C#교육, JAVA교육


http://ojc.asia/bbs/board.php?bo_table=LecJpa&wr_id=373 


Querydsl을 이용한 JPQL 쿼리, 서브쿼리, EXISTS, ORDER BY, WHERE, 메소드기반쿼리

Querydsl을 이용한 JPQL 쿼리, 서브쿼리, EXISTS, ORDER BY, WHERE, 메소드기반쿼리4장. Querydsl을 이용한 JPQL 쿼리이번 장에서 Querydsl을 이용한 JPQL 작성방법을 상세하게 살펴보겠습니다.4.1. 테스트 프로젝트

ojc.asia



4장. Querydsl을 이용한 JPQL 쿼리

이번 장에서 Querydsl을 이용한 JPQL 작성방법을 상세하게 살펴보겠습니다.


4.1. 테스트 프로젝트 만들기

"부록 7.1 Querydsl JPA Query with MySQL" 부분을 참고하여 진행합니다.


application.properties

환경설정 파일에서 다음처럼 변경합니다.

spring.datasource.initialize=true

spring.jpa.hibernate.ddl-auto=create


4.2. JPA Query 학습


4.2.1. Select

Q 타입 클래스를 사용하여 메소드 기반으로 쿼리를 작성할 수 있습니다.


1. 기본 사용법

DeptDao.java

package com.example.employee.repository;


import javax.persistence.EntityManager;

import javax.persistence.PersistenceContext;

import org.springframework.stereotype.Repository;

import com.example.employee.model.Dept;

import com.example.employee.model.QDept;

import com.querydsl.jpa.impl.JPAQuery;


@Repository

public class DeptDao {

@PersistenceContext

private EntityManager entityManager;


public Dept getByDname(String dname){

    // 쿼리타입 클래스내 static 멤버변수가 가리키는 인스턴스를 그대로 사용할  있다.

QDept dept = QDept.dept;


// 별칭을 생성자에 주고 새로 쿼리타입 클래스의 인스턴스를 만들어 사용할 수도 있다.

//QDept department = new QDept("department");


JPAQuery<?> query = new JPAQuery<Void>(entityManager);

Dept accounting = query.select(dept).from(dept)

.where(dept.dname.eq(dname)).fetchOne();

return accounting;

}

}


질의 결과가 하나의 로우라면 fetchOne 메소드를 사용합니다. 얻고자 하는 결과의 자료형에 따라 메소드를 선택합니다.


제네릭이 Long 인 경우
제네릭이 엔티티 클래스 Dept 인 경우





fetchOne 메소드는 결과가 하나 이상이면 TooManyRowsException 예외가 발생합니다. fetchFirst 메소드는 TooManyRowsException 예외가 발생하지 않고 데이터가 있다면 첫 번째 로우를 리턴합니다. 둘 다 데이터가 없으면 null을 리턴합니다.


DeptDaoTest.java

package com.example.employee.repository;


import static org.junit.Assert.*;

import static org.hamcrest.CoreMatchers.*;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;


import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;


import org.springframework.test.context.junit4.SpringRunner;

import com.example.employee.model.Dept;


@RunWith(SpringRunner.class)

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)

public class DeptDaoTest {

@Autowired

private DeptDao deptDao;


@Test

public void testGetByDname() {

String dname = "ACCOUNTING";

Dept accounting = deptDao.getByDname(dname);

System.out.println(accounting);


assertThat(accounting, is(notNullValue()));

}

}


SQL 로그 

콘솔창에 기록된 SQL쿼리를 살펴보겠습니다. 

결과 로그에서 앨리어스를 'dept0_'에서 'd'로 바꾸어 보기 편하게 수정했습니다.

select 

d.deptno as deptno1_0_, 

d.dname as dname2_0_, 

d.loc as loc3_0_ 

from dept d 

where d.dname='ACCOUNTING'



2. 1차 캐싱

1차 캐싱 서비스를 제공받는지 테스트해 보겠습니다.


DeptDaoTest.java

클래스에 testEntityManagerCaching 메소드를 직접 추가합니다.

@Transactional
@Test

public void testEntityManagerCaching() {

String dname = "ACCOUNTING";


// 쿼리 수행

Dept accounting1 = deptDao.getDeptByDname(dname);

System.out.println(accounting1);


System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~");


// 한번  같은 쿼리를 수행

Dept accounting2 = deptDao.getDeptByDname(dname);

System.out.println(accounting2);


// 테이블의 로우() 일치여부는  값이 같은지 여부로 판단한다

// 객체도 그에 맞게 처리하기 위해서 equals, hashCode 메소드를 

재정의해서 사용한다.

System.out.println(accounting1.hashCode() == accounting2.hashCode());

System.out.println(accounting1.equals(accounting2));


assertThat(accounting1, equalTo(accounting2));


// true 결과는  객체의 메모리 주소가 같다는 것을 의미한다.

System.out.println(accounting1 == accounting2);


assertThat(accounting1, is(accounting2));

assertThat(accounting1, sameInstance(accounting2));

}


EntityManager는 PersistenceContext로 데이터베이스로부터 질의하여 얻은 결과를 담고 있는 엔티티 객체를 보관합니다. 다음에 같은 결과를 요구하는 요청을 받으면 갖고 있는 엔티티 객체를 돌려준다는 것을 확인 할 수 있습니다. 이는 같은 트랜잭션 범위 안에서만 유효합니다. 


JPAQuery, JPAQueryFactory 클래스를 이용하면 엔티티매니저가 제공하는 메소드를 직접 이용할 때와 마찬가지로 동일한 기능을 이용할 수 있습니다.



3. 필터 설정방법


DeptDao.java

public List<String> getDeptDnameWhenEmpCountExist(){

QDept dept = QDept.dept;

JPAQuery<?> query = new JPAQuery<Void>(entityManager);


List<String> dnames = query.select(dept.dname).from(dept)

.where(dept.emps.size().gt(0)).fetch();

return dnames;

}


DeptDaoTest.java

@Test

public void testGetDeptDnameWhenEmpCountExist(){

List<String> dnames = deptDao.getDeptDnameWhenEmpCountExist();

for (String dname : dnames) {

System.out.println(dname);

}


assertThat(dnames.size(), is(3));

}


SQL 로그

select 

d.dname as col_0_0_ 

from dept d 

where (

select 

count(e.deptno) 

from emp e 

where d.deptno=e.deptno

)>0


where 조건절에 서브쿼리를 사용하여 부서에 배정된 직원이 있는 부서명만 조회합니다. 이 요청을 dept.emps.size().gt(0) 코드처럼 객체를 이용하는 방식 그대로 처리할 수 있습니다. 


위 SQL을 exists 구문을 사용하는 것으로 바꾸면 다음과 같습니다.

select dname

from dept

where exists (

select 1 from emp where deptno=dept.deptno)


위 SQL을 사용하도록 요청하는 메소드 기반 쿼리 작성예시는 다음과 같습니다. 

public List<String> getDeptDnameWhenEmpCountExist(){

QDept dept = QDept.dept;

QEmp emp = QEmp.emp;

JPAQuery<?> query = new JPAQuery<Void>(entityManager);

List<String> dnames = query.select(dept.dname).from(dept)

.where(JPAExpressions

.select(Expressions.constant(1))

.from(emp).where(emp.dept.deptno.eq(dept.deptno)).exists()).fetch();

return dnames;

}


4. 쿼리 작성용 Methods

JPAQuery 메소드들은 모두 자신이 속한 객체를 리턴하므로 메소드 체이닝기법으로 "."을 찍고 연속해서 메소드를 사용할 수 있습니다. JPAQuery가 구현한 JPQLQuery 인터페이스의 cascading 메소드들은 다음과 같습니다.


메소드
기능
from
쿼리 대상을 설정한다.
innerJoin, join,
leftJoin, fullJoin, on
조인 부분을 추가한다. 조인 메소드에서 첫 번째 인자는 조인 소스이고, 두 번재 인자는 대상(별칭으로 지정 가능)이다.
where
쿼리에 필터를 추가한다.
가변인자나 and, or 메소드를 이용해서 필터를 추가한다.
groupBy
가변인자 형식의 인자를 기준으로 그룹을 추가한다.
having
Predicate 표현식을 이용해서 "group by" 그룹핑의 필터를 추가한다.
orderBy
정렬 표현식을 이용해서 정렬 순서를 지정한다. 숫자나 문자열에 대해서는 asc()나 desc()를 사용하고, OrderSpecifier에 접근하기 위해 다른 비교 표현식을 사용한다.
limit, offset, restrict
결과의 페이징을 설정한다. limit은 최대 결과 개수, offset은 앞에서부터 건너 뛸 로우의 개수, restrict는 limit과 offset을 함께 정의한다.


5. Order by

EmpDao.java

package com.example.employee.repository;


import java.util.List;

import javax.persistence.EntityManager;

import javax.persistence.PersistenceContext;

import org.springframework.stereotype.Repository;

import com.example.employee.model.Emp;

import com.example.employee.model.QEmp;

import com.querydsl.jpa.impl.JPAQuery;


@Repository

public class EmpDao {

@PersistenceContext

private EntityManager entityManager;


public List<Emp> getEmpOrderByDeptnoAscEmpnoDesc(){

QEmp emp = QEmp.emp;

JPAQuery<?> query = new JPAQuery<Void>(entityManager);

List<Emp> emps = query.select(emp).from(emp)

.orderBy(emp.dept.deptno.asc(), emp.empno.desc()).fetch();

return emps;

}

}


EmpDaoTest.java

package com.example.employee.repository;


import static org.hamcrest.CoreMatchers.is;

import static org.junit.Assert.assertThat;

import java.util.List;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;


import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;


import org.springframework.test.context.junit4.SpringRunner;

import org.springframework.transaction.annotation.Transactional;

import com.example.employee.model.Emp;


@Transactional

@RunWith(SpringRunner.class)

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)

public class EmpDaoTest {

@Autowired

private EmpDao empDao;


@Test

public void testGetEmpOrderByDeptnoAscEmpnoDesc() {

List<Emp> emps = empDao.getEmpOrderByDeptnoAscEmpnoDesc();

for (Emp emp : emps) {

System.out.println(emp);

}

assertThat(emps.size(), is(14));

}

}


SQL 로그

select 

e.empno as empno1_1_, 

e.comm as comm2_1_, 

e.deptno as deptno7_1_, 

e.ename as ename3_1_, 

e.hiredate as hiredate4_1_, 

e.job as job5_1_, 

e.mgr as mgr8_1_, 

e.sal as sal6_1_ 

from emp e left outer join dept d 

on e.deptno=d.deptno 

order by d.deptno asc, e.empno desc


Emp : Dept = N : 1 의 연관관계에서 Emp의 정보를 요청했습니다. 이 경우 EAGER 로딩정책에 따라 Dept의 정보를 구하는 쿼리가 수행되는 것을 로그에서 볼 수 있습니다.


emp.dept.deptno.asc()

위 구문을 처리하기 위해서 조인을 사용하였습니다. 사용된 SQL 쿼리를 살펴보면 "order by d.deptno asc" 처럼 처리하기 위해서 조인을 사용했다고 판단할 수 있습니다.


6. Where

EmpDao.java

다음 메소드를 추가합니다.

public Emp getEmpByEnameAndJob(String ename, String job){

QEmp emp = QEmp.emp;

JPAQuery<?> query = new JPAQuery<Void>(entityManager);

Emp result = query.select(emp).from(emp)

.where(emp.ename.eq(ename), emp.job.eq(job)).fetchFirst();

return result;

}

where 메소드에 전달하는 구문으로 콤마(,) 대신 and 메소드로 대체할 수 있습니다.

Emp result = query.select(emp).from(emp)

.where(emp.ename.eq(ename).and(emp.job.eq(job))).fetchFirst();

필터 조건을 or로 조합하고 싶다면 다음 패턴을 사용합니다.

List<Emp> result = query.select(emp).from(emp)

.where(emp.ename.eq(ename).or(emp.job.eq(job))).fetch();


EmpDaoTest.java

@Test

public void testGetEmpByEnameAndJob() {

Emp emp = empDao.getEmpByEnameAndJob("SMITH", "CLERK");

System.out.println(emp);


assertThat(emp.getEname(), is("SMITH"));
assertThat(emp.getJob(), is("CLERK"));

}


SQL 로그

select 

e.empno as empno1_1_, 

e.comm as comm2_1_, 

e.deptno as deptno7_1_, 

e.ename as ename3_1_, 

e.hiredate as hiredate4_1_, 

e.job as job5_1_, 

e.mgr as mgr8_1_, 

e.sal as sal6_1_ 

from emp e 

where e.ename='SMITH' and e.job='CLERK' 

limit 1

fetchFirst 메소드를 사용하면 ' limit 1' 구문이 사용되는 것을 알 수 있습니다. 추가로 deptno=20에 해당하는 정보를 구합니다.


객체 그래프 탐색을 위한 결과

'SMITH'에 상사 직원정보를 연속적으로 쿼리해서 얻어진 결과 로그입니다.

Emp(

empno=7369, 

ename=SMITH, 

job=CLERK, 

mgr=Emp(

empno=7902, 

ename=FORD, 

job=ANALYST, 

mgr=Emp(

empno=7566, 

ename=JONES, 

job=MANAGER, 

mgr=Emp(

empno=7839, 

ename=KING, 

job=PRESIDENT, 

mgr=null, 

hiredate=1981-11-17, 

sal=5000.0, 

comm=null, 

dept=Dept(deptno=10, dname=ACCOUNTING, loc=NEW YORK)

), 

hiredate=1981-04-02, 

sal=2975.0, 

comm=null, 

dept=Dept(deptno=20, dname=RESEARCH, loc=DALLAS)

), 

hiredate=1981-12-03, 

sal=3000.0, 

comm=null, 

dept=Dept(deptno=20, dname=RESEARCH, loc=DALLAS)

), 

hiredate=1980-12-17, 

sal=800.0, 

comm=null, 

dept=Dept(deptno=20, dname=RESEARCH, loc=DALLAS)

)



#Querydsl, #JPQL, #Querydsk서브쿼리, #QuerydslEXISTS, #JPA, #JPA교육, #JPA동영상, #Querydsl동영상, Querydsl, JPQL, Querydsk서브쿼리, QuerydslEXISTS, JPA, JPA교육, JPA동영상, Querydsl동영상, 

(C#교육동영상)C# ADO.NET 실습 ODP.NET/ODAC 설치 오라클 함수 호출 실습, C#학원, WPF학원, 닷넷학원, 자바학원

  (C#교육동영상)C# ADO.NET 실습  ODP.NET/ODAC 설치  오라클 함수 호출 실습, C#학원, WPF학원, 닷넷학원, 자바학원 https://www.youtube.com/watch?v=qIPU85yAlzc&list=PLxU-i...