반응형
JPA 게시글은 대부분 인프런의 김영한님의 강의인 '자바 ORM 표준 JPA 프로그래밍' 기반으로 내용을 정리했습니다.
객체지향 쿼리 언어(JPQL)
JPA는 다양한 쿼리 방법 지원
- JPQL
- JPA Criteria
- QueryDSL
- 네이티브 SQL
- JDBC API 직접 사용, MyBatis, SpringJdbcTemplate 함께 사용
JPQL
- 가장 단순한 조회 방법
- EntityManager.find()
- 객체 그래프 탐색
- 나이가 18살 이상인 회원을 모두 검색하고 싶다면? 이런 경우에는 em.find()로 가져올 수가 없다.
- JPA를 사용하면 엔티티 객체를 중심으로 개발
- 문제는 검색 쿼리
- 검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색
- 모든 DB 데이터를 객체로 변환해서 검색하는 것은 불가능
- 애플리케이션이 필요한 데이터만 DB에서 불러오려면 결국 검색 조건이 포함된 SQL이 필요
- JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 제공한다.
- SQL과 문법이 유사하며 SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN을 지원한다.(ANSI 표준 전부 지원)
- JPQL은 엔티티 객체를 대상으로 쿼리한다.
- SQL은 데이터베이스 테이블을 대상으로 쿼리한다.
조회
List<Member> result = em.createQuery("select m From Member m where m.username like '%kim%'",
Member.class).getResultList();
- em.createQuery() 메서드를 이용해 JPQL을 사용할 수 있다.
- get.ResultList()는 조회해 온 결과를 List로 만들어준다.
- JPQL은 테이블이 아닌 객체를 대상으로 쿼리한다.
// 출력 로그
Hibernate:
/* select
m
From
Member m
where
m.username like '%kim%' */ select
member0_.MEMBER_ID as MEMBER_I1_6_,
member0_.city as city2_6_,
member0_.street as street3_6_,
member0_.zipcode as zipcode4_6_,
member0_.USERNAME as USERNAME5_6_,
member0_.endDate as endDate6_6_,
member0_.startDate as startDat7_6_
from
Member member0_
where
member0_.USERNAME like '%kim%'
JPQL을 한마디로 정의하면 객제 지향 SQL이다.
Criteria
- JPQL의 경우 쿼리가 String으로 이루어져있어 동적 쿼리를 만들기 어렵다.
- 문자가 아닌 자바 코드로 JPQL을 작성할 수 있다.
- JAVA 표준 문법에서 지원하는 기능이며 JPA 공식 기능이다.
// 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();
- Criteria는 동적 쿼리를 쉽게 만들 수 있다.
- JAVA 표준 문법이라 String 제외 메서드에서 오타가 날 시 에러 표시가 되어 에러를 찾기 쉽다.
- 하지만 사용하기에 어려움이 있어 실무에서는 잘 사용하지 않는다.
- 복잡하여 유지보수가 어렵다.
- 그래서 대신 QueryDSL 사용을 권장한다.
QueryDSL 소개
- 문자가 아닌 자바코드로 JPQL을 작성할 수 있다.
- JPQL 빌더 역할
- 컴파일 시점에 문법 오류를 찾을 수 있다.
- 동적 쿼리 작성이 편리하다.
- 단순하고 쉽다.
- 실무에서 사용하길 권장한다.
//JPQL
// select m from Member m where m.age > 18
JAPFactoryQuery query = new JPAQueryFactory(em);
QMember m = QMember.member;
List<Member> list =
query.selectFrom(m)
.where(m.age.at(18))
.orderBy(m.name.desc())
.fetch();
JPQL을 잘하면 QueryDSL는 쉽게 배울 수 있다.
네이티브 SQL 소개
- JPA가 제공하는 SQL을 직접 사용하는 기능
- JPQL로 해결할 수 없는 특정 데이터베이스에 의존적인 기능
예) 오라클 CONNECT BY, 특정 DB만 사용하는 SQL 힌트 - 잘 사용하지 않는다.
String query = "select MEMBER_ID, city, street, zipcode, USERNAME from MEMBER";
List<Member> resultList =
em.createNativeQuery(query).getResultList();
JDBC 직접 사용, SpringJdbcTemplate 등
- JPA를 사용하면서 JDBC 커넥션을 직접 사용하거나, 스프링 JdbcTemplate, Mybatis 등을 함께 사용 가능하다.
- 단 영속성 컨텍스트를 적절한 시점에 강제로 플러시 해줘야한다.
예) JPA를 우회에서 SQL을 실행하기 직전에 영속성 컨텍스트를 수동 플러시 한다.
Member member = new Member();
member.setUsername("member1");
em.persist(member);
// flush ->commit, query
String query = "select MEMBER_ID, city, street, zipcode, USERNAME from MEMBER";
List<Member> resultList =
em.createNativeQuery(query, Member.class).getResultList();
for(Member member1 : resultList) {
System.out.println("member1 = " + member1);
}
tx.commit();
위 코드는 정상적으로 작동되는 코드이다.
Hibernate:
call next value for hibernate_sequence
Hibernate:
/* insert hellojpa.Member
*/ insert
into
Member
(city, street, zipcode, USERNAME, endDate, startDate, MEMBER_ID)
values
(?, ?, ?, ?, ?, ?, ?)
Hibernate:
/* dynamic native SQL query */ select
MEMBER_ID,
city,
street,
zipcode,
USERNAME
from
MEMBER
member1 = hellojpa.Member@5eccd3b9
위 출력 로그를 보면 select 하기 전에 먼저 flush가 된다. 그래야 persist() 한 것을 DB에 반영하고, 그 반영된 데이터 값을 가져올 수 있기 때문이다. 참고로 flush는 commit, query() 시 자동으로 동작한다.
그런데 조회 쿼리를 스프링 DB 커넥션 같은 걸 사용해서 조회를 할 때 문제가 생긴다. DB 커넥션은 JPA에서 관리하지도 않아 조회 전에 flush가 자동으로 동작하지 않아 persist()한 member는 아직 JPA 안에 있어, 제대로 된 데이터를 가져올 수가 없게된다. 그러니 조회 전에 강제로 flush를 날려줘야된다.
반응형