Table of Content
QueryDSL 시작하기
MariaDB 설치
여기 에서 MariaDB 를 다운받아 설치한다.
JPA 테스트
CREATE DATABASE db_test;
USE db_test;
SHOW TABLES;
CREATE TABLE tbl_user (
id int NOT NULL AUTO_INCREMENT,
username varchar(32),
email varchar(32),
PRIMARY KEY (id)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
build.gradle
......
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.mariadb.jdbc:mariadb-java-client:3.0.5'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
......
application.yml
spring:
datasource:
driverClassName: org.mariadb.jdbc.Driver
url: jdbc:mariadb://localhost:3306/db_test
# driverClassName: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://localhost:3306/test
username: testuser
password: testpass
jpa:
show-sql: true
hibernate:
ddl-auto: update
format_sql: true
@Getter
@Builder
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "tbl_user", catalog = "db_test")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String username;
private String email;
}
public interface UserRepository extends JpaRepository<User, Integer> {
List<User> findByUsername(String username);
}
@SpringBootTest
class UserRepositoryTest {
@Autowired
private UserRepository repository;
@Test
void jpa() {
// given
User user = User
.builder()
.username("Lee")
.email("skyer9@gmail.com")
.build();
// when
repository.save(user);
List<User> list = repository.findByUsername("Lee");
// then
assertTrue(list.size() > 0);
User item = list.get(0);
Assertions.assertEquals(item.getUsername(), "Lee");
}
}
QueryDSL 설정
// 1. queryDsl version
buildscript {
ext {
queryDslVersion = "5.0.0"
}
}
plugins {
id 'org.springframework.boot' version '2.6.3'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
// 2. querydsl plugins
id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
id 'java'
}
//...
dependencies {
// 3. querydsl dependencies
implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"
implementation "com.querydsl:querydsl-apt:${queryDslVersion}"
//...
}
test {
useJUnitPlatform()
}
/*
* queryDSL 설정 추가
*/
// querydsl에서 사용할 경로 설정
def querydslDir = "$buildDir/generated/querydsl"
// JPA 사용 여부와 사용할 경로를 설정
querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
// build 시 사용할 sourceSet 추가
sourceSets {
main.java.srcDir querydslDir
}
// querydsl 컴파일시 사용할 옵션 설정
compileQuerydsl{
options.annotationProcessorPath = configurations.querydsl
}
// querydsl 이 compileClassPath 를 상속하도록 설정
configurations {
compileOnly {
extendsFrom annotationProcessor
}
querydsl.extendsFrom compileClasspath
}
아래와 같이 compileQuerydsl 을 실행시키면,
QClass 가 생성된다.
QueryDSL 테스트
@Configuration
public class QueryDslConfig {
@PersistenceContext
private EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
ompileQuerydsl 를 먼저 실행시켜야 한다.
annotation 활성화를 시켜줘야 한다.
Lombok 이 작동한다고 annotation 이 활성화되어 있다고 생각하지 말자.
여전히 인식하지 못하면 프로젝트를 닫았다 다시 열자.
import static com.example.querydsltest.domain.QUser.user;
@Repository
public class UserDslRepository extends QuerydslRepositorySupport {
private final JPAQueryFactory jpaQueryFactory;
public UserDslRepository(JPAQueryFactory jpaQueryFactory) {
super(User.class);
this.jpaQueryFactory = jpaQueryFactory;
}
public List<User> list() {
return jpaQueryFactory
.selectFrom(user)
.where(user.username.eq("Lee")).fetch();
}
}
@SpringBootTest
class UserDslRepositoryTest {
@Autowired
UserDslRepository repository;
@Test
void querydsl() {
// given
// when
List<User> list = repository.list();
// then
assertTrue(list.size() > 0);
User item = list.get(0);
Assertions.assertEquals(item.getUsername(), "Lee");
}
}
group by
생성자는 쓰이지 않고 Setter
가 쓰인다.
따라서, @Setter
와 @NoArgsConstructor
는 반드시 있어야 한다.
생성자를 이용한 객체 생성의 경우,
누군가 생성자 파라미터의 순서가 맘에 들지 않는다고,
뜯어고쳤을 때 찾아낼 수 없는 버그가 만들어지기에 좋지않다.
@Getter
@Setter
@NoArgsConstructor
public class UserGroupByDto {
private String username;
private Long count;
}
public List<UserGroupByDto> groupBy() {
return jpaQueryFactory
.select(
Projections.bean(
UserGroupByDto.class
, user.username
, user.count().as("count")
)
)
.from(user)
.groupBy(user.username)
.orderBy(user.username.asc())
.fetch();
}
@Test
void groupBy() {
// given
// when
List<UserGroupByDto> list = repository.groupBy();
// then
assertTrue(list.size() > 0);
UserGroupByDto item = list.get(0);
Assertions.assertEquals(item.getUsername(), "Lee");
Assertions.assertEquals(item.getCount(), 3);
}
JPA repository 에서 QueryDSL 사용하기
DSL Repository 인터페이스를 생성합니다.
public interface UserDslRepository {
List<User> list();
}
UserRepository 에 추가해 줍니다.
public interface UserRepository extends JpaRepository<User, Integer>, UserDslRepository {
List<User> findByUsername(String username);
}
구현 클래스는 클래스명이 DSL Repository 인터페이스
+ Impl
로 작성해야 합니다.
public class UserDslRepositoryImpl extends QuerydslRepositorySupport implements UserDslRepository {
private final JPAQueryFactory jpaQueryFactory;
public UserDslRepositoryImpl(JPAQueryFactory jpaQueryFactory) {
super(User.class);
this.jpaQueryFactory = jpaQueryFactory;
}
@Override
public List<User> list() {
System.out.println("DSL Called");
return jpaQueryFactory
.selectFrom(user)
.where(user.username.eq("Lee")).fetch();
}
}
JPA Repository 에서 DSL 이 호출되는 것을 확인할 수 있습니다.
@SpringBootTest
class UserRepositoryTest {
@Autowired
private UserRepository repository;
@Test
void listWithDsl() {
// given
// when
List<User> list = repository.list();
// then
assertTrue(list.size() > 0);
User item = list.get(0);
Assertions.assertEquals(item.getUsername(), "Lee");
}
}
```
```java