Web/Spring
[Spring/스프링] Spring boot QueryDSL 사용하기
poopooreum
2024. 9. 30. 19:00
반응형
✏️ QueryDSL을 찾아보게 된 이유
Table을 만들게 되면 idx를 Primary Key로 설정하고나서 id를 만드는 경우가 있음
-
- User에 id라는 column이 있다고 가정
- 그러면 로그인을 할 때 사용자가 입력한 id가 User테이블에 있는지 검사를 해야함
- userRepository.findById(String id)로 찾고 싶지만, 필자는 idx라는 int자료형을 PK로 설정했음
- 위 문장에서 findById의 Id는 idx를 뜻함 => String id로 찾고 싶지만 이 함수는 PK를 기반으로 찾는 것이기 때문에 찾을 수가 없음대표적인 경우가 User테이블을 만드는 경우
✏️ build.gradle 설정
dependencies{
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
}
tasks.named('test') {
useJUnitPlatform()
jvmArgs '-Xshare:off' // JVM 아규먼트 설정
}
clean {
delete file('src/main/generated')
}
1. gradle - Tasks - build - clean 클릭
2. gradld - Tasks - build - build를 클릭하거나 compilie.java를 클릭
✏️ QueryDslConfig
public class QueryDslConfig {
@PersistenceContext
private EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
✏️ UserController
public class UserController {
private final UserService userService;
@PostMapping("/user/login")
public ResponseEntity<ResponseDTO<Object>> login(@RequestBody LoginDTO loginDTO) {
log.error("not post");
List<User>userList = userService.getUserById(loginDTO.getId());
if (userList.isEmpty()) {
return new ResponseEntity<>(ResponseDTO.builder().statusCode(41).message("id not exist.").data(null).build(), HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(ResponseDTO.builder().statusCode(20).message("로그인 성공").data(null).build(),HttpStatus.OK);
}
}
✏️ User
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "idx", nullable = false)
private Integer idx;
@Column(name = "id", nullable = false, length = 20)
private String id;
@Column(name = "password", nullable = false)
private String password;
@Column(name = "name", nullable = false, length = 20)
private String name;
}
✏️ LoginDTO
@Getter
@Setter
public class LoginDTO {
private String id;
private String password;
}
✏️ ResponseDTO
@Getter
@Setter
@Builder
public class ResponseDTO <T>{
private int statusCode;
private String message;
private T data;
}
✏️ CustomUserRepository
public interface CustomUserRepository {
List<User> findById(String id);
}
✏️ CustomUserRepositoryImpl
@Repository
@RequiredArgsConstructor
public class CustomUserRepositoryImpl implements CustomUserRepository {
private final JPAQueryFactory jpaQueryFactory;
@Override
public List<User> findById(String id) {
return jpaQueryFactory
.selectFrom(user)
.where(user.id.eq(id))
.fetch();
}
}
✏️ UserService
public interface UserService {
List<User> getUserById(String id);
}
✏️ UserServiceImpl
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
private final CustomUserRepository customUserRepository;
@Override
public List<User> getUserById(String id) {
return customUserRepository.findById(id);
}
}
QueryDSL return type
- UserController에서 id를 기준으로 User 테이블에서 검색을 해서 Optional<User>로 반환받을려고 했는데 QueryDSL은 Entity로 반환을 권장하지 않는다는 점을 알게 되었다.
- Entity type으로 return하지 않는 이유
- Entity의 모든 column을 가져오기 때문에, 불필요한 column도 가져온다
- @OneToOne 관계에 있는 엔티티의 정보도 가져와야 한다.
- @OneToOne 관계에서 N+1문제
- 일반적으로 OneToOne 양방향 관계에서는 Lazy Loading이 적용되지 않는다.
- 연관관계 주인 엔티티만 조회했을 경우엔 Lazy로 동작하지만, 연관관계 주인이 아닌 엔티티만 조회하면 Eager로 동작한다.
- OneToOne 관계에서 Lazy Loading이 작동하지 않는 이유
- 조회할 때, Lazy Loading으로 설정되어 있는 entity의 경우, entity내의 property는 proxy 개체를 대신 삽입하게 됨
- proxy 객체는 null을 감쌀 수 없기 때문에, Lazy Loading으로 설정되어 있는 entity는 proxy 객체 또는 null로 할당이 된다.
- 연관관계를 맺은 entity가 null인지 아닌지 확인을 한 후, proxy 객체로 할당할지, null로 할당할지 결정
- 연관관계 주인이 아닌 entity를 조회할 때, N+1의 문제가 생길 수 밖에 없다.
- 연관관계 주인인 entity는 FK를 가지고 있기 때문에, Fk 값이 null이면 해당 연관관계를 맺는 entity가 null인지 조회하지 않고도, 확인할 수 있다.
- 연관관계 주인이 아닌 entity → FK를 가지고 있지 않아, 연관관계를 맺고 있는 entity가 null인지 조회하지 않고 확인할 수 없기 때문에 연관관계를 맺고 있는 entity를 무조건 조회하는 것
- List 타입으로 반환
- fetch()를 사용
List<User> fetch = jpaQueryFactory .selectFrom(user) .fetch();
- boolean 타입으로 반환
- fetchOne()을 사용
- return jpaQueryFactory .selectFrom(user) .where(user.id.eq(id)) .fetchOne()!=null;
- Entity 타입으로 반환
- fetchFirst()를 사용
- return jpaQueryFactory .selectFrom(user) .where(user.id.eq(id)) .fetchFirst();
- fetchResults()와 fetchCount는 @Deprecated
정렬
return jpaQueryFactory
.selectFrom(user)
.where(user.id.eq(id))
.orderBy(user.id.desc())
.fetch();
페이징
List<User> result = jpaQueryFactory
.selectFrom(user)
.orderBy(user.id.desc())
.offset(1) // 0부터 시작
.limit(2)
.fetch();
반응형