프로젝트 생성
Querydsl 설정과 검증
Querydsl 사이트 : http://querydsl.com/
MVN Repository : https://mvnrepository.com/artifact/com.querydsl
📌 build.gradle
// querydsl 추가
buildscript {
ext {
queryDslVersion = "5.0.0"
}
}
plugins {
id 'org.springframework.boot' version '2.6.0'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
//querydsl 추가
id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
id 'java'
}
group = 'study'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
//querydsl 추가
implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"
implementation "com.querydsl:querydsl-apt:${queryDslVersion}"
// implementation 'com.querydsl:querydsl-jpa'
// implementation 'com.querydsl:querydsl-apt'
//테스트에서 lombok 사용
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
}
//querydsl 추가 시작
def querydslDir = "$buildDir/generated/querydsl"
querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
sourceSets {
main.java.srcDir querydslDir
}
configurations {
querydsl.extendsFrom compileClasspath
}
compileQuerydsl {
options.annotationProcessorPath = configurations.querydsl
}
//querydsl 추가 끝
querydsl 설정을 추가한다.
해당 위치에 Hello 라는 엔티티를 하나 생성해보자
📌 Hello 엔티티
package study.querydsl.entity;
@Entity
@Getter @Setter
public class Hello {
@Id @GeneratedValue
private Long id;
}
참고로 Hello 엔티티를 아예 안만들고 그냥 Tasks → other → compileQuerydsl을 실행시켜도 된다.
그리고나서 Gradle에서 Tasks → other → compileQuerydsl 을 더블 클릭한다.
성공 표시가 뜰 것이다.
build → generated → study → querydsl → entity → QHello 가 생겨있는 것을 볼 수 있다.
querydsl 폴더가 생긴 것이 중요하다.
엔티티를 정의한 후 빌드를 하면 알아서 Q엔티티를 생성해준다.
참고로 generated 하위 폴더에 있는 Q 파일들은 깃에 올라가면 안된다. 왜냐하면 Q 파일들은 프로젝트를 계속 만들어내는 동안에 바뀔 수 있기 때문이다. 그러므로 깃 이그노어를 해줘야 한다.
다행히 build 폴더 내부 파일들은 자동으로 깃 이그노어가 되어있다.
Querydsl이 잘 작동하는지 검증하도록 하겠다.
📌 QuerydslApplicationTests
package study.querydsl;
@SpringBootTest
@Transactional
class QuerydslApplicationTests {
@Autowired
EntityManager em;
@Test
void contextLoads() {
Hello hello = new Hello();
em.persist(hello);
// 최신 버전에서는 JPA쿼리 팩토리 사용을 권장한다.
JPAQueryFactory query = new JPAQueryFactory(em);
/* Q파일 생성하는 방법 1 */
// 생성자의 파라미터로 별칭(alias)을 넣는다.
// QHello qHello = new QHello("h");
/* Q파일 생성하는 방법 2 */
QHello qHello = QHello.hello;
// 엔티티를 사용하지 않고 쿼리와 관련된 것들은 다 Q타입을 넣어야 한다.
Hello result = query
.selectFrom(qHello)
.fetchOne();
assertThat(result).isEqualTo(hello);
assertThat(result.getId()).isEqualTo(hello.getId());
}
}
쿼리와 관련된 것들은 다 Q타입으로 생성해야 한다.
Q class 객체를 생성하는 방법은 2가지가 있다.
- 1. new 연산자로 생성 (파라미터로 별칭을 줘야한다.)
- 2. Q 파일의 static 메서드로 생성하기
Q파일 내부 로직에 이미 생성자를 만들 수 있는 메서드가 존재한다. 그것을 이용하면 된다.
라이브러리 살펴보기
- 핵심 라이브러리
- 스프링 MVC
- JPA, 하이버네이트
- 스프링 데이터 JPA
- Querydsl
1. querydsl-apt
Q파일 생성 라이브러리
2. Querydsl-JPA
쿼리와 연관된 라이브러리
3. spring-boot-starter-web
기본적인 웹 프로젝트
톰켓을 내장하고 있고, webmvc를 가지고 있다.
4. spring-boot-starter-data-jpa
최신 버전의 하이버네이트를 가지고 있다.
JDBC에서 HikariCP 라이브러리가 있는데 이것은 데이터베이스 커넥션 풀링 라이브러리다.
로깅을 찍는데 sl4f 인터페이스의 구현체로 logback을 사용한다.
스프링 부트 설정 - JPA, DB
📌 application.yml
spring:
datasource:
url: jdbc:h2:tcp://localhost/~/querydsl
username: sa
password:
driver-class-name: org.h2.Driver
jpa:
hibernate:
ddl-auto: create
properties:
hibernate:
format_sql: true
logging.level:
org.hibernate.SQL: debug
예제 도메인 모델과 동작 확인
1. 도메인 모델
📌 Member 엔티티
package study.querydsl.entity;
@Entity
@Getter
@Setter
@NoArgsConstructor(access = PROTECTED)
@ToString(of = {"id", "username", "age"})
public class Member {
@Id @GeneratedValue
@Column(name = "member_id")
private Long id;
private String username;
private int age;
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "team_id")
private Team team;
public void changeTeam(Team team) {
this.team = team;
team.getMembers().add(this);
}
public Member(String username, int age, Team team) {
this.username = username;
this.age = age;
if (team!=null) {
changeTeam(team);
}
}
public Member(String username, int age) {
this.username = username;
this.age = age;
}
public Member(String username) {
this.username = username;
}
}
실무에서는 @Setter를 사용하지 않을 것을 권장한다. 지금은 연습이니 사용했다.
@ToString은 가급적 내부 필드만(연관관계 없는 필드만) 적용한다.
📌 Team 엔티티
package study.querydsl.entity;
@Entity
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString(of = {"id", "name"})
public class Team {
@Id @GeneratedValue
@Column(name = "team_id")
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
public Team(String name) {
this.name = name;
}
}
2. 동작확인
📌 MemberTest
package study.querydsl.entity;
@SpringBootTest
@Transactional
class MemberTest {
@Autowired
EntityManager em;
@Test
void testEntity() {
Team teamA = new Team("teamA");
Team teamB = new Team("teamB");
em.persist(teamA);
em.persist(teamB);
Member member1 = new Member("member1", 10, teamA);
Member member2 = new Member("member2", 20, teamA);
Member member3 = new Member("member3", 30, teamB);
Member member4 = new Member("member4", 40, teamB);
em.persist(member1);
em.persist(member2);
em.persist(member3);
em.persist(member4);
// 초기화
em.flush();
em.clear();
List<Member> members = em.createQuery("select m from Member m", Member.class)
.getResultList();
for (Member member : members) {
System.out.println("member = " + member);
System.out.println(" -> member.team = " + member.getTeam());
}
}
}
select
member0_.member_id as member_i1_1_,
member0_.age as age2_1_,
member0_.team_id as team_id4_1_,
member0_.username as username3_1_
from
member member0_
member = Member(id=3, username=member1, age=10)
select
team0_.team_id as team_id1_2_0_,
team0_.name as name2_2_0_
from
team team0_
where
team0_.team_id=?
-> member.getTeam() = Team(id=1, name=teamA)
// --------------------------------------------------
member = Member(id=4, username=member2, age=20)
-> member.getTeam() = Team(id=1, name=teamA)
// --------------------------------------------------
member = Member(id=5, username=member3, age=30)
select
team0_.team_id as team_id1_2_0_,
team0_.name as name2_2_0_
from
team team0_
where
team0_.team_id=?
-> member.getTeam() = Team(id=2, name=teamB)
// --------------------------------------------------
member = Member(id=6, username=member4, age=40)
-> member.getTeam() = Team(id=2, name=teamB)
👀 참고 자료
https://www.inflearn.com/course/Querydsl-%EC%8B%A4%EC%A0%84/dashboard
'[JPA] > 실전! Querydsl' 카테고리의 다른 글
[Querydsl] 스프링 데이터 JPA가 제공하는 Querydsl 기능 (0) | 2022.04.29 |
---|---|
[Querydsl] 스프링 데이터 JPA 와 Querydsl (0) | 2022.04.28 |
[Querydsl] 실무 활용 - 순수 JPA와 Querydsl (0) | 2022.04.27 |
[QueryDsl] 중급 문법 (0) | 2022.04.26 |
[Querydsl] 기본 문법 (0) | 2022.04.24 |