레이블이 JPAQueryFactory인 게시물을 표시합니다. 모든 게시물 표시
레이블이 JPAQueryFactory인 게시물을 표시합니다. 모든 게시물 표시

2022년 1월 10일 월요일

Spring Data JPA, Querydsl, JPAQuery, JPAQueryFactory, JPASQLQuery, SQLQuery,SQLQueryFactory, 자바교육, 스프링교육, JAVA교육, Spring교육, 자바학원,스프링교육

 Spring Data JPA, Querydsl, JPAQuery, JPAQueryFactory, JPASQLQuery, SQLQuery,SQLQueryFactory, 자바교육, 스프링교육, JAVA교육, Spring교육, 자바학원,스프링교육



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


Spring Data JPA, Querydsl, JPAQuery, JPAQueryFactory, JPASQLQuery, SQLQuery,SQLQueryFactory

Spring Data JPA, Querydsl, JPAQuery, JPAQueryFactory, JPASQLQuery, SQLQuery,SQLQueryFactory3장. Querydsl 입문Querydsl은 미리 작성한 쿼리타입 클래스를 사용해서 쿼리를 메소드 기반으로 작성할 수 있도록 도와주는 프레

ojc.asia

https://www.youtube.com/watch?v=rwDOqrM2B9Q&list=PLxU-iZCqT52AlV-Y7UlwSQp8N6pMjijFX&index=2 

https://www.youtube.com/watch?v=pue8iaffaXM&list=PLxU-iZCqT52AlV-Y7UlwSQp8N6pMjijFX&index=3 

3장. Querydsl 입문

Querydsl은 미리 작성한 쿼리타입 클래스를 사용해서 쿼리를 메소드 기반으로 작성할 수 있도록 도와주는 프레임워크입니다. 문자열로 작성하거나 XML 파일에 쿼리를 작성하는 대신 Querydsl이 제공하는 메소드를 사용해서 쿼리를 작성합니다.



3.1. 장점

문자열로 쿼리를 작성하는 대신 Querydsl을 사용하여 자바코드로 쿼리를 작성하게 되면 얻을 수 있는 장점은 다음과 같습니다.


  • IDE의 코드 자동완성 기능을 사용할 수 있어서 보다 빠른 개발이 가능하다.
  • 문법적으로 잘못된 쿼리를 거의 허용하지 않아 안전하다.
  • 도메인 타입과 프로퍼티를 편리하게 참조할 수 있다.
  • 도메인 타입의 리팩토링 작업이 수월하다. 


타입에 안전하게 쿼리를 작성해서 얻는 이득이 무엇일지 잠시 생각해 봅니다. 메소드 기반으로 쿼리를 작성하기 때문에 도메인 관련 변경 작업 시 변경이 자동적으로 쿼리에 반영되므로 변화가 있을 때 좀 더 유연하게 적용할 수 있습니다. 또한 쿼리 작성 과정에서 IDE 툴이 제공하는 코드 자동완성 기능을 사용함으로써 쿼리를 더 빠르게 만들 수 있다는 점도 도움이 되는 부분입니다.


Querydsl은 JPQL과 Criteria 기술을 모두 대체할 수 있습니다.
Querydsl은 Criteria 기술에서 제공했던 쿼리를 조건에 따라 동적으로 작성하는 기능과 JPQL 작성을 타입에 안전한 방법으로 작성하는 방법을 모두 제공합니다.



3.2. Querydsl의 종류

Querydsl은 엔티티로 설정된 모델 클래스와 동일한 패키지에 "Q" 모델이라는 쿼리타입 클래스를 미리 생성해 놓고 메타데이터로 사용하여 쿼리를 메소드 기반으로 작성합니다. Querydsl을 사용하면 보다 쉽고 직관적으로 처리할 수 있습니다. 


3.2.1. Querydsl 구분


Thread Safe
as Bean(Singleton)
Use EntityManager
(First Caching)
Use Entity Class
JPAQuery
new
v
v
JPAQueryFactory
v
v
v
JPASQLQuery
new
v (No First Caching)
Table
SQLQuery
new
DataSource
Table
SQLQueryFactory
v
DataSource
Table


  • 쿼리문을 작성하기 위해서 모두 Q 타입 클래스를 사용합니다.
  • JPAQuery, JPAQueryFactory 클래스를 사용하면 EntityManager를 통해서 질의가 처리되고 이 때 사용하는 쿼리문은 JPQL 입니다.
  • SQLQuery, SQLQueryFactory 클래스를 사용하면 JDBC 기술을 사용하여 질의가 처리되고 이 때 사용하는 쿼리문은 SQL입니다.
  • JPASQLQuery 클래스를 사용하면 EntityManager를 통해서 질의가 처리되고 이 때 사용하는 쿼리문은 SQL입니다. 추가적으로 EntityManager를 이용하고 싶을 때 사용합니다.

3.2.2. 엔티티 클래스를 만들어서 사용

"Q" 모델은 클래스 내 자체적으로 이미 인스턴스를 static 변수로 가지고 있기 때문에 예를 들어 "QEmp.emp" 처럼 간단하게 사용할 수 있습니다.


1. JPAQuery

QEmp emp = QEmp.emp

JPAQuery query = new JPAQuery(em);      

query.select(emp).from(emp).where(emp.ename.eq("SMITH"));      

List<Emp> result = query.fetch(); 


JPAQuery 생성자에 EntityManager 객체를 전달합니다.

select 메소드는 얻고자 하는 칼럼들을 지정하는 Projection설정용입니다.

from 메소드는 쿼리대상을 지정합니다. 

where 메소드는 쿼리 조건을 적용하는 필터입니다. 

fetch 메소드로 복수의 로우 데이터를 얻을 수 있습니다.


대부분의 데이터베이스는 저장하는 정보를 필터링할 때 대소문자를 구분하기 때문에 "SMITH" 문자열을 소문자로 사용하면 해당 데이터가 없다고 할 것이니 주의가 필요합니다.


2. JPAQueryFactory

JPAQueryFactory  queryFactory = new JPAQueryFactory(em);

List<Emp> emps = queryFactory.selectFrom(emp).where(emp.ename.eq("SMITH")).fetch();


JPAQueryFactory로 JPAQuery를 만들어서 사용하는 방식입니다. JPAQueryFactory를 빈 컨테이너에 등록해 놓고 DI 받아 사용하면 편리합니다. JPAQueryFactory는 JPQLQueryFactory 인터페이스를 구현했으며 JPAQuery 인스턴스를 포함하여 다양한 방법으로 쿼리 할 수 있습니다. 생성할 때 EntityManager만 인자로 넣어 생성할 수도 있고 JPQLTemplate도 같이 인자로 줘서 생성 할 수 있습니다. 다이나믹하게 데이터베이스가 바뀌는 경우는 거의 없기 때문에 보통 JPQLTemplate은 생략하고 사용합니다.


selectFrom(emp) 메소드는 select(emp).from(emp) 선언의 축약형태입니다. 

eq 메소드는 "=" 연산자를 대체하는 메소드입니다.


3.2.3. 엔티티 클래스를 만들지 않고 사용


1. JPASQLQuery

엔티티 클래스를 만들지 않고 사용할 수 있습니다. 엔티티 클래스를 작성한 후 이를 바탕으로 테이블을 구성하여 진행하는 개발방식 대신 이미 테이블이 존재하는 경우 이를 바탕으로 쿼리타입을 생성하여 질의할 때 이용하는 방식입니다. 


