Search

JPA의 여러 쿼리 방법

생성일
2024/01/30 01:45
태그
spring
Database
상태
Done

JPA의 쿼리 방법

JPA는 JPQL, JPA Criteria, QueryDSL, 네이티브 SQL 등 다양한 쿼리 방법을 지원한다. 그 외에도 JDBC API를 직접 사용하는 방법이나, MyBatis나 SpringJdbcTemplate을 함께 사용하는 것도 가능하다.
JPQL
JPA를 사용한다면 엔티티 객체를 중심으로 개발을 해야하는데, 조회 쿼리를 수행할 때에도 테이블이 아닌 엔티티 객체를 대상으로 검색해야하는 문제가 있다. 모든 DB 데이터를 엔티티 객체로 변환해서 검색하는 것은 불가능하기 때문에, 애플리케이션에서 원하는 데이터를 불러올 때 검색 조건이 포함된 SQL이 필요하기에 JPA에서 JPQL을 지원한다.
SELECT m FROM Member m WHERE m.username like '%kim%'
SQL
복사
JPQL은 SQL을 추상화한 객체 지향 쿼리 언어로, SQL 문법과 유사하며 테이블이 아닌 엔티티 대상으로 쿼리를 수행하고 SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 등 ansi 표준 문법을 전부 지원한다.
Criteria
이러한 JPQL은 결국 문자열을 만들어서 넣는 방법이기 때문에, 동적 쿼리를 작성하는데 어려움이 많다. 이에 대한 대안으로 나온 것이 Criteria와 QueryDSL이다. 하지만 Criteria는 SQL과 이질감이 커서 직관적으로 이해하기가 어렵고, 그 때문에 너무 복잡하고 유지보수성이 낮아 실용성이 없다.
QueryDSL
List<Member> list = query.selectFrom(m) .where(m.age.gt(18)) .orderBy(m.name.desc()) .fetch();
Java
복사
반면 QueryDSL은 초기에 설정하기가 어렵지만, 위 코드를 보면 알 수 있듯이 단순하고 쉬워 직관적으로 이해되고 문법이나 필드명이 틀린 경우에도 컴파일 오류를 발생시키기 때문에 유지보수 측면에서 굉장히 좋다. 때문에 동적 쿼리를 작성한다면 가급적 QueryDSL을 사용하길 권장된다.
NativeQuery
String query = "SELECT id, age, team_id, name FROM member WHERE name = 'kim'" List<Member> resultList = em.createNativeQuery(query, Member.class) .getResultList();
Java
복사
네이티브 SQL은 SQL을 직접 사용할 수 있도록 JPA가 제공하는 기능으로, 네이티브 쿼리를 통해 JPQL로 해결할 수 없는 특정 데이터베이스의 방언이나 함수 같은 기능들을 사용할 수 있다.
JDBC 직접 호출, SpringJdbcTemplate
JPA를 사용하면서 JDBC 커넥션을 직접 사용하거나, 스프링 JdbcTemplate, MyBatis 등을 함께 사용하여 쿼리를 작성할 수도 있다. 하지만 JPA를 우회하여 SQL을 실행하기 때문에, JPA에서 query를 날리기 전에 자동으로 플러시하는 기능이 동작하지 않는다. 때문에 DB에 값을 새로 저장하거나 변동 사항이 있다면, 쿼리 실행 직전에 영속성 컨텍스트를 강제로 수동 플러시 해주어야 한다.

JPQL과 Spring Data JPA

JPQL(Java Persistence Query Language)은 엔티티 객체를 대상으로 쿼리할 수 있어, SQL의 데이터베이스 테이블을 엔티티 객체처럼 취급하여 사용할 수 있게 지원하는 SQL이다. 다시말해, 객체 지향 SQL이라고 볼 수 있고, 이를 통해 개발자는 더 쉽게 객체 지향적인 구조로 개발을 할 수 있는 것이다.
@Autowried private final EntityManager em; String jpql = "SELECT m FROM Member m WHERE m.name LIKE 'hello%'; List<Member> result = em.createQuery(jpql, Member.class).getResultList();
Java
복사
일반적으로 JPA에서는 위와 같이 JPQL을 사용하여, 쿼리를 직접 작성하여 CRUD를 처리할 수 있다.
최근 Spring을 통해 서버를 개발한다고 하면, EntityManager를 한번도 사용해보지 않고 구현하게 된다. 이는 Spring에서 JPA를 더 쉽고 편하게 사용하도록, JPA를 추상화한 Spring Data JPA라는 모듈을 제공하기 때문이다.
@Repository public interface UserRepository extends JpaRepository<User, Long> {} @Service public class UserService { @Autowired private UserRepository userRepository; public List<User> findAll() { return userRepository.findAll(); } public User findUser(String name) { return userRepository.findByName(name); } public void saveUser(User user) { userRepositroy.save(user); } }
Java
복사
Spring Data JPA에서는 위와 같이 JpaRepository를 확장하는 Repository 인터페이스를 생성하여 사용한다. 그 후 선언한 인터페이스를 통해 위처럼 findAllByName와 같이 미리 정해진 규칙에 따라 호출하면, 별도의 JPQL이나 EntityManager를 통해 트랜잭션을 관리할 필요 없이 Spring Data JPA가 CRUD 처리를 수행할 수 있다.

참고