[사소한 TIP] Spring Data JPA에서 FindBy 와 FindAllBy 차이점
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