이는 엔티티매니저의 createNativeQuery 메소드를 사용하는 것과 결과가 같습니다. 하지만 둘 사이에는 큰 차이가 존재합니다. 


JPASQLQuery 클래스를 사용하거나 em.createNativeQuery 메소드를 사용하거나 둘 다 개발자가 지정해서 알려 준 쿼리를 사용합니다. em.createNativeQuery에서 파라미터로 넘겨주는 모델클래스는 엔티티이므로 여러 엔티티 클래스의 연관관계에 따른 EAGER 로딩정책을 지키기 위해서 추가적으로 쿼리가 더 수행될 수 있습니다. 

하지만 new JPASQLQuery<Void>(em, templates) 코드에서 보는 것처럼 엔티티매니저를 사용한다고 하더라도 엔티티 클래스를 알려 준 것은 아니므로 연관관계에 따른 추가적인 쿼리가 수행되지 않는다는 차이점이 존재합니다. 더불어 엔티티 클래스를 이용한 것이 아니므로 1차 캐싱기능을 이용하지 못합니다. 질의를 하면 엔티티매니저가 매번 데이터베이스에 질의하여 데이터를 얻어다 줄 것입니다.


JPAQuery 객체로는 인라인뷰 쿼리를 작성하지 못합니다. 따라서 인라인뷰를 사용하고자 하는 경우에는 대신 JPASQLQuery를 사용해야 합니다. 인라인뷰를 사용하지 못하는 것은 Querydsl의 한계가 아니라 JPA의 기술적인 한계입니다.


JPASQLQuery를 사용하기 위해서 우선 테이블을 바탕으로 리버스 엔지니어링을 진행하여 쿼리타입 클래스를 생성해야 합니다. 이 때 필요하다면 모델 클래스를 같이 생성할 수 있습니다. 메이븐 설정에 querydsl-maven-plugin을 추가하여 Q 타입 클래스를 생성한 다음 이용합니다.


SQLTemplates templates = new DerbyTemplates();

QEmp emp = new QEmp("emp");

JPASQLQuery<?> query = new JPASQLQuery<Void>(em, templates);

List<Tuple> rows = query.select(emp.empno, emp.ename).from(emp)

.where(emp.ename.eq("SMITH").fetch();


SQLTemplates의 구현체는 데이터베이스에 따라서 선택해서 사용합니다. 선택된 구현체를 파라미터로 전달하고 사용해야 하는 Dialect를 판단하는 용도로 사용됩니다. 

2. SQLQuery

SQLQuery를 직접 사용하는 방법보다는 SQLQueryFactory를 통해서 사용하는 방법을 권장합니다. 따라서 SQLQueryFactory를 사용하는 방법을 바로 살펴 보겠습니다.


3. SQLQueryFactory

SQLTemplates templates = new MySQLTemplates();

Configuration configuration = new Configuration(templates);

SQLQueryFactory queryFactory = new SQLQueryFactory(configuration, dataSource);

QEmp emp = new QEmp("emp");

List<Emp> result  = queryFactory.selectFrom(emp).where(emp.ename.eq("SMITH")).fetch();           


SQLQueryFactory 생성자에 설정정보인 Configuration 과 데이터베이스 연결정보인 DataSource를 주고 있습니다. 따라서 엔티티매니저를 거쳐서 사용하는 것이 아닌 개발자가 직접 데이터베이스가 이해하는 SQL을 작성한 다음 JDBC 기술을 사용하여 처리하는 방식이라는 것을 알 수 있습니다.


다음은 이해를 돕기위한 com.querydsl.sql.AbstractSQLQuery.fetch 메소드 소스의 일부분 입니다.

//… 생략
final PreparedStatement stmt = getPreparedStatement(queryString);

try {

setParameters(stmt, constants, 

serializer.getConstantPaths(), queryMixin.getMetadata().getParams());

context.addPreparedStatement(stmt);

listeners.prepared(context);


listeners.preExecute(context);

final ResultSet rs = stmt.executeQuery();

listeners.executed(context);

//... 생략

} finally {

stmt.close();

}

//... 생략


SQLQueryFactory를 이용하는 경우 내부적으로 Querydsl은 JDBC기술을 사용한다는 것을 알 수 있습니다.

3.3. JPAQuery, JPAQueryFactory

이제부터 Querydsl의 사용법을 종류별로 자세히 살펴보겠습니다.



3.3.1. 프로젝트 생성

앞서 만든 프로젝트 chapter2-3을 소스 전체를 복사하여 chapter3-3 이름으로 프로젝트를 만듭니다.


3.3.2. 프로젝트 환경설정

로깅 디펜던시와 JPAQuery 사용을 위한 Querydsl 디펜던시를 확인합니다.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>


<groupId>com.example</groupId>

<artifactId>boot</artifactId>

<version>0.0.1-SNAPSHOT</version>

<packaging>jar</packaging>


<parent>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>1.4.1.RELEASE</version>

<relativePath /> <!-- lookup parent from repository -->

</parent>


<properties>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

<java.version>1.8</java.version>

</properties>


<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-jpa</artifactId>

</dependency>

<dependency>

<groupId>org.projectlombok</groupId>

<artifactId>lombok</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>


<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<scope>runtime</scope>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

</dependency>


<dependency>

<groupId>org.bgee.log4jdbc-log4j2</groupId>

<artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>

<version>1.16</version>

</dependency>

<!-- Spring Boot 프로젝트를 만들 경우 버전설정은 생략 가능 -->

<dependency>

<groupId>com.querydsl</groupId>

<artifactId>querydsl-apt</artifactId>

<scope>provided</scope>

</dependency>

<dependency>

<groupId>com.querydsl</groupId>

<artifactId>querydsl-jpa</artifactId>

</dependency>

<!-- Spring Boot 프로젝트를 만들 경우 slf4j-log4j12 디펜던시 선언은 생략 가능 -->

<!-- 

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-log4j12</artifactId>

<version>1.6.1</version>

</dependency>

-->

</dependencies>


<build>

<plugins>

<plugin>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-maven-plugin</artifactId>

</plugin>

<!-- Q 타입 클래스 생성을 위한 플러그인 설정 -->

<plugin>

<groupId>com.mysema.maven</groupId>

<artifactId>apt-maven-plugin</artifactId>

<version>1.1.3</version>

<executions>

<execution>

<goals>

<goal>process</goal>

</goals>

<configuration>

<outputDirectory>

target/generated-sources/java

</outputDirectory>

<processor>

com.querydsl.apt.jpa.JPAAnnotationProcessor

</processor>

</configuration>

</execution>

</executions>

</plugin>

</plugins>

</build>


</project>



3.3.3. Q 타입클래스 생성

프로젝트 선택 > 마우스 오른쪽 클릭 > Run AS > 

Maven generate-sources 또는 Maven install 선택


생성 확인

프로젝트 선택 > 새로고침 > target/generated-sources/java 폴더 및 Q 타입 클래스 생성 확인


소스 폴더로 등록

target/generated-sources/java 폴더 선택 > 마우스 오른쪽 클릭 > Build Path > Use as Source Folder


작업 결과


3.3.4. Persistence Layer

다음 작업으로 클래스를 추가로 작성합니다. 클래스의 위치는 소스의 package 정보를 참고하세요.

EmpDao.java

package com.example.employee.repository;


import java.util.List;

import com.example.employee.model.Emp;


public interface EmpDao {

public List<Emp> getByEname1(String ename);

public List<Emp> getByEname2(String ename);

}

EmpDaoImpl.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;

import com.querydsl.jpa.impl.JPAQueryFactory;


@Repository

public class EmpDaoImpl implements EmpDao {

@PersistenceContext

private EntityManager em;


@Override

public List<Emp> getByEname1(String ename) {

QEmp emp = QEmp.emp; 

JPAQuery<Emp> query = new JPAQuery<Emp>(em);
// select * from emp where ename='SMITH'

query.select(emp).from(emp).where(emp.ename.eq("SMITH"));      


return query.fetch(); 

}


@Override

public List<Emp> getByEname2(String ename) {

QEmp emp = QEmp.emp; 

JPAQueryFactory  queryFactory = new JPAQueryFactory(em);

// select * from emp where ename='SMITH'

return queryFactory.selectFrom(emp).where(emp.ename.eq("SMITH")).fetch();

}

}



3.3.5. JUnit 테스트 클래스 작성

EmpDaoImpl.java 파일 선택 > 마우스 오른쪽 클릭 > 

New > JUnit Test Case 선택



EmpDaoImplTest.java

package com.example.employee.repository;


import static org.hamcrest.CoreMatchers.*;

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 com.example.employee.model.Emp;


@RunWith(SpringRunner.class)

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)

