JPA @Query

์ด์ข…์œคยท2022๋…„ 2์›” 15์ผ
0

Spring JPA

๋ชฉ๋ก ๋ณด๊ธฐ
20/23

@Query

๐Ÿค”์•ž์„œ ๋ฐฐ์› ๋˜ ๋‚ด์šฉ๋งŒ ํ™œ์šฉํ• ์ˆ˜์žˆ์œผ๋ฉด JPA๋Š” ์“ธ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์•ฝ๊ฐ„์˜ ์˜ˆ์™ธ์ ์ธ ์ƒํ™ฉ์—์„œ Customํ•ด์•ผํ•˜๋Š” ์ƒํ™ฉ์ด ์ƒ๊ธด๋‹ค. ๊ทธ๋Ÿด๋•Œ๋Š” @Query๋ฅผ ์จ๋ณด์ž.

์™œ? @Query๐Ÿค”๐Ÿค”

์ฒซ๋ฒˆ์งธ ์ด์œ ๋กœ ๊ธด Query Method์˜ ๊ฐ€๋…์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด ์ค€๋‹ค. (JPQL ์‚ฌ์šฉ)

  • Query Method์˜ ์ด๋ฆ„์˜ ๊ธธ์ด ๊ต‰์žฅํžˆ ๊ธธ์–ด์งˆ๋•Œ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹๋‹ค. ์—„์ฒญ ๊ธด ์ด๋ฆ„์˜ ์ฟผ๋ฆฌ๋ฅผ ํ•œ๋ฒˆ ๋งŒ๋“ค์–ด๋ณด๊ณ  Test ํ•ด๋ณด์ž.
    // ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ null์ด๋ฉด์„œ ์ด๋ฆ„์€ ์ง€์ •ํ•œ๊ฐ’์œผ๋กœ,createdAt ๋ณด๋‹ค ํฌ๊ฑฐ๋‚˜ ๊ฐ™๊ณ , updatedAt ๋ณด๋‹ค ํฌ๊ฑฐ๋‚˜ ๊ฐ™์€ ์ฟผ๋ฆฌ
    List<Book> findByCategoryIsNullAndNameEqualsAndCreatedAtGreaterThanEqualAndUpdatedAtGreaterThanEqual(String name, LocalDateTime createdAt,LocalDateTime updatedAt);
    @Test
    void queryTest() {
        bookRepository.findAll().forEach(System.out::println);

        System.out.println("findByCategoryIsNullAndNameEqualsAndCreatedAtGreaterThanEqualAndUpdatedAtGreaterThanEqual : " +
                bookRepository.findByCategoryIsNullAndNameEqualsAndCreatedAtGreaterThanEqualAndUpdatedAtGreaterThanEqual(
                        "JPA ์ž‘์€๊ฒฉ์ฐจ ํด๋ž˜์Šค",
                        LocalDateTime.now().minusDays(1L),
                        LocalDateTime.now().minusDays(1L)
                ));
  • data.sql์—์„œ ๋™์ž‘ํ•˜๋Š” ์ฟผ๋ฆฌ๋Š” ๋ฆฌ์Šค๋„ˆ์˜ ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๋Š”๋‹ค.
insert into publisher(`id`,`name`,`created_At`,`updated_At`) value (1, '๋น ๋ฅธ๋Œ€ํ•™',now(),now());
insert into book(`id`,`name`,`publisher_id`, `deleted`, `created_At`,`updated_At`) values (2,'Spring Security ์ž‘์€๊ฒฉ์ฐจ ํด๋ž˜์Šค', 1,false,now(),now());
insert into book(`id`,`name`,`publisher_id`, `deleted`, `created_At`,`updated_At`) values (3,'SpringBoot ํ•˜๋‚˜์ธ ํด๋ž˜์Šค', 1,true,now(),now());
insert into book(`id`,`name`,`publisher_id`, `deleted`, `created_At`,`updated_At`) values (1,'JPA ์ž‘์€๊ฒฉ์ฐจ ํด๋ž˜์Šค', 1,false,now(),now());
  • ์œ„์™€๊ฐ™์ด ์ฟผ๋ฆฌ๋ฌธ์— ์ผ์ผ์ด ์ถ”๊ฐ€ํ•ด์ค˜๋„ ๋˜์ง€๋งŒ JPA๋ฅผ ์ด์šฉํ•ด๋ณด์ž.
@EntityListeners(value = AuditingEntityListener.class)
public class BaseEntity implements Auditable {
    @CreatedDate
    @Column(columnDefinition = "datetime(6) default now(6) comment '์ƒ์„ฑ์‹œ๊ฐ„'", nullable = false, updatable = false)
    private LocalDateTime createdAt;

    @LastModifiedDate
    @Column(columnDefinition = "datetime(6) default now(6) comment '์ˆ˜์ •์‹œ๊ฐ„'", nullable = false)
    private LocalDateTime updatedAt;
}
  • columnDefinition์„ ์ด์šฉํ•ด์„œ Auto DDL์„ ์ƒ์„ฑํ•˜๋Š”๊ฑด ํ˜„์—…์—์„œ ์ž˜ ์“ฐ์ด์ง€๋Š” ์•Š์ง€๋งŒ ์•Œ์•„๋‘์ž.
  • ์œ„์™€๊ฐ™์ด columnDefinition์„ ์ฃผ๋ฉด DDL ์ƒ์„ฑ์„ ํ• ๋•Œ ์•„๋ž˜์™€ ๊ฐ™์ด ์ƒ์„ฑ๋œ๋‹ค.
    create table author (
       id bigint not null auto_increment,
        created_at datetime(6) default now(6) comment '์ƒ์„ฑ์‹œ๊ฐ„' not null,
        updated_at datetime(6) default now(6) comment '์ˆ˜์ •์‹œ๊ฐ„' not null,
  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ์œ„์™€๊ฐ™์ด ๊ณ ์น˜๊ณ  Testํ•˜๋ฉด ์ž˜ ๋‚˜์˜ฌ ๊ฒƒ์ด๋‹ค.
  • ์ž, ๊ธธ๊ณ  ๊ธด method ์–ด๋–ป๊ฒŒ ๊ณ ์น˜๋ƒ? ๋ฐ”๋กœ @Query๋ฅผ ์ด์šฉํ•˜์ž.
    @Query(value = "select b from Book b where name = ?1 and createdAt >= ?2 and updatedAt >= ?3 and category is null")
    List<Book> findByNameRecently(String name, LocalDateTime createdAt,LocalDateTime updatedAt);
    @Test
    void queryTest() {
        System.out.println("findByNameRecently : " +
                bookRepository.findByNameRecently(
                        "JPA ์ž‘์€๊ฒฉ์ฐจ ํด๋ž˜์Šค",
                        LocalDateTime.now().minusDays(1L),
                        LocalDateTime.now().minusDays(1L)));
  • ์œ„์—์„œ ์‚ฌ์šฉ๋œ ์ฟผ๋ฆฌ๊ฐ€ JPQL์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค.
  • JPQL์˜ ์žฅ์ ์€ ๋‹ค์ด์–ผ๋ ‰ํŠธ(๋ฐฉ์–ธ)๊ธฐ๋Šฅ๋•Œ๋ฌธ์— DB์— ๋งž๊ฒŒ Query๋ฅผ ์ƒ์„ฑํ•ด์ค€๋‹ค. ๊ทธ๋ž˜์„œ ๋„ค์ดํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ์™€ ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค. ๋˜ํ•œ ํด๋ž˜์Šค์œ„์— ์‚ฌ์šฉํ•œ @Where ๋„ ์ ์šฉ๋œ๋‹ค. ?1,?2,?3์€ ๊ฐ๊ฐ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋œปํ•œ๋‹ค. ํ•˜์ง€๋งŒ ์ž๋ฐ”์—์„œ๋Š” ์ˆœ์„œ์— ์˜์กดํ•˜๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ์‚ฌ์šฉ์€ ์ง€์–‘ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ @Param์„ ์‚ฌ์šฉํ•œ๋‹ค.
    @Query(value = "select b from Book b where name = :name and createdAt >= :createdAt and updatedAt >= :updatedAt and category is null")
    List<Book> findByNameRecently(
            @Param("name") String name,
            @Param("createdAt") LocalDateTime createdAt,
            @Param("updatedAt") LocalDateTime updatedAt);

๋‘๋ฒˆ์งธ ์ด์œ ๋กœ Entity์— ์—ฐ๊ฒฐ๋˜์ง€์•Š๋Š” ์ฟผ๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

  • ์‹ค์ œ ํ˜„์—…์—์„œ ์‚ฌ์šฉํ•˜๋Š” Entity์— ์ปฌ๋Ÿผ์€ ์ •๋ง ๋งŽ๋‹ค. ๊ทธ๋Ÿด๋•Œ ํ•„์š”ํ•œ ์ปฌ๋Ÿผ๋งŒ ์ถ”๋ ค์„œ ์กฐํšŒํ• ์ˆ˜ ์žˆ๋‹ค.
  • ์‹ค์Šต์„ ํ•ด๋ณด์ž. ๋จผ์ € dtoํŒจํ‚ค์ง€๋ฅผ ๋งŒ๋“ค๊ณ  BookNameAndCategory ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์ž.
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BookNameAndCategory {
    private String name;
    private String category;
}
  • ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ตฌ์ฒดํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Query๋ฅผ ๋ฐ›์•„์˜ฌ์ˆ˜ ์žˆ๋‹ค.
    @Query(value = "select new com.practice.jpa.bookmanager.repository.dto.BookNameAndCategory(b.name, b.category) from Book b")
    List<BookNameAndCategory> findBookNameAndCategory();
  • ์ด๋ ‡๊ฒŒ DTO ๊ตฌ์ฒดํด๋ž˜์Šค๋ฅผ ์ด์šฉํ•˜์—ฌ ์›ํ•˜๋Š” ์ปฌ๋Ÿผ๋งŒ ์‚ฌ์šฉํ•˜์—ฌ @Query๋ฅผ ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.

@Query๋ฅผ ์ด์šฉํ•œ Paging

    @Query(value = "select new com.practice.jpa.bookmanager.repository.dto.BookNameAndCategory(b.name, b.category) from Book b")
    Page<BookNameAndCategory> findBookNameAndCategory(Pageable pageable);

test

        bookRepository.findBookNameAndCategory(PageRequest.of(1, 1)).forEach(
                bookNameAndCategory -> System.out.println(bookNameAndCategory.getName() + " : " + bookNameAndCategory.getCategory()));

        bookRepository.findBookNameAndCategory(PageRequest.of(0, 1)).forEach(
                bookNameAndCategory -> System.out.println(bookNameAndCategory.getName() + " : " + bookNameAndCategory.getCategory()));
profile
OK๊ฐ€์ž

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