Spring Data JPA - QueryDSL ์„ค์ •

๋ชฝ๋ฃจ๋ฌธยท2024๋…„ 3์›” 26์ผ
0

Spring Data JPA

๋ชฉ๋ก ๋ณด๊ธฐ
7/7
post-thumbnail

๐Ÿ“ Spring Data JPA - QueryDSL ์„ค์ •

Spring Data JPA๋ฅผ ์‚ฌ์šฉํ•˜๋‹ค๋ณด๋ฉด Mybatis์˜ ๋™์ ์ฟผ๋ฆฌ๊ฐ€ ๊ทธ๋ฆฌ์›Œ ์งˆ ๋•Œ๊ฐ€ ์žˆ๋‹ค.
Select ๊ธฐ๋Šฅ์˜ ๊ฒฝ์šฐ Database์— ๋™์ ์ฟผ๋ฆฌ๊ฐ€ ํ•„์ˆ˜์ ์ธ๋ฐ, ์ด๋Ÿด๋•Œ QueryDSL์„ ์‚ฌ์šฉํ•˜์—ฌ Spring Data JPA์™€ ์—ฐ๋™ํ•˜๋ฉด ๋™์ ์ฟผ๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค!
Spring Boot + Maven ํ™˜๊ฒฝ์—์„œ ์„ค์ • ๋ฐฉ๋ฒ•์„ ์ •๋ฆฌํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.




๐Ÿšฉ QueryDSL ์„ค์ •

1. Pom.xml ์—์„œ Denpendency ์ถ”๊ฐ€

<dependencies>
  <!-- Querydsl Dependency-->
  <dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-jpa</artifactId>
  </dependency>

  <!-- Querydsl Dependency-->
  <dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-apt</artifactId>
    <scope>provided</scope>
  </dependency>
</dependencies>

2. Pom.xml ์—์„œ Plugins ์ถ”๊ฐ€

<build>
  <plugins>
    <plugin>
      <groupId>com.mysema.maven</groupId>
      <artifactId>apt-maven-plugin</artifactId>
      <version>1.1.3</version>
      <executions>
        <execution>
          <goals>
            <goal>process</goal>
          </goals>
          <configuration>
            <outputDirectory>target/generated-sources/java</outputDirectory>
            <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

3. Intellij -> Project Structure -> Modules ๋ฉ”๋‰ด์—์„œ 'target/generated-sources/java' ํด๋”๋ฅผ Source ํด๋”๋กœ ์ง€์ •

4. QueryDSL ์„ค์ •ํŒŒ์ผ ์ƒ์„ฑ

// Config Package ํ•˜์œ„์— ํ•˜๊ฑฐ๋‚˜ JPA ์™€ ๊ด€๋ จ๋œ Package ํ•˜์œ„์— Config Package๋ฅผ ๋งŒ๋“ค์–ด์„œ ์„ค์ •ํ•œ๋‹ค.
// ํ•„์ž๋Š” repository.config Package ํ•˜์œ„์— ์ž‘์„ฑํ•˜์˜€๋‹ค.
@Configuration
public class JPAConfig {
    @PersistenceContext
    private EntityManager entityManager;

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(entityManager);
    }
}

์ด์ƒ์œผ๋กœ QueryDSL์„ ์‚ฌ์šฉํ•  ๊ธฐ๋ณธ์ ์ธ ์„ค์ •์€ ๋ชจ๋‘ ๋งˆ์ณค๋‹ค!
๋‹ค์Œ์œผ๋กœ๋Š” Spring Data JPA์™€ ์—ฐ๋™ํ•˜์—ฌ CustomRepository ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฒ•์„ ์ •๋ฆฌํ•ด๋ณด๊ฒ ๋‹ค.

๐Ÿšฉ Spring Data JPA + QueryDSL Repository ์ƒ์„ฑ

Spring Data JPA + QueryDSL Repository ์ƒ์„ฑ์— ์•ž์„œ์„œ ๊ธฐ๋ณธ์ ์ธ ๊ตฌ์กฐ๋Š” ์•„๋ž˜์˜ ๊ทธ๋ฆผ์„ ์ฐธ๊ณ ํ•ด์•ผํ•œ๋‹ค.

  • ๊ธฐ๋ณธ์ ์ธ Spring Data JPA์˜ Repository๋Š” JpaRepository ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์†๋ฐ›์•„ ํ™•์žฅํ•œ ์ธํ„ฐํŽ˜์ด์Šค ์ด๋‹ค
  • Spring Data JPA + QueryDSL์„ ํ•˜๋‚˜์˜ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์‚ฌ์šฉ์ž ์ •์˜ Repository ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ QueryDSL์—์„œ ์‚ฌ์šฉํ•˜๋Š” Custom ์ธํ„ฐํŽ˜์ด์Šค๋„ ์ƒ์† ๋ฐ›์•„์„œ ํ™•์žฅํ•ด์•ผํ•œ๋‹ค.
  • QueryDSL Custom ์ธํ„ฐํŽ˜์ด์Šค๋Š” CustomImpl Class๊ฐ€ ์ƒ์†๋ฐ›์•„์„œ ๊ตฌํ˜„ํ•œ๋‹ค.
    โ†’ ๋ฐ˜๋“œ์‹œ ***Impl Naming์„ ์ง€์ผœ์•ผ Spring์—์„œ ์ธ์‹ํ•˜๋ฏ€๋กœ ์ฃผ์˜ํ•ด์•ผํ•œ๋‹ค.

