출처 : https://www.inflearn.com/course/ORM-JPA-Basic
자바 ORM 표준 JPA 프로그래밍 - 기본편 강의 - 인프런
현업에서 실제로 JPA로 개발을 하고 있습니다. 그런 입장에서보면 지금 작성하고 있는 코드들이 어떻게 작동하는지 이해하는데 큰 도움을 주는 강의입니다. 다음은 제가 느낀 이 강의의 장점들
www.inflearn.com
JPA가 지원하는 쿼리 방법
- JPQL
- JPA Criteria
- QueryDSL
- 네이티브 SQL
- JDBC API 직접 사용, MyBatis, SpringJdbcTemplate 함께 사용
JPQL
- 객체 지향 쿼리 언어
- 가장 단순한 조회 방법
- 엔티티 객체를 대상으로 쿼리
- 검색 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색
- SQL 문법과 유사, SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 지원
- 특정 데이터베이스에 의존 X
try{
List<Member> result = em.createQuery(
// SQL로 번역하여 실행함. Member = 엔티티를 가르킴
"select m From Member m where m.username like '%kim%'",
Member.class
).getResultList();
for (Member member : result) {
System.out.println("member : " + member);
}
tx.commit();
Criteria
- 문자가 아닌 자바코드로 JPQL을 작성할 수 있다. (동적 쿼리를 작성할 때 좋음)
- JPQL 빌더 역할
- 단점 : 너무 복잡하고 실용성 없다. (실무에서 사용하지 않는다고 하심)
- Criteria 대신에 QueryDSL 사용을 권장
// 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);
String username = "abcd";
if(username != null){
cq = cq.where(cb.equal(m.get("username"),username));
}
List<Member> resultList = em.createQuery(cq).getResultList();
tx.commit();
QueryDSL
- 문자가 아닌 자바코드로 JPQL을 작성할 수 있음
- JPQL 빌더 역할
- 컴파일 시점에 문법 오류를 찾을 수 있음
- 동적쿼리 작성 편리
- 단순하고 쉬움
- 실무 사용 권장
- JPQL 문법을 잘 알면 배우기 쉬움
네이티브 SQL
- JPA가 제공하는 SQL을 직접 사용하는 기능
- JPQL로 해결할 수 없는 특정 데이터베이스에 의존적인 기능
- 예) 오라클 CONNECT BY, 특정 DB만 사용하는 SQL 힌트
em.createNativeQuery("select MEMBER_ID, city, street, zipcode, USERNAME from MEMBER")
.getResultList();
JDBC 직접 사용, SpringJdbcTemplate 등
- JPA를 사용하면서 JDBC 커넥션을 직접 사용하거나, 스프링 JdbcTemplate, 마이바티스등을 함께 사용 가능
- 단 영속성 컨텍스트를 적절한 시점에 강제로 플러시 필요
- 예) JPA를 우회해서 SQL을 실행하기 직전에 영속성 컨텍스트 수동 플러시
Member member = new Member();
member.setUsername("member1");
em.persist(member);
// 영속성 컨텍스트에는 있으나 db에 안 들어가 있음.
// flush는 commit, query 날아갈 때 호출 됨.
// jpa와 관련된 기술을 사용할 때는 flush 되고 쿼리 실행 됨.
List<Member> resultList = em.createNativeQuery("select MEMBER_ID, city, street, zipcode, USERNAME from MEMBER", Member.class)
.getResultList();
// 이 때는 쿼리 실행 안 됨.
dbconn.executeQuery("select * from member");
// flush 수동으로 해줘야 함.
em.flush();
for (Member member1 : resultList) {
System.out.println("member1 : " + member1);
}
tx.commit();
JPQL 기본 문법과 쿼리 API
- select m from Member as m where m.age > 18
- 엔티티와 속성은 대소문자 구분O (Member, age)
- JPQL 키워드는 대소문자 구분X (SELECT, FROM, where)
- 엔티티 이름 사용, 테이블 이름 아님!
- 별칭은 필수(m), as는 생략 가능
jpql 프로젝트 생성
Member 클래스
package jpql;
import javax.persistence.*;
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String username;
private int age;
@ManyToOne()
@JoinColumn(name = "TEAM_ID")
private Team team;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Team 클래스
package jpql;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import java.util.ArrayList;
import java.util.List;
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Address 클래스
package jpql;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
@Embeddable
public class Address {
private String city;
private String street;
private String zipcode;
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getZipcode() {
return zipcode;
}
public void setZipcode(String zipcode) {
this.zipcode = zipcode;
}
}
Product 클래스
package jpql;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Product {
@Id @GeneratedValue
private Long id;
private String name;
private int price;
private int stockAmount;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public int getStockAmount() {
return stockAmount;
}
public void setStockAmount(int stockAmount) {
this.stockAmount = stockAmount;
}
}
Order 클래스
package jpql;
import javax.persistence.*;
@Entity
@Table(name = "ORDERS")
public class Order {
@Id @GeneratedValue
private Long id;
private int orderAmount;
@Embedded
private Address address;
@ManyToOne
@JoinColumn(name = "PRODUCT_ID")
private Product product;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public int getOrderAmount() {
return orderAmount;
}
public void setOrderAmount(int orderAmount) {
this.orderAmount = orderAmount;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
}
집합과 정렬
select
COUNT(m), // 회원 수
SUM(m.age), // 나이 합
AVG(m.age), // 최대 나이
MIN(m.age) // 최소 나이
from Member m
TypeQuery, Query
- TypeQuery : 반환 타입이 명확할 때 사용
- Query : 반환 타입이 명확하지 않을 때 사용
// TypedQuery
TypedQuery<Member> query1 = em.createQuery("select m from Member m", Member.class);
TypedQuery<String> query2 = em.createQuery("select m from Member m", String.class);
// Query
Query query3 = em.createQuery("select m.username, m.age from Member m");
결과 조회 API
- query.getResultList() : 결과가 하나 이상일 때, 리스트 반환.
- 결과 없으면 빈 리스트 반환 (NullPointException 발생 X)
- query.getSingleResult() : 결과가 정확히 하나, 단일 객체 반환.
- 결과 없으면 NoResultException
- 둘 이상이면 NonUniqueResultException
// 값이 여러 개 일 때
List<Member> resultList = query.getResultList();
// 값이 하나 일 때
Member result = query.getSingleResult();
파라미터 바인딩
이름 기준
Member result = em.createQuery("select m from Member m where m.username = : username", Member.class)
.setParameter("username","member1") // username에 member1이라는 파라미터가 바인딩 됨.
.getSingleResult();
System.out.println("result : " + result);
위치 기준 (안 쓰는게 좋음)
Member result = em.createQuery("select m from Member m where m.username = : ?1", Member.class)
.setParameter(1, "member1")
.getSingleResult();
System.out.println("result : " + result);
'코딩 > JPA' 카테고리의 다른 글
[JPA] 객체지향 쿼리 언어(JPQL) - 중급 문법 (0) | 2024.02.18 |
---|---|
[JPA] 객체지향 쿼리 언어(JPQL) - 기본 문법(2) (0) | 2024.02.05 |
[JPA] 값 타입 (0) | 2023.12.07 |
[JPA] 프록시와 연관관계 관리 (0) | 2023.12.07 |
[JPA] 상속관계 매핑 (0) | 2023.12.04 |