public class EmpDaoImplTest {

@Autowired

private EmpDaoImpl empDao;

private final String TEST_ENAME = "SMITH";


@Test

public void testGetByEname1() {

List<Emp> result = empDao.getByEname1(TEST_ENAME);

for (Emp emp : result) {

System.out.println(emp);

}


assertThat(result.size(), is(1));

assertThat(result.get(0).getEname(), is(TEST_ENAME));

}


@Test

public void testGetByEname2() {

List<Emp> result = empDao.getByEname2(TEST_ENAME);

for (Emp emp : result) {

System.out.println(emp);

}


assertThat(result.size(), is(1));

assertThat(result.get(0).getEname(), is(TEST_ENAME));

}


}



3.3.6. 테스트

EmpDaoImplTest > 마우스 오른쪽 클릭 > Run As > JUnit Test 선택


두 개의 메소드는 JPAQuery를 사용하거나 JPAQueryFactory를 사용하는 정도에 차이만 있을 뿐 대부분의 로직은 비슷하고 결과 또한 같습니다.

개발자가 질의하고자 원했던 쿼리는 다음과 같습니다.

select * from emp where ename='SMITH'


로그 정보를 확인하여 어떻게 처리되었는지 확인해 보세요.

#1 쿼리

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'


앨리어스의 과다사용(?)이 눈에 띄지만 의도한 쿼리를 사용하였다는 것을 알 수 있습니다. emp 테이블과 dept 테이블은 N:1 관계입니다. emp 테이블에 질의를 할 때 emp와의 연관관계를 살펴서 1에 해당하는 테이블이 존재하면 EAGER 로딩정책에 따라 관련 테이블의 데이터를 가져오기 위한 질의를 추가적으로 수행합니다. 따라서, deptno=20에 해당하는 dept 테이블의 정보를 구하기 위해서 #2 쿼리가 수행됩니다.



#2 쿼리

select 

d.deptno as deptno1_0_0_, 

d.dname as dname2_0_0_, 

d.loc as loc3_0_0_ 

from dept d 

where d.deptno=20


연관관계 처리에서 dept 테이블과 관련된 쿼리는 더 이상 수행되지 않습니다. 이는 20번 부서에 속한 직원정보는 구하지 않는다는 것을 의미합니다. 왜냐하면 부서와 직원의 연관관계는 1:N이기 때문에 LAZY 로딩정책에 따라 부서에 속한 직원정보를 구하는 쿼리가 별도로 수행되지 않는 것입니다.


하지만 #1 쿼리 결과로 얻은 데이터 중 mgr 칼럼은 직속상관의 empno를 의미하므로 조회 대상 'SMITH'의 직속상관 데이터를 구하기 위해서 쿼리가 더 수행되어야 합니다. 이는 emp(직원) 테이블과 emp(직속상관) 테이블은 N:1 관계이기 때문입니다. 

따라서 연관관계 N:1 에 기본 로딩정책 EAGER에 따라 직속상관 데이터를 구하는 #3 쿼리가 수행됩니다.

#3 쿼리

select 

e.empno as empno1_1_0_, 

e.comm as comm2_1_0_, 

e.deptno as deptno7_1_0_, 

e.ename as ename3_1_0_, 

e.hiredate as hiredate4_1_0_, 

e.job as job5_1_0_, 

e.mgr as mgr8_1_0_, 

e.sal as sal6_1_0_, 

d.deptno as deptno1_0_1_, 

d.dname as dname2_0_1_, 

d.loc as loc3_0_1_, 

e2.empno as empno1_1_2_, 

e2.comm as comm2_1_2_, 

e2.deptno as deptno7_1_2_, 

e2.ename as ename3_1_2_, 

e2.hiredate as hiredate4_1_2_, 

e2.job as job5_1_2_, 

e2.mgr as mgr8_1_2_, 

e2.sal as sal6_1_2_ 

from emp e left outer join dept d 

on e.deptno=d.deptno left outer join emp e2 

on e.mgr=e2.empno 

where e.empno=7902


#1 쿼리에서 구한 'SMITH'의 상관을 가리키는 직원번호 7902를 사용하여 직속상관의 데이터를 구할 때 이용합니다. 7902에 해당하는 직원은 'FORD' 입니다. 


EntityManager는 스마트해서 최소한의 쿼리를 사용하여 필요한 모든 데이터를 구하기 위해서 3개의 테이블 emp(직원), dept(부서), emp(직속상관) 테이블들을 조인하여 처리하는 것을 볼 수 있습니다. 조인쿼리를 사용하여 7902(FORD)의 정보와 FORD의 직속상관인 7566(JONES)의 정보를 구했습니다. 


다음으로 7566(JONES)의 직속상관인 7839의 데이터를 구하기 위한 #4 쿼리가 수행됩니다. emp(직원) 테이블과 emp(직속상관) 테이블의 연관관계는 N:1 이므로 계속해서 직속상관 데이터를 구하기 위한 쿼리가 연속적으로 수행됩니다.

#4 쿼리

select 

e.empno as empno1_1_0_, 

e.comm as comm2_1_0_, 

e.deptno as deptno7_1_0_, 

e.ename as ename3_1_0_, 

e.hiredate as hiredate4_1_0_, 

e.job as job5_1_0_, 

e.mgr as mgr8_1_0_, 

e.sal as sal6_1_0_, 

d.deptno as deptno1_0_1_, 

d.dname as dname2_0_1_, 

d.loc as loc3_0_1_, 

e2.empno as empno1_1_2_, 

e2.comm as comm2_1_2_, 

e2.deptno as deptno7_1_2_, 

e2.ename as ename3_1_2_, 

e2.hiredate as hiredate4_1_2_, 

e2.job as job5_1_2_, 

e2.mgr as mgr8_1_2_, 

e2.sal as sal6_1_2_ 

 from emp e left outer join dept d 

 on e.deptno=d.deptno left outer join emp e2 

 on e.mgr=e2.empno 

 where e.empno=7839



#3 쿼리에서 구한 7839 직원번호를 사용하여 'JONES'의 상관인 'KING'의 데이터를 구했습니다.


