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를 클릭

클릭 후 build가 생성된 모습

✏️ 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();
반응형