✔️ springinitializer
✔️ build.gradle
// 추가
buildscript {
ext {
queryDslVersion = "5.0.0"
}
}
plugins {
id 'java'
id 'org.springframework.boot' version '2.7.8'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
// 추가
id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
}
group = 'QueryDSLStudy'
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'
//querydsl 추가
implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"
annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}"
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
// 테스트에서 lombok 사용 - 추가
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
// 추가
// querydsl 설정 추가
// querydsl에서 사용할 경로를 설정한다.
def querydslDir = "$buildDir/generated/querydsl"
// JPA 사용 여부와 사용할 경로를 설정
querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
// build 시 사용할 sourceSet 추가한다.
sourceSets {
main.java.srcDir querydslDir
}
// querydsl이 compileClassPath를 상속하도록 설정
configurations {
compileOnly {
extendsFrom annotationProcessor
}
querydsl.extendsFrom compileClasspath
}
// querydsl 컴파일 시 사용할 옵션 설정
compileQuerydsl {
options.annotationProcessorPath = configurations.querydsl
}
✔️ test
Controller
추가compileQuerydsl
로 실행
실행 후, 구체적으로 어떠한 것이 생기는 지 궁금하다면 이전 참고자료 를 보면 될 것 같다.
QHello
가 생성되었다.
✔️ h2 database
✔️ 실행 파일 h2start
✔️ 생성된 h2 DB 연결
spring:
datasource:
url: jdbc:h2:tcp://localhost//Users/leekyoungchang/Desktop/Study/computer/db/h2Database/querydslDB
username: sa
password: 1234
driver-class-name: org.h2.Driver
jpa:
hibernate:
ddl-auto: update
properties:
hibernate:
format_sql: true
logging:
level:
org.hibernate.SQL: debug
📡 실행 순서
UserSignUpRequestDto -> UserController -> UserService -> UserRepository -> UserRepositoryInformation -> UserRepositoryInformationImpl
✔️ Entity 생성후, Tasks/other/compileQuerydsl 클릭을 통해 QEntity 클래스 생성
✏️ Native Query
Native Query
: 순수 쿼리, 자기가 사용하는 DB에 맞게 쿼리문을 직접 만들 때 주로 사용한다.
Hibernate
가 지원하는 메서드 내부에서는 JDBC API가 동작하고 있어, 개발자가 직접 SQL을 작성하지 않아도 된다.- 만약 사용자가 sql을 입력했을 때 Hibernate가 자동으로 그 DB에 해당하는 sql문으로 바꾸어준다.
- 그런데
Native Query
를 사용하면 사용자는 사용하는 DB에 맞게 쿼리문을 직접 입력해야 한다.
나는 @QueryProjection
을 이용해 결과를 반환했다.
✔️ QueryProjection 사용해보기
@QueryProjection
을 dto에 추가후, compileQuerydsl 클릭!
QUserCheckRequestDto
생성된다.
시작하기 전 테스트 값 미리 넣기
✔️ Controller
@Slf4j
@Controller
public class UserController {
@Autowired
UserService userService;
@PostMapping("/test")
public ResponseEntity<List<UserCheckRequestDto>> signIn(UserSignUpRequestDto userRequestDto){
log.info("user : " + userRequestDto.getUsername() + " pwd : " + userRequestDto.getPassword());
userRequestDto.setUsername("chang");
userRequestDto.setPassword("chang");
return ResponseEntity.ok(userService.signIn(userRequestDto));
}
}
/test
로 보내도 null이 결과로 나온다. (고치지 못한 문제점)setUsername
, setPassword
를 추가
✔️ Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
@Service
public class UserService {
private final UserRepository userRepository;
// 회원가입
// public UserResponseDto signup(UserSignUpRequestDto userRequestDto){
//
// }
// 로그인
public List<UserCheckRequestDto> signIn(UserSignUpRequestDto userRequestDto){
return userRepository.findUserInformation(userRequestDto);
}
}
✔️ Repository
public interface UserRepository extends JpaRepository<UserEntity, Long>, UserRepositoryInformation {
}
✔️ UserRepository
는 왜 UserRepositoryInformation
을 상속받는가?
보통 sql문이 길거나 복잡할 때 xxImpl
클래스를 만들어서, sql문을 적는다. (interface <-> xxxImpl
)
즉, QueryDsl
사용하기 위해서 상속받았다.
✔️ interface
public interface UserRepositoryInformation {
List<UserCheckRequestDto> findUserInformation(UserSignUpRequestDto userRequestDto);
}
✔️ xxImpl
import QueryDSLStudy.user.QUserCheckRequestDto;
import QueryDSLStudy.user.UserCheckRequestDto;
import QueryDSLStudy.user.UserSignUpRequestDto;
import com.querydsl.jpa.impl.JPAQueryFactory;
import javax.persistence.EntityManager;
import java.util.List;
import static QueryDSLStudy.user.QUserEntity.userEntity;
public class UserRepositoryInformationImpl implements UserRepositoryInformation {
private final JPAQueryFactory jpaQueryFactory;
public UserRepositoryInformationImpl(EntityManager em) {
this.jpaQueryFactory = new JPAQueryFactory(em);
}
@Override
public List<UserCheckRequestDto> findUserInformation(UserSignUpRequestDto userRequestDto) {
return jpaQueryFactory
.select(new QUserCheckRequestDto(
userEntity.username.as("username"),
userEntity.password.as("password"),
userEntity.address.as("address"),
userEntity.phone.as("phone")))
.from(userEntity)
.where(
userEntity.username.eq(userRequestDto.getUsername()),
userEntity.password.eq(userRequestDto.getPassword())
).fetch();
}
}
QueryDSLStudy.user.QUserEntity.userEntity;
내부적으로 QUserEntity 생성한 객체를 사용한다.JPAQueryFactory
을 사용해서 select문
을 이용한다.fetch join
username
과 password
를 가진 회원의 정보가 List
로 넘어온다.
✔️ 실행 결과
결과로, 복잡한 sql문이 있을 때 어떻게 해결해야할지? Querydsl
사용법을 알게 된 것 같다.