'KING'은 조직의 보스로서 자신이 조직도의 정점입니다. 더 이상 직속상관의 정보를 구하기 위한 쿼리를 수행할 필요가 없습니다. emp(직원), dept(부서) 테이블을 조인쿼리로 질의하면서 이미 'KING'이 속한 10번 부서의 정보를 구했으므로 역시나 dept 테이블을 별도로 조회하는 쿼리는 필요가 없습니다.


EmpDaoImpl. getByEname1 메소드 내 코드

query.select(emp).from(emp).where(emp.ename.eq("SMITH"));


위 메소드로 만들어지는 결과는 JPQL입니다. 이는 EntityManager에 의해서 SQL로 바뀌어 해당 데이터베이스에게 질의가 됩니다. 


정리

#1 쿼리만을 개발자가 사용했지만 테이블의 연관관계에 따라 추가적으로 데이터를 구하기 위해서 자동으로 필요한 질의가 수행되는 모습을 살펴보았습니다. 개발자의 입장에서는 굉장히 편리한 기능입니다. 하지만 사용하지 않는 데이터를 매번 이렇게 연속적으로 쿼리가 수행되게 두는 것은 그리 현명한 판단이 아닐 것입니다.



#SpringDataJPA, #Querydsl, #JPAQuery, #JPAQueryFactory, #JPASQLQuery, #SQLQuery,#SQLQueryFactory​, #자바교육, #스프링교육, #자바학원, #스프링학원, SpringDataJPA, Querydsl, JPAQuery, JPAQueryFactory, JPASQLQuery, SQLQuery,SQLQueryFactory​, 자바교육, 스프링교육, 자바학원, 스프링학원, 


2021년 12월 4일 토요일

(스프링JPA교육, JPA동영상, 스프링교육)Querydsl JPA Query with MySQL, JPAQueryFactory 빈 설정, Oracle, MySQL, Querydsl SQL, 자바학원, 스프링학원교육

 


Querydsl JPA Query with MySQL, JPAQueryFactory 빈 설정, Querydsl JPA Query with Oracle, Querydsl SQL Query with MySQL, Querydsl SQL Query with Oracle

Querydsl JPA Query with MySQL, JPAQueryFactory 빈 설정, Querydsl JPA Query with Oracle, Querydsl SQL Query with MySQL, Querydsl SQL Query with Oracle7. Scaffolding Project 새로운 기술을 습득하고 이를 실무에 반영하기 위해서는 많…

ojc.asia



7. Scaffolding Project

새로운 기술을 습득하고 이를 실무에 반영하기 위해서는 많은 테스트가 필요합니다대표적인 데이터베이스인 Oracle, MySQL 연동되는 프로젝트를 미리 만들어 놓고 테스트가 필요할  간단히 프로젝트를 복사해서 이용하면 매우 편리합니다테스트를 위한 템플릿 프로젝트를 만들어 봅니다.


7.1 Querydsl JPA Query with MySQL


1. 프로젝트 생성

File > New > Spring Starter Project >

프로젝트 : scaffolding-querydsl-jpa-query-with-mysql > Next >

디펜던시 선택: Lombok, JPA, MySQL, Web > Finish



2. 프로젝트 환경 설정

application.properties

# DATASOURCE

spring.datasource.platform=mariadb

spring.datasource.sqlScriptEncoding=UTF-8

spring.datasource.url=jdbc:log4jdbc:mysql://localhost:3306/testdb?createDatabaseIfNotExist=true

spring.datasource.username=root

spring.datasource.password=1111

spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy

spring.datasource.initialize=false

# JPA

spring.jpa.hibernate.ddl-auto=none

spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect

spring.jpa.show-sql=true

spring.data.jpa.repositories.enabled=true

# Logging

logging.config=classpath:logback-spring.xml


3. 프로젝트 로깅 설정

pom.xml

net.sf.log4jdbc.sql.jdbcapi.DriverSpy 디펜던시 추가

<dependency>

<groupId>org.bgee.log4jdbc-log4j2</groupId>

<artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>

<version>1.16</version>

</dependency>


logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>

<configuration>

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">

<!-- By default, encoders are assigned the type

ch.qos.logback.classic.encoder.PatternLayoutEncoder -->

<encoder>

<pattern>

%d{yyyyMMdd HH:mm:ss.SSS} [%thread] %-3level %logger{5} -%msg %n

</pattern>

</encoder>

</appender>


<logger name="jdbc" level="OFF" />

<logger name="jdbc.sqlonly" level="DEBUG" additivity="false">

<appender-ref ref="STDOUT" />

</logger>

<logger name="jdbc.resultsettable" level="DEBUG" additivity="false">

<appender-ref ref="STDOUT" />

</logger>


<include resource="org/springframework/boot/logging/logback/base.xml"/>


<appender name="dailyRollingFileAppender"

class="ch.qos.logback.core.rolling.RollingFileAppender">

<prudent>true</prudent>

<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

<fileNamePattern>applicatoin.%d{yyyy-MM-dd}.log</fileNamePattern>

<maxHistory>30</maxHistory>

</rollingPolicy>

<filter class="ch.qos.logback.classic.filter.ThresholdFilter">

<level>INFO</level>

</filter>

<encoder>

<pattern>

%d{yyyy:MM:dd HH:mm:ss.SSS} %-5level --- [%thread] %logger{35} : %msg %n

</pattern>

</encoder>

</appender>


<logger name="org.springframework.web" level="INFO"/>

<logger name="org.thymeleaf" level="INFO"/>

<logger name="org.hibernate.SQL" level="INFO"/>

<logger name="org.quartz.core" level="INFO"/>

<logger name="org.h2.server.web" level="INFO"/>


<root level="INFO">

<appender-ref ref="STDOUT" />

<appender-ref ref="dailyRollingFileAppender" />

</root>

</configuration>


log4jdbc.log4j2.properties

log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator

# multi-line query display

log4jdbc.dump.sql.maxlinelength=0



4. 엔티티 클래스 생성

Dept.java

package com.example.employee.model;


import java.util.ArrayList;

import java.util.List;

import javax.persistence.Column;

import javax.persistence.Entity;

import javax.persistence.Id;

import javax.persistence.OneToMany;

import com.fasterxml.jackson.annotation.JsonIgnore;

import lombok.Data;

import lombok.ToString;


@Data

@ToString(exclude={"emps"})

@Entity

public class Dept {

@Id

private Long deptno;

@Column(length = 14, nullable = false)

private String dname;

@Column(length = 13)

private String loc;

@OneToMany(mappedBy="dept")

@JsonIgnore

private List<Emp> emps = new ArrayList<Emp>();


public void addEmp(Emp emp){

this.emps.add(emp);

if (emp.getDept() != this) {

emp.setDept(this);

}

}

}


Emp.java

package com.example.employee.model;


import java.util.ArrayList;

import java.util.Date;

import java.util.List;

import javax.persistence.Column;

import javax.persistence.Entity;

import javax.persistence.Id;

import javax.persistence.JoinColumn;

import javax.persistence.ManyToOne;

import javax.persistence.OneToMany;

import javax.persistence.Temporal;

import javax.persistence.TemporalType;

import com.fasterxml.jackson.annotation.JsonIgnore;

import lombok.Data;

import lombok.ToString;


@Data

@ToString(exclude={"staff"})

@Entity