1. Spring Data JPA Interface ์„ค์ •

// extend์— QueryDSL Custom ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ๋‹ค.
public interface UserInfoRepository 
        extends JpaRepository<UserInfo, Long>, UserInfoRepositoryCustom {
    @Override
    @EntityGraph(attributePaths = {"userDetailInfo"})
    Optional<UserInfo> findById(Long aLong);
}

2. QueryDSL Custom ์ธํ„ฐํŽ˜์ด์Šค ์„ค์ •

public interface UserInfoRepositoryCustom {
    List<UserInfo> findByRequestPayload(UserInfoSelectRequestPayload payload, 
                                        Pageable pageable);
}

3. QueryDSL Custom ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์†๋ฐ›์•„ CustomImpl Class ๊ตฌํ˜„

@Repository
@RequiredArgsConstructor
public class UserInfoRepositoryImpl implements UserInfoRepositoryCustom {
    private final JPAQueryFactory queryFactory;

    @Override
    public List<UserInfo> findByRequestPayload(UserInfoSelectRequestPayload payload,
                                               Pageable pageable) {
        return queryFactory.selectFrom(userInfo)
                .where(likeName(payload.getName()), 
                        eqSex(payload.getSex()), 
                        likeDowntownName(payload.getDowntownName()))
                .leftJoin(userInfo.downtownInfo, downtownInfo).fetchJoin()
                .leftJoin(userInfo.userDetailInfo, userDetailInfo).fetchJoin()
                .offset(pageable.getPageNumber())
                .limit(pageable.getPageSize())
                .fetch();
    }

    private BooleanExpression likeName(String name) {
        if (ObjectUtils.isEmpty(name))
            return null;

        return userInfo.name.like(name);
    }

    private BooleanExpression likeName(int name) {
        String findName = String.valueOf(name);

        if (ObjectUtils.isEmpty(findName))
            return null;

        return userInfo.name.like(findName);
    }

    private BooleanExpression eqSex(Sex sex) {
        if (ObjectUtils.isEmpty(sex))
            return null;

        return userInfo.userDetailInfo.sex.eq(sex);
    }

    private BooleanExpression likeDowntownName(String downtownName) {
        if (ObjectUtils.isEmpty(downtownName))
            return null;

        return userInfo.downtownInfo.name.like(downtownName);
    }
}

4. Service Layer ์—์„œ ์‚ฌ์šฉ

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class UserInfoServiceImpl implements IUserInfoService {
    private final UserInfoRepository userInfoRepository;

    @Override
    public UserInfoSelectResponsePayload userInfoSelect(UserInfoSelectRequestPayload payload, Pageable pageable) {
    	// ์—ฌ๊ธฐ์„œ ์‚ฌ์šฉํ•œ๋‹ค!!!!
        List<UserInfo> findUserInfoList = userInfoRepository.findByRequestPayload(payload, pageable);

        List<UserInfoDTO> userInfoDTOList = findUserInfoList.stream()
                .map(UserInfoDTO::new)
                .collect(Collectors.toList());

        UserInfoSelectResponsePayload userInfoSelectResponsePayload = new UserInfoSelectResponsePayload();
        userInfoSelectResponsePayload.setUserInfoList(userInfoDTOList);

        return userInfoSelectResponsePayload;
    }
}





๐Ÿ“Œ ๋งˆ๋ฌด๋ฆฌ

Spring Data JPA + QueryDSL ์˜ ๊ธฐ๋ณธ ์„ค์ • ๋ฐ ๊ตฌํ˜„์„ ๋ชจ๋‘ ์ •๋ฆฌํ•˜์˜€๋‹ค.
QueryDSL ์˜ CustomImpl Class์—์„œ ๊ตฌํ˜„ ์ฝ”๋“œ์— ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๊ธฐ๋ฒ•์ด ๋‚˜์™”๋Š”๋ฐ ๊ฐ„๋žตํ•˜๊ฒŒ ์„ค๋ช…ํ•˜์ž๋ฉด QueryFactory ๋ฅผ ์ด์šฉํ•˜์—ฌ ํ…Œ์ด๋ธ”๋“ค์„ SQL๋ฌธ ์ฒ˜๋Ÿผ ์กฐํšŒํ•˜๊ณ  LeftJoin.fetchJoin() Method๋ฅผ ํ™œ์šฉํ•˜์—ฌ N+1 ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•œ ๊ฒƒ์ด๋‹ค.
๋‹ค์Œ ์ •๋ฆฌ์—์„œ ์ž์„ธํ•˜๊ฒŒ ๋‹ค๋ค„๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค.

profile
์•Œ๊ณ  ์žˆ๋Š” ๊ฒƒ์„ ์ •๋ฆฌํ•˜๊ณ , ์ƒˆ๋กœ์šด ๊ฒƒ์„ ์•Œ๊ธฐ์œ„ํ•ด ๋„์ ์ด๋Š”๊ณณ..

0๊ฐœ์˜ ๋Œ“๊ธ€