조인
내부 조인 (INNER JOIN)
SQL INNER JOIN과 동일하며 INNER는 생략 가능하다.
// 내부조인 (inner 생략 가능)
String query = "select m from Member m inner join m.team t";
List<Member> result = em.createQuery(query, Member.class).getResultList();
외부 조인 (LEFT OUTER JOIN)
SQL LEFT OUTER JOIN과 동일하며 OUTER는 생략 가능하다.
// 외부조인 (outer 생략 가능)
String query = "select m from Member m left outer join m.team t";
List<Member> result = em.createQuery(query, Member.class).getResultList();
세타 조인
연관관계가 전혀 없는 조인으로 WHERE절을 사용하여 조인한다. (INNER JOIN 방식)
// 세타조인 (크로스 조인, where 절)
String query = "select m from Member m, Team t where m.username = t.name";
List<Member> result = em.createQuery(query, Member.class).getResultList();
ON절
조인 대상 필터링
ON절을 사용하여 조인 대상을 필터링 할 수 있다.
// 조인 대상 필터링
String query = "select m from Member m left join m.team t on t.name = 'teamA'";
List<Member> result = em.createQuery(query, Member.class).getResultList();
연관관계 없는 엔티티 외부 조인
ON절을 사용하여 연관관계가 없는 엔티티를 조인할 수 있다.
// 연관관계 없는 외부조인
String query = "select m from Member m left join Team t on m.username = t.name";
List<Member> result = em.createQuery(query, Member.class).getResultList();
서브쿼리
지원 함수
EXISTS / NOT EXISTS
서브쿼리 결과가 존재하면 참
// EXISTS
String query = "select m from Member m where exists (select t from m.team t where t.name = 'teamA')";
List<Member> result = em.createQuery(query, Member.class).getResultList();
IN / NOT IN
서브쿼리 결과 중 하나라도 같은 것이 있으면 참
ALL
조건을 모두 만족하면 참
// ALL
String query = "select o from Order o where o.orderAmount > ALL (select p.stockAmount from Product p)";
List<Order> result = em.createQuery(query, Order.class).getResultList();
ANY / SOME
조건을 하나라도 만족하면 참
// ANY
String query = "select m from Member m where m.team = ANY (select t from Team t)";
List<Member> result = em.createQuery(query, Member.class).getResultList();
JPA 서브쿼리 한계
- JPA는 WHERE, HAVING절에서만 서브쿼리를 사용 가능하고 하이버네이트는 SELECT절에서도 사용 가능하다.
- FROM절의 서브쿼리는 현재 JPQL에서 불가능하다.
- FROM절의 서브쿼리는 조인으로 풀어서 해결하거나 조인이 불가능하면 Native SQL을 사용 또는 쿼리를 분해해서 실행하여 결과를 애플리케이션에서 조립하는 방법을 사용한다.
JPQL 타입 표현과 기타식
타입 표현
- 문자, 숫자, Boolean형은 SQL과 동일하게 사용
- ENUM 타입의 경우 보통 파라미터 바인딩으로 사용하지만 쿼리에 작성해야 한다면 패키지명까지 작성해야 한다.
- 엔티티 타입의 경우
TYPE(부모 엔티티) = 자식 엔티티
방식으로 사용한다. (상속 관계에서만 사용)
예제 (기본 타입 + ENUM)
기본타입 및 ENUM 타입 예제로 회원 유형이 관리자인 데이터 조회
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String username;
private int age;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "TEAM_ID")
private Team team;
// ENUM 타입
@Enumerated(EnumType.STRING)
private MemberType type;
// getter, setter, toString
}
public enum MemberType {
ADMIN, USER
}
Team team = new Team();
team.setName("teamA");
em.persist(team);
Member member = new Member();
member.setUsername("member1");
member.setAge(10);
member.setTeam(team);
member.setType(MemberType.ADMIN); // JPQL 타입 표현
em.persist(member);
em.flush();
em.clear();
// 타입 표현 (기본타입 + ENUM)
String query = "select m.username, 'HELLO', true from Member m where m.type = :userType";
List<Object[]> result = em.createQuery(query).setParameter("userType", MemberType.ADMIN).getResultList();
for(Object[] objects : result) {
System.out.println("object[0] = " + objects[0]);
System.out.println("object[1] = " + objects[1]);
System.out.println("object[2] = " + objects[2]);
}
예제 (기타식)
기타식 예제로 나이가 0 ~ 10세인 데이터 조회
// 기타식(between)
String query = "select m.username, 'HELLO', true from Member m where m.age between 0 and 10";
List<Object[]> result = em.createQuery(query).getResultList();
for(Object[] objects : result) {
System.out.println("object[0] = " + objects[0]);
System.out.println("object[1] = " + objects[1]);
System.out.println("object[2] = " + objects[2]);
}
예제 (엔티티 타입)
엔티티 타입 표현 예제로 이전 실습 패키지를 사용하여 DTYPE = 'Book'
인 데이터 조회
// jpashop package
Book book = new Book();
book.setName("JPA");
book.setAuthor("jsk");
em.persist(book);
// 엔티티 타입
em.createQuery("select i from Item i where type(i) = Book", Item.class).getResultList();
조건식
CASE
기본 SQL 문법과 동일하게 사용
// CASE
String query = "select" +
" case when m.age <= 10 then '학생요금' " +
" when m.age >= 60 then '경로요금' " +
" else '일반요금' " +
" end " +
"from Member m";
List<String> result = em.createQuery(query).getResultList();
for(String s : result) {
System.out.println("s : " + s);
}
COALESCE
하나씩 조회해서 NULL이 아니면 반환
Member member = new Member();
member.setUsername(null); // coalesce
member.setAge(10);
em.persist(member);
em.flush();
em.clear();
// COALESCE
String query = "select coalesce(m.username, '이름 없는 회원') from Member m";
List<String> result = em.createQuery(query).getResultList();
for(String s : result) {
System.out.println("s : " + s);
}
NULLIF
두 값이 같으면 NULL 반환, 다르면 첫번째 값 반환
Member member = new Member();
member.setUsername("관리자"); // nullif
member.setAge(10);
em.persist(member);
em.flush();
em.clear();
// NULLIF
String query = "select nullif(m.username, '관리자') from Member m";
List<String> result = em.createQuery(query).getResultList();
for(String s : result) {
System.out.println("s : " + s);
}
JPQL 함수
기본함수
JPA가 제공하는 표준 함수로 데이터베이스에 상관 없이 사용 가능하다.
CONCAT
문자열 더하기
// CONCAT
String query = "select concat('a', 'b') from Member m";
List<String> result = em.createQuery(query).getResultList();
for(String s : result) {
System.out.println("s : " + s);
}
SUBSTRING
문자열 자르기
// SUBSTRING
String query = "select substring('abc', 2, 3) from Member m";
List<String> result = em.createQuery(query).getResultList();
for(String s : result) {
System.out.println("s : " + s);
}
TRIM
공백 제거
LOWER, UPPER
대/소문자 변경
LENGTH
문자열 길이 측정
LOCATE
특정 문자 시작 위치 찾기
// LOCATE
String query = "select locate('ef', 'abcdefg') from Member m";
List<Integer> result = em.createQuery(query).getResultList();
for(Integer i : result) {
System.out.println("i : " + i);
}
ABS, SQRT, MOD
절대값, 제곱근, 나머지 구하기
SIZE
컬렉션 크기
// SIZE
String query = "select size(t.members) from Team t";
List<Integer> result = em.createQuery(query).getResultList();
for(Integer i : result) {
System.out.println("i : " + i);
}
INDEX
값 타입 컬렉션에서 위치값 찾기는 함수. 중간에 리스트에서 값 빠질경우 NULL로 들어오기 때문에 거의 사용하지 않는다.
사용자 정의 함수
사용자가 구성한 함수로 해당 함수를 만들고 사용하기 위해서는 다음과 같이 설정해야 한다.
- 사용하는 데이터베이스 방언 클래스를 상속받는 클래스를 생성한다.
- 해당 클래스에 사용자 정의 함수를 구성한다.
persistence.xml
내 데이터베이스 dialect 옵션을 해당 클래스로 지정한다.- SQL에서 사용자 정의 함수를 이용한다.
예제
- MyH2Dialect.java
public class MyH2Dialect extends H2Dialect { // 사용하는 DB 방언 상속
// 사용자 정의 함수 구성
public MyH2Dialect() {
registerFunction("group_concat", new StandardSQLFunction("group_concat", StandardBasicTypes.STRING));
}
}
- persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2" xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
<persistence-unit name="hello">
<properties>
<!-- 필수 속성 -->
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
<property name="javax.persistence.jdbc.user" value="sa" />
<property name="javax.persistence.jdbc.password" value="" />
<property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test" />
<property name="hibernate.dialect" value="dialect.MyH2Dialect" /> <!-- 사용자 정의 함수 -->
<!-- 옵션 -->
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.use_sql_comments" value="true" />
<property name="hibernate.jdbc.batch_size" value="10" />
<property name="hibernate.hbm2ddl.auto" value="create" />
</properties>
</persistence-unit>
</persistence>
- JpaMain.java
Member member = new Member();
member.setUsername("관리자");
member.setAge(10);
em.persist(member);
Member member2 = new Member();
member2.setUsername("관리자2");
member2.setAge(11);
em.persist(member2);
em.flush();
em.clear();
// 사용자 정의 함수
String query = "select group_concat(m.username) from Member m";
List<String> result = em.createQuery(query).getResultList();
for(String s : result) {
System.out.println("s : " + s);
}
'Dev > JPA' 카테고리의 다른 글
[JPA] 객체지향 쿼리 문법 - 중급문법(2) (0) | 2021.09.11 |
---|---|
[JPA] 객체지향 쿼리 언어 - 중급문법 (1) (0) | 2021.09.10 |
[JPA] 객체지향 쿼리 언어 - 기본 문법 (1) (0) | 2021.09.07 |
[JPA] 실습 - 값 타입 매핑 (0) | 2021.09.02 |
[JPA] 값 타입 (0) | 2021.09.01 |
댓글