public class Emp {

@Id

private Long empno;

@Column(length = 10, nullable = false)

private String ename;

@Column(length = 9)

private String job;

@ManyToOne

@JoinColumn(name = "mgr")

private Emp mgr;

@OneToMany(mappedBy = "mgr")

@JsonIgnore

private List<Emp> staff = new ArrayList<Emp>();

@Temporal(TemporalType.DATE)

private Date hiredate;

private Double sal;

private Double comm;

@ManyToOne

@JoinColumn(name = "deptno")

private Dept dept;


public void setDept(Dept dept){

this.dept = dept;

if (!dept.getEmps().contains(this)) {

dept.getEmps().add(this);

}

}

}



5. 테스트 데이터 입력 처리

data.sql

INSERT IGNORE INTO DEPT(deptno, dname, loc) VALUES (10,'ACCOUNTING','NEW YORK');

INSERT IGNORE INTO DEPT(deptno, dname, loc) VALUES (20,'RESEARCH','DALLAS');

INSERT IGNORE INTO DEPT(deptno, dname, loc) VALUES (30,'SALES','CHICAGO');

INSERT IGNORE INTO DEPT(deptno, dname, loc) VALUES (40,'OPERATIONS','BOSTON');


INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7839,'KING','PRESIDENT', NULL,STR_TO_DATE('17-11-1981','%d-%m-%Y'),5000,NULL,10);

INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7566,'JONES','MANAGER', 7839,STR_TO_DATE('2-4-1981', '%d-%m-%Y'),2975,NULL,20);

INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7698,'BLAKE','MANAGER', 7839,STR_TO_DATE('1-5-1981', '%d-%m-%Y'),2850,NULL,30);

INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7782,'CLARK','MANAGER', 7839,STR_TO_DATE('9-6-1981', '%d-%m-%Y'),2450,NULL,10);


INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7902,'FORD','ANALYST', 7566,STR_TO_DATE('3-12-1981', '%d-%m-%Y'),3000,NULL,20);

INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7788,'SCOTT','ANALYST', 7566,STR_TO_DATE('13-07-1987', '%d-%m-%Y'),3000,NULL,20);

INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7499,'ALLEN','SALESMAN', 7698,STR_TO_DATE('20-2-1981', '%d-%m-%Y'),1600,300,30);

INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7521,'WARD','SALESMAN', 7698,STR_TO_DATE('22-2-1981', '%d-%m-%Y'),1250,500,30);

INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7654,'MARTIN','SALESMAN',7698,STR_TO_DATE('28-9-1981', '%d-%m-%Y'),1250,1400,30);

INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7844,'TURNER','SALESMAN',7698,STR_TO_DATE('8-9-1981', '%d-%m-%Y'),1500,0,30);

INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7900,'JAMES','CLERK', 7698,STR_TO_DATE('3-12-1981', '%d-%m-%Y'),950,NULL,30);

INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7934,'MILLER','CLERK', 7782,STR_TO_DATE('23-1-1982', '%d-%m-%Y'),1300,NULL,10);


INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7369,'SMITH','CLERK', 7902,STR_TO_DATE('17-12-1980','%d-%m-%Y'),800,NULL,20);

INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7876,'ADAMS','CLERK', 7788,STR_TO_DATE('13-07-1987', '%d-%m-%Y'),1100,NULL,20);



6. Q 타입 클래스 생성을 위한 디펜던시 설정

타입 클래스 생성을 위한 디펜던시 "querydsl-apt" 추가하고 "apt-maven-plugin" 플러그인을 설정합니다. Spring Boot 프로젝트를 만들 경우 slf4j-log4j12 디펜던시 선언은 생략이 가능합니다.


pom.xml

<dependencies>

<dependency>

<groupId>com.querydsl</groupId>

<artifactId>querydsl-apt</artifactId>

<scope>provided</scope>

</dependency>

<dependency>

<groupId>com.querydsl</groupId>

<artifactId>querydsl-jpa</artifactId>

</dependency>

</dependencies>


<build>

<plugins>

<plugin>

<groupId>com.mysema.maven</groupId>

<artifactId>apt-maven-plugin</artifactId>

<version>1.1.3</version>

<executions>

<execution>

<goals>

<goal>process</goal>

</goals>

<configuration>

<outputDirectory>

target/generated-sources/java

</outputDirectory>

<processor>

com.querydsl.apt.jpa.JPAAnnotationProcessor

</processor>

</configuration>

</execution>

</executions>

</plugin>

</plugins>

</build>



7. Q 타입 클래스 생성

만약 작업  프로젝트에 빨간 x 표시가 보이면 다음 작업을 수행합니다.

프로젝트 선택 > 마우스 오른쪽 클릭 > Maven > Update Project… > 대상 프로젝트 확인 > OK


타입 클래스를 생성하기 위해서 다음 작업을 수행합니다.

프로젝트 선택 > 마우스 오른쪽 클릭 > Run AS > Maven generate-sources


소스 폴더로 등록

target/generated-sources/java 폴더 선택 > 마우스 오른쪽 클릭 >

Build Path > Use as Source Folder



작업결과 프로젝트 구조

프로젝트 선택 > 새로고침 > target/generated-sources/java 폴더 확인

사진 삭제

사진 설명을 입력하세요.


타입 클래스 생성작업을 진행할  데이터베이스와 연결하는 동작은 필요하지 않습니다프로젝트 내의 엔티티 클래스를 찾아서 Q 타입 클래스를 생성합니다.

따라서 application.properties 파일의 데이터베이스 연결 설정은 사용되지 않습니다다만 나중에 데이터베이스 처리 로직을 학습할  필요하므로 미리 설정했다고 보면 됩니다.


8. JPAQueryFactory 빈 설정

JPAQueryFactory  빈으로 등록해 놓고 필요할  DI받아서 사용하면 편리합니다.

QuerydslJpaQueryConfig.java

package com.example.common.config;


import javax.persistence.EntityManager;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import com.querydsl.jpa.impl.JPAQueryFactory;


@Configuration

public class QuerydslJpaQueryConfig {

@Bean

public JPAQueryFactory queryFactory(EntityManager em) {

return new JPAQueryFactory(em);

}

}


7.2 Querydsl JPA Query with Oracle

1. 프로젝트 생성

File > New > Spring Starter Project >

프로젝트 : scaffolding-querydsl-jpa-query-with-oracle > Next >

디펜던시 선택: Lombok, JPA, Web > Finish


2. 오라클 데이터베이스 연결 드라이버 디펜던시 설정 추가

pom.xml

<dependencies>

<dependency>

<groupId>com.oracle</groupId>

<artifactId>ojdbc6</artifactId>

<version>11.2.0.3</version>

</dependency>

</dependencies>


<repositories>

<repository>

<id>oracle</id>

<name>ORACLE JDBC Repository</name>

<url>https://maven.oracle.com;

</repository>

</repositories>


3. 프로젝트 환경 설정

application.properties

# DATASOURCE

spring.datasource.platform=oracle

spring.datasource.sqlScriptEncoding=UTF-8

spring.datasource.url=jdbc:log4jdbc:oracle:thin:@192.168.0.225:1521:orcl

spring.datasource.username=scott

spring.datasource.password=1234

spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy

spring.datasource.initialize=false


# JPA

spring.jpa.hibernate.ddl-auto=none

spring.jpa.database-platform=org.hibernate.dialect.Oracle10gDialect

spring.jpa.show-sql=true

spring.data.jpa.repositories.enabled=true


# Logging

