백엔드기술/스프링프레임워크

[사소한 TIP] Spring Data JPA에서 FindBy 와 FindAllBy 차이점

RevFactory 2023. 2. 17. 01:38

Spring Data JPA를 사용하다보면 지금 상황에서 findBy 를 써야할지 FindAllBy를 써야할지 고민해보신적 있나요?

저는 보통 collection 을 리턴하면 findAllBy를 사용하고, 리스트로 반환은 되지만 1개의 아이템이 예상되면 findBy를 쓰곤 했는데요.

 

결론 먼저 말씀드리면 findBy와 findAllBy 는 동일한 동작을 합니다.

 

1. Entity 와 Repository 선언

아래와 같은 Entity와 Repository가 있습니다.

Player

@Entity
public class Player {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    private Integer score;

    //all-arg and no-arg constructors
    //overriden equals method
    //getters and setters
}

 

PlayerRepository

findBy 와 findAllBy는 아래와 같이 PlayerRepository에 선언하여 사용할 수 있습니다.

@Repository 
public interface PlayerRepository extends JpaRepository<Player, Long> { 
    List<Player> findByScoreGreaterThan(Integer target);
    List<Player> findAllByScoreGreaterThan(Integer target);
}

 

 

2. 테스트 코드로 결과 비교

그리고 테스트 코드를 작성합니다.

@RunWith(SpringRunner.class)
@SpringBootTest(classes = FindByVsFindAllByApplication.class)
public class FindByVsFindAllByIntegrationTest {
    @Autowired
    private PlayerRepository playerRepository;

    @Before
    public void setup() {
        Player player1 = new Player(600);
        Player player2 = new Player(500);
        Player player3 = new Player(300);
        playerRepository.saveAll(Arrays.asList(player1, player2, player3));
    }

    @Test
    public void givenSavedPlayer_whenUseFindByOrFindAllBy_thenReturnSameResult() {
        List<Player> findByPlayers = playerRepository.findByScoreGreaterThan(400);
        List<Player> findAllByPlayers = playerRepository.findAllByScoreGreaterThan(400);
        assertEquals(findByPlayers, findAllByPlayers);
    }
}

 

테스트 결과는 두 개 메서드 결과가 동일합니다.

(참고: 해당 테스트를 통과하려면 플레이어 엔터티에 id 및 점수 필드를 모두 비교하는 재정의된 equals() 메서드가 있어야 합니다 .)

 

3. SQL 로그 비교

쿼리 로그를 추가하여 실제 수행되는 쿼리도 비교해봅니다.

application.properties

spring.jpa.show-sql=true

 

쿼리 결과

select
    player0_.id as id1_0_, player0_.score as score2_0_ 
from
    player player0_ 
where
    player0_.score>?

 

두 개 메서드로 수행되는 쿼리도 동일한 것을 확인할 수 있습니다.

 

4. 단일 결과 조회 시

만약에 위에 저의 케이스처럼 findBy 에서 결과값이 두개 이상이었다면 어떻게 될까요?

단일 결과를 반환하도록 인터페이스를 변경하면 NonUniqueResultException 이 발생할 위험이 있습니다.

따라서 지정된 단일 결과를 찾으려면 find 와 by 사이에 First 나 Top 과 같은 키워드를 추가해서 사용해야 합니다.

아래와 같이 말이죠.

Optional<Player> findFirstByScoreGreaterThan(Integer target);

 

 

5. 결론

위 결과에서 findBy 와 findAllBy는 동일하다는 것을 확인했습니다.

그렇다면 왜 동일한 기능을 두개로 만들었을까요?

그것은 JPA의 인터페이스를 유지하기 위함입니다.

단일 결과 조회 할 때와 같이 Find 와 By 사이에 키워드를 넣을 수 있는데 기본적으로는 All 이 모든 데이터를 조회하는 역할을 하고 있고, 편의성을 위해 생략하더라도 All과 동일한 동작을 하도록 보장한 것이라고 이해할 수 있겠습니다.

 

(참고) Spring Data JPA 문서 - 4.4.2 Query Creation

 

추가로 볼만한 것

- The difference between Spring Data JPA’s findById, getOne, getById, and findOne methods

 

원본 출처 : Baeldung - Difference Between findBy and findAllBy in Spring Data JPA