EntityManager.find( ) 메서드를 사용하면 식별자로 엔티티 하나를 조회할 수 있다.
이렇게 조회한 엔티티에 객체 그래프 탐색을 사용하면 연관된 엔티티들을 찾을 수 있다.
만약 나이가 30살 이상인 회원을 모두 검색해야 한다면, 모든 회원 엔티티를 메모리에 올려두고 애플리케이션에서 30살 이상인 회원을 검색하는 것은 현실성이 없다.
일반 SQL을 직접 작성한다면 최대한 걸러서 조회할 수 있겠지만 ORM을 사용하면 엔티티 객체를 대상으로 개발하기 때문에 검색도 테이블이 아닌 엔티티 객체를 대상으로 하는 방법이 필요하다.
💡 JPQL은 이런 문제를 해결하기 위해 만들어졌다.
JPA는 JPQL와 다양한 검색 방법을 제공한다.
// JPQL 사용 예제
String jpql = "select m from Member as m where m.username = 'kim'";
List<Member> resultList = em.createQuery(jpql, Member.class).getResultList();
// Criteria Query 사용 예제
// Criteria 사용 준비
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Member> query = cb.createQuery(Member.class);
// 루트 클래스(조회를 시작할 클래스)
Root<Member> m = query.from(Member.class);
// 쿼리 생성
CriteriaQuery<Member> cq = query.select(m).where(cb.equal(m.get("username"), "kim));
List<Member> resultList = em.createQuery(cq).getResultList();
// QueryDSL 사용 에제
// 준비
JPAQuery query = new JPAQuery(em);
Qmember member = Qmember.member;
// 쿼리, 결과조회
List<Member> members =
query.from(member)
.where(member.username.eq("kim))
.list(member);
💡 QueryDSL도 어노테이션 프로세서를 사용해서 쿼리 전용 클래스를 만들어야 한다.
// Native SQL 사용 예제
String sql = "SELECT ID, AGE, TEAM_ID, NAME FROM MEMBER WHERE NAME = 'kim'";
List<Member> resultList = em.createNativeQuery(sql, Member.class).getResultList();
// JDBC 커넥션 직접 접근 예제
Session session = entityManager.unwrap(Session.class);
session.doWork(new Work() {
@Override
public void execute(Connection connection) throws SQLException {
// work ...
}
});
SELECT m FROM MEMBER AS m where m.username = 'Hello'
💡 JPA 표준 명세는 별칭을 식별 변수(identification variable)라는 용어로 정의했다.
// TypeQuery 사용 예제 - 반환할 타입이 명확한 경우
TypedQuery<Member> query = em.createQuery("SELECT m FROM Member m", Member.class);
List<Member> resultList = query.getResultList();
// Query 사용 예제 - 반환할 타입이 명확하지 않은 경우
Query query = em.createQuery("SELECT m.username, m.age from MEMBER m");
List resultList = query.getResultList();
💡 getSingleResult( )는 결과가 정확히 1개가 아니면 예외가 발생한다는 점에 주의해야 한다.
// 이름 기준 파라미터 사용
String usernameParam = "User1";
TypedQuery<Member> query =
em.createQuery("SELECT m FROM Member m where m.username = :username", Member.class);
query.setParameter("username", usernameParam);
List<Member> resultList = query.getResultList();
// JPQL 메서드 체이닝 예제
List<Member> members =
em.createQuery("SELECT m FROM Member m where m.username = :username", Member.class)
.setParameter("username", usernameParam)
.getResultList();
// 위치 기준 파라미터 사용 예제
List<Member> members =
em.createQuery("SELECT m FROM Member m where m.username = ?1", Member.class)
.setParameter(1, usernameParam)
.getResultList();
💡 위치 기준 파라미터 방식 보다는 이름 기준 파라미터 바인딩 방식을 사용하는 것이 더 명확하다.
// 파라미터 바인딩 방식을 사용하지 않고 직접 JPQL을 만들면 위험하다.
"select m from Member m where m.username = '" + usernameParam + "'";