logging.config=classpath:logback-spring.xml


4. 프로젝트 로깅 설정

pom.xml

net.sf.log4jdbc.sql.jdbcapi.DriverSpy 디펜던시 추가

<dependency>

<groupId>org.bgee.log4jdbc-log4j2</groupId>

<artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>

<version>1.16</version>

</dependency>


logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>

<configuration>

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">

<!-- By default, encoders are assigned the type

ch.qos.logback.classic.encoder.PatternLayoutEncoder -->

<encoder>

<pattern>

%d{yyyyMMdd HH:mm:ss.SSS} [%thread] %-3level %logger{5} -%msg %n

</pattern>

</encoder>

</appender>


<logger name="jdbc" level="OFF" />

<logger name="jdbc.sqlonly" level="DEBUG" additivity="false">

<appender-ref ref="STDOUT" />

</logger>

<logger name="jdbc.resultsettable" level="DEBUG" additivity="false">

<appender-ref ref="STDOUT" />

</logger>


<include resource="org/springframework/boot/logging/logback/base.xml"/>


<appender name="dailyRollingFileAppender"

class="ch.qos.logback.core.rolling.RollingFileAppender">

<prudent>true</prudent>

<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

<fileNamePattern>applicatoin.%d{yyyy-MM-dd}.log</fileNamePattern>

<maxHistory>30</maxHistory>

</rollingPolicy>

<filter class="ch.qos.logback.classic.filter.ThresholdFilter">

<level>INFO</level>

</filter>

<encoder>

<pattern>

%d{yyyy:MM:dd HH:mm:ss.SSS} %-5level --- [%thread] %logger{35} : %msg %n

</pattern>

</encoder>

</appender>


<logger name="org.springframework.web" level="INFO"/>

<logger name="org.thymeleaf" level="INFO"/>

<logger name="org.hibernate.SQL" level="INFO"/>

<logger name="org.quartz.core" level="INFO"/>

<logger name="org.h2.server.web" level="INFO"/>


<root level="INFO">

<appender-ref ref="STDOUT" />

<appender-ref ref="dailyRollingFileAppender" />

</root>

</configuration>


log4jdbc.log4j2.properties

log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator

# multi-line query display

log4jdbc.dump.sql.maxlinelength=0


로깅 설정은 부록 7.1 동일합니다다만 나중 편의를 위하여 조금 반복작업을 수행한다고   있습니다.



5. 엔티티 클래스 생성

Dept.java

package com.example.employee.model;


import java.util.ArrayList;

import java.util.List;


import javax.persistence.Column;

import javax.persistence.Entity;

import javax.persistence.Id;

import javax.persistence.OneToMany;


import com.fasterxml.jackson.annotation.JsonIgnore;


import lombok.Data;

import lombok.ToString;


@Data

@ToString(exclude={"emps"})

@Entity

public class Dept {

@Id

private Long deptno;

@Column(length = 14, nullable = false)

private String dname;

@Column(length = 13)

private String loc;

@OneToMany(mappedBy="dept")

@JsonIgnore

private List<Emp> emps = new ArrayList<Emp>();


public void addEmp(Emp emp){

this.emps.add(emp);

if (emp.getDept() != this) {

emp.setDept(this);

}

}

}


Emp.java

package com.example.employee.model;


import java.util.ArrayList;

import java.util.Date;

import java.util.List;


import javax.persistence.Column;

import javax.persistence.Entity;

import javax.persistence.Id;

import javax.persistence.JoinColumn;

import javax.persistence.ManyToOne;

import javax.persistence.OneToMany;

import javax.persistence.Temporal;

import javax.persistence.TemporalType;


import com.fasterxml.jackson.annotation.JsonIgnore;


import lombok.Data;

import lombok.ToString;


@Data

@ToString(exclude={"staff"})

@Entity

public class Emp {

@Id

private Long empno;

@Column(length = 10, nullable = false)

private String ename;

@Column(length = 9)

private String job;

@ManyToOne

@JoinColumn(name = "mgr")

private Emp mgr;

@OneToMany(mappedBy = "mgr")

@JsonIgnore

private List<Emp> staff = new ArrayList<Emp>();

@Temporal(TemporalType.DATE)

private Date hiredate;

private Double sal;

private Double comm;

@ManyToOne

@JoinColumn(name = "deptno")

private Dept dept;


public void setDept(Dept dept){

this.dept = dept;

if (!dept.getEmps().contains(this)) {

dept.getEmps().add(this);

}

}

}


엔티티 클래스의 내용은 부록 7.1에서 사용한 엔티티 클래스와 같습니다.



6. Q 타입 클래스 생성을 위한 디펜던시 설정

pom.xml

<dependencies>

<dependency>

<groupId>com.querydsl</groupId>

<artifactId>querydsl-apt</artifactId>

<scope>provided</scope>

</dependency>

<dependency>

<groupId>com.querydsl</groupId>

<artifactId>querydsl-jpa</artifactId>

</dependency>

</dependencies>


<build>

<plugins>

<plugin>

<groupId>com.mysema.maven</groupId>

<artifactId>apt-maven-plugin</artifactId>

<version>1.1.3</version>

<executions>

<execution>

<goals>

<goal>process</goal>

</goals>

<configuration>

<outputDirectory>

target/generated-sources/java

</outputDirectory>

<processor>

com.querydsl.apt.jpa.JPAAnnotationProcessor

</processor>

</configuration>

</execution>

</executions>

</plugin>

</plugins>

</build>



7. Q 타입 클래스 생성

만약 작업  프로젝트에 빨간 x 표시가 보이면 다음 작업을 수행합니다.

프로젝트 선택 > 마우스 오른쪽 클릭 > Maven > Update Project… > 대상 프로젝트 확인 > OK


타입 클래스를 생성하기 위해서 다음 작업을 수행합니다.

프로젝트 선택 > 마우스 오른쪽 클릭 > Run AS > Maven generate-sources


작업결과 프로젝트 구조

프로젝트 선택 > 새로고침 > target/generated-sources/java 폴더 확인

사진 삭제

사진 설명을 입력하세요.



7.1 7.2 많은 부분이 비슷합니다사실상 다른 부분은 application.properties 뿐인데 이는 Q 타입 클래스 생성  사용되지 않으므로 거의 동일하다고   있습니다.

연동 데이터베이스가 오라클인 경우 프로젝트 생성  오라클 드라이버 디펜던시를 설정할  없으므로 별도 수작업으로 pom.xml 디펜던시를 추가한 부분이 다른 부분입니다.



8. JPAQueryFactory 빈 설정

JPAQueryFactory  빈으로 등록해 놓고 필요할  DI받아서 사용하면 편리합니다.


QuerydslJpaQueryConfig.java

package com.example.common.config;


import javax.persistence.EntityManager;


import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;


import com.querydsl.jpa.impl.JPAQueryFactory;


@Configuration

public class QuerydslJpaQueryConfig {

@Bean

public JPAQueryFactory queryFactory(EntityManager em) {

return new JPAQueryFactory(em);

}

}



7.3 Querydsl SQL Query with MySQL


1. 프로젝트 생성

File > New > Spring Starter Project >

프로젝트 : scaffolding-querydsl-sql-query-with-mysql > Next >

디펜던시 선택: Lombok, JPA, MySQL, Web > Finish


2. 프로젝트 환경 설정

application.properties

# DATASOURCE

spring.datasource.platform=mariadb

spring.datasource.sqlScriptEncoding=UTF-8

spring.datasource.url=jdbc:log4jdbc:mysql://localhost:3306/test2db?createDatabaseIfNotExist=true

spring.datasource.username=root

spring.datasource.password=1111

spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy

spring.datasource.initialize=false


# JPA

spring.jpa.hibernate.ddl-auto=none

spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect

spring.jpa.show-sql=true

spring.data.jpa.repositories.enabled=true


# Logging

logging.config=classpath:logback-spring.xml


3. 프로젝트 로깅 설정

부록 7.1.3  동일합니다.


4. 엔티티 클래스 생성 대신 테이블 생성

Querydsl SQL Query 프로젝트  엔티티 클래스를 만들지 않고 사용합니다이미 데이터베이스  테이블이 존재할  이를 바탕으로 리버스엔지니어링 작업을 수행해서 Q 타입 클래스와 모델 클래스를 생성한  사용합니다.


따라서 작업 대상 "test2db" 데이터베이스를 생성하고 필요한 테이블을 생성하는 쿼리와 테스트 데이터를 입력하는 쿼리를 실행합니다.

schema.sql

CREATE TABLE IF NOT EXISTS `dept` (

`deptno` INT(11) NOT NULL,

`dname` VARCHAR(14) NOT NULL,

`loc` VARCHAR(13) NULL DEFAULT NULL,

PRIMARY KEY (`deptno`)

)

COLLATE='utf8_general_ci'

ENGINE=InnoDB;


CREATE TABLE IF NOT EXISTS `emp` (

`empno` INT(11) NOT NULL,

`comm` DOUBLE NULL DEFAULT NULL,

`ename` VARCHAR(10) NOT NULL,

`hiredate` DATE NULL DEFAULT NULL,

`job` VARCHAR(9) NULL DEFAULT NULL,

`sal` DOUBLE NULL DEFAULT NULL,

`deptno` INT(11) NULL DEFAULT NULL,

`mgr` INT(11) NULL DEFAULT NULL,

PRIMARY KEY (`empno`),

INDEX `FKfqt0j25nlvjwt7qt1t3x7v6qf` (`deptno`),

INDEX `FKfehivfm7m674r8qrrnug1of2q` (`mgr`),

CONSTRAINT `FKfehivfm7m674r8qrrnug1of2q` FOREIGN KEY (`mgr`)

REFERENCES `emp` (`empno`),

CONSTRAINT `FKfqt0j25nlvjwt7qt1t3x7v6qf` FOREIGN KEY (`deptno`)

REFERENCES `dept` (`deptno`)

)

COLLATE='utf8_general_ci'

ENGINE=InnoDB;


data.sql

INSERT IGNORE INTO DEPT(deptno, dname, loc) VALUES (10,'ACCOUNTING','NEW YORK');

INSERT IGNORE INTO DEPT(deptno, dname, loc) VALUES (20,'RESEARCH','DALLAS');

INSERT IGNORE INTO DEPT(deptno, dname, loc) VALUES (30,'SALES','CHICAGO');

INSERT IGNORE INTO DEPT(deptno, dname, loc) VALUES (40,'OPERATIONS','BOSTON');


INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7839,'KING','PRESIDENT', NULL,STR_TO_DATE('17-11-1981','%d-%m-%Y'),5000,NULL,10);

INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7566,'JONES','MANAGER', 7839,STR_TO_DATE('2-4-1981', '%d-%m-%Y'),2975,NULL,20);

INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7698,'BLAKE','MANAGER', 7839,STR_TO_DATE('1-5-1981', '%d-%m-%Y'),2850,NULL,30);

INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7782,'CLARK','MANAGER', 7839,STR_TO_DATE('9-6-1981', '%d-%m-%Y'),2450,NULL,10);


INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7902,'FORD','ANALYST', 7566,STR_TO_DATE('3-12-1981', '%d-%m-%Y'),3000,NULL,20);

INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7788,'SCOTT','ANALYST', 7566,STR_TO_DATE('13-07-1987', '%d-%m-%Y'),3000,NULL,20);

INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7499,'ALLEN','SALESMAN', 7698,STR_TO_DATE('20-2-1981', '%d-%m-%Y'),1600,300,30);

INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7521,'WARD','SALESMAN', 7698,STR_TO_DATE('22-2-1981', '%d-%m-%Y'),1250,500,30);

INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7654,'MARTIN','SALESMAN',7698,STR_TO_DATE('28-9-1981', '%d-%m-%Y'),1250,1400,30);

INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7844,'TURNER','SALESMAN',7698,STR_TO_DATE('8-9-1981', '%d-%m-%Y'),1500,0,30);

INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7900,'JAMES','CLERK', 7698,STR_TO_DATE('3-12-1981', '%d-%m-%Y'),950,NULL,30);

INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7934,'MILLER','CLERK', 7782,STR_TO_DATE('23-1-1982', '%d-%m-%Y'),1300,NULL,10);


INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7369,'SMITH','CLERK', 7902,STR_TO_DATE('17-12-1980','%d-%m-%Y'),800,NULL,20);

INSERT IGNORE INTO EMP(empno, ename, job, mgr, hiredate, sal, comm, deptno)

VALUES(7876,'ADAMS','CLERK', 7788,STR_TO_DATE('13-07-1987', '%d-%m-%Y'),1100,NULL,20);


5. Q 타입 클래스 생성을 위한 디펜던시 설정

pom.xml

<properties>

<querydsl.version>4.1.2</querydsl.version>

</properties>


<dependencies>

<dependency>

<groupId>com.querydsl</groupId>

<artifactId>querydsl-sql</artifactId>

<version>${querydsl.version}</version>

</dependency>


<dependency>

<groupId>com.querydsl</groupId>

<artifactId>querydsl-sql-spring</artifactId>

<version>${querydsl.version}</version>

</dependency>


<dependency>

<groupId>com.querydsl</groupId>

<artifactId>querydsl-sql-codegen</artifactId>

<version>${querydsl.version}</version>

<scope>provided</scope>

</dependency>

</dependencies>


<build>

<plugins>

<plugin>

<groupId>com.querydsl</groupId>

<artifactId>querydsl-maven-plugin</artifactId>

<version>${querydsl.version}</version>

<executions>

<execution>

<goals>

<goal>export</goal>

</goals>

</execution>

</executions>

<configuration>

<jdbcDriver>com.mysql.jdbc.Driver</jdbcDriver>

<jdbcUrl>jdbc:mysql://localhost:3306/test2db</jdbcUrl>

<jdbcUser>root</jdbcUser>

<jdbcPassword>1111</jdbcPassword>

<packageName>com.example.employee.model</packageName>

<targetFolder>target/generated-sources/java</targetFolder>

<namePrefix>Q</namePrefix>

<exportBeans>true</exportBeans>

<tableNamePattern>DEPT,EMP</tableNamePattern>

</configuration>

<dependencies>

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>5.1.38</version>

<scope>compile</scope>

</dependency>

</dependencies>

</plugin>


</plugins>

</build>



6. Q 타입 클래스 생성

만약 작업  프로젝트에 빨간 x 표시가 보이면 다음 작업을 수행합니다.

프로젝트 선택 > 마우스 오른쪽 클릭 > Maven > Update Project… > 대상 프로젝트 확인 > OK


타입 클래스를 생성하기 위해서 다음 작업을 수행합니다.

프로젝트 선택 > 마우스 오른쪽 클릭 > Run AS > Maven generate-sources


작업 로그

사진 삭제

사진 설명을 입력하세요.

=


작업결과 프로젝트 구조

프로젝트 선택 > 새로고침 > target/generated-sources/java 폴더 확인

사진 삭제

사진 설명을 입력하세요.




7. SQLQueryFactory 빈 설정

SQLQueryFactory 빈으로 등록해 놓고 필요할  DI받아서 사용하면 편리합니다.


QuerydslSqlQueryConfig.java

package com.example.common.config;


import java.sql.Connection;


import javax.inject.Provider;

import javax.sql.DataSource;


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

import org.springframework.context.annotation.Bean;


import com.querydsl.sql.Configuration;

import com.querydsl.sql.MySQLTemplates;

import com.querydsl.sql.SQLQueryFactory;

import com.querydsl.sql.SQLTemplates;

import com.querydsl.sql.spring.SpringConnectionProvider;

import com.querydsl.sql.spring.SpringExceptionTranslator;


@org.springframework.context.annotation.Configuration

public class QuerydslSqlQueryConfig {


@Autowired

private DataSource dataSource;


@Bean

public Configuration configuration() {

SQLTemplates templates = MySQLTemplates.builder().build();

Configuration configuration = new Configuration(templates);

configuration.setExceptionTranslator(new SpringExceptionTranslator());

return configuration;

}


@Bean

public SQLQueryFactory queryFactory() {

Provider<Connection> provider = new SpringConnectionProvider(dataSource);

return new SQLQueryFactory(configuration(), provider);

}

}



7.4 Querydsl SQL Query with Oracle

1. 프로젝트 생성

File > New > Spring Starter Project >

프로젝트 : scaffolding-querydsl-sql-query-with-oracle > Next >

디펜던시 선택: Lombok, JPA, Web > Finish


2. 오라클 데이터베이스 연결 드라이버 디펜던시 설정 추가

pom.xml

<dependencies>

<dependency>

<groupId>com.oracle</groupId>

<artifactId>ojdbc6</artifactId>

<version>11.2.0.3</version>

</dependency>

</dependencies>


<repositories>

<repository>

<id>oracle</id>

<name>ORACLE JDBC Repository</name>

<url>https://maven.oracle.com;

</repository>

</repositories>


3. 프로젝트 환경 설정

application.properties

# DATASOURCE

spring.datasource.platform=oracle

spring.datasource.sqlScriptEncoding=UTF-8

spring.datasource.url=jdbc:log4jdbc:oracle:thin:@192.168.0.225:1521:orcl

spring.datasource.username=scott

spring.datasource.password=1234

spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy

spring.datasource.initialize=false


# JPA

spring.jpa.hibernate.ddl-auto=none

spring.jpa.database-platform=org.hibernate.dialect.Oracle10gDialect

spring.jpa.show-sql=true

spring.data.jpa.repositories.enabled=true


# Logging

logging.config=classpath:logback-spring.xml


4. 프로젝트 로깅 설정

부록 7.2.4  동일합니다.


5. 엔티티 클래스 생성 대신 테이블 생성

따로 테이블을 생성하지 않고 SCOTT 계정으로 이미 존재하는 테이블을 대상으로 학습  보겠습니다.


6. Q 타입 클래스 생성을 위한 디펜던시 설정


pom.xml

<properties>

<querydsl.version>4.1.2</querydsl.version>

</properties>


<dependencies>

<dependency>

<groupId>com.querydsl</groupId>

<artifactId>querydsl-sql</artifactId>

<version>${querydsl.version}</version>

</dependency>


<dependency>

<groupId>com.querydsl</groupId>

<artifactId>querydsl-sql-spring</artifactId>

<version>${querydsl.version}</version>

</dependency>


<dependency>

<groupId>com.querydsl</groupId>

<artifactId>querydsl-sql-codegen</artifactId>

<version>${querydsl.version}</version>

<scope>provided</scope>

</dependency>

</dependencies>


<build>

<plugins>

<plugin>

<groupId>com.querydsl</groupId>

<artifactId>querydsl-maven-plugin</artifactId>

<version>${querydsl.version}</version>

<executions>

<execution>

<goals>

<goal>export</goal>

</goals>

</execution>

</executions>

<configuration>

<jdbcDriver>oracle.jdbc.driver.OracleDriver</jdbcDriver>

<jdbcUrl>jdbc:oracle:thin:@192.168.0.225:1521:orcl</jdbcUrl>

<jdbcUser>scott</jdbcUser>

<jdbcPassword>1234</jdbcPassword>

<packageName>com.example.employee.model</packageName>

<exportView>false</exportView>

<schemaPattern>SCOTT</schemaPattern>

<tableNamePattern>DEPT,EMP</tableNamePattern>

<targetFolder>target/generated-sources/java</targetFolder>

<namePrefix>Q</namePrefix>

<exportBeans>true</exportBeans>

</configuration>

<dependencies>

<dependency>

<groupId>com.oracle</groupId>

<artifactId>ojdbc6</artifactId>

<version>11.1.0.7.0</version>

</dependency>

</dependencies>

</plugin>

</plugins>

</build>


tableNamePattern 대문자로 설정해야 합니다.



7. Q 타입 클래스 생성

만약 작업  프로젝트에 빨간 x 표시가 보이면 다음 작업을 수행합니다.

프로젝트 선택 > 마우스 오른쪽 클릭 > Maven > Update Project… > 대상 프로젝트 확인 > OK


타입 클래스를 생성하기 위해서 다음 작업을 수행합니다.

프로젝트 선택 > 마우스 오른쪽 클릭 > Run AS > Maven generate-sources


작업결과 프로젝트 구조

프로젝트 선택 > 새로고침 > target/generated-sources/java 폴더 확인

사진 삭제

사진 설명을 입력하세요.




8. SQLQueryFactory 빈 설정

SQLQueryFactory 빈으로 등록해 놓고 필요할  DI받아서 사용하면 편리합니다.


QuerydslSqlQueryConfig.java

package com.example.common.config;


import java.sql.Connection;


import javax.inject.Provider;

import javax.sql.DataSource;


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

import org.springframework.context.annotation.Bean;


import com.querydsl.sql.Configuration;

import com.querydsl.sql.MySQLTemplates;

import com.querydsl.sql.SQLQueryFactory;

import com.querydsl.sql.SQLTemplates;

import com.querydsl.sql.spring.SpringConnectionProvider;

import com.querydsl.sql.spring.SpringExceptionTranslator;


@org.springframework.context.annotation.Configuration

public class QuerydslSqlQueryConfig {


@Autowired

private DataSource dataSource;


@Bean

public Configuration configuration() {

SQLTemplates templates = OracleTemplates.builder().build();

Configuration configuration = new Configuration(templates);

configuration.setExceptionTranslator(new SpringExceptionTranslator());

return configuration;

}


@Bean

public SQLQueryFactory queryFactory() {

Provider<Connection> provider = new SpringConnectionProvider(dataSource);

return new SQLQueryFactory(configuration(), provider);

}

}




#Querydsl, #JPA, #MySQL, #JPAQueryFactory, #JPA교육, #스프링교육, #JPA오라클, #오라클JPA, #자바교육, #JPA교육,Querydsl, JPA, MySQL, JPAQueryFactory, JPA교육, 스프링교육, JPA오라클, 오라클JPA, 자바교육, JPA교육



(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...