Programming/JPA

JPA - 기본 문법과 기능 (기본 문법, 프로젝션, 페이징)

잇(IT) 2023. 7. 18. 11:42

- JPA는 다양한 쿼리 방법을 지원한다.

1. JPQL

2. QueryDSL

3. JDBC API 직접 사용, MyBaits, SpringJdbcTemplate 함께 사용

 

- JPQL

1. JPA를 사용하면 엔티티 객체를 중심으로 개발한다.

2. 검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색한다.

3. JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 제공한다.

4. SQL과 문법이 유사하다. SELECT, FROM, WHERE GROUP BY, HAVING, JOIN 지원

5. JPQL은 엔티티 객체를 대상으로 쿼리

6. SQL은 데이터베이스 테이블을 대상으로 쿼리

//검색
 String jpql = "select m From Member m where m.name like ‘%hello%'";
 
 List<Member> result = em.createQuery(jpql, Member.class)
 	.getResultList();

1. 테이블이 아닌 객체를 대상으로 검색하는 객체 지향 쿼리

2. SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않는다.

3. JPQL는 즉, 객체 지향 SQL이다.

 //검색
 String jpql = "select m from Member m where m.age > 18";
 List<Member> result = em.createQuery(jpql, Member.class)
 .getResultList();

- JDBC 직접 사용, SpringJdbcTemplate 등

1. JPA를 사용하면서 JDBC 커넥션을 직접 사용하거나, 스프링 JdbcTemplate, 마이바티스등을 함께 사용 가능하다.

2. 단, 영속성 컨텍스트를 적절한 시점에 강제로 플러시가 필요하다. JDBC, 마이바티스 등은 JPA가 아니다. JPA의 데이터는 flush가 되어야 DB에 데이터가 저장되기 때문에 강제 flush()를 해줘야 한다.


- JPQL - 기본 문법과 기능

1. JPQL은 객체지향 쿼리 언어다. 따라서 테이블을 대상으로 쿼리하는 것이 아니라 엔티티 객체를 대상으로 쿼리한다.

2. JPQL은 SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않는다.

3. JPQL은 결국 SQL로 변환한다.

 

- JPQL 문법

select_문 :: = 
 select_절
 from_절
 [where_절]
 [groupby_절]
 [having_절]
 [orderby_절]
update_문 :: = update_절 [where_절]
delete_문 :: = delete_절 [where_절]

1. select m from Member as m where m.age > 18

2. 엔티티와 속성은 대소문자 구분을 한다. (Member, age)

3. JPQL 키워드는 대소문자 구분을 하지 않는다. (SELECT, FROM, where)

4. 엔티티 이름 사용, 테이블 이름이 아니다. (Member)

5. 별칭은 필수(m) (as는 생략 가능하다.)


- 집합과 정렬

select
 COUNT(m), //회원수
 SUM(m.age), //나이 합
 AVG(m.age), //평균 나이
 MAX(m.age), //최대 나이
 MIN(m.age) //최소 나이
from Member m

- GROUP BY, HAVING

- ORDER BY


- TypeQuery, Query

1. TypeQuery: 반환 타입이 명확할 때 사용

2. Query: 반환 타입이 명확하지 않을 때 사용

TypedQuery<String> query2 = em.createQuery("select m.username from Member m", String.class);
Query query3 = em.createQuery("select m.username, m.age from Member m", String.class);

 

- 결과 조회 API

1. query.getResultList(): 결과가 하나 이상일 때, 리스트 반환

1.1. 결과가 없으면 빈 리스트 반환

2. query.getSingleResult(): 결과가 정확히 하나, 단일 객체 반환

2.1. 결과가 없으면: javax.persistence.NoResultException

2.2. 둘 이상이면: javax.persistence.NonUniqueResultException

 

- 파라미터 바인딩 - 이름 기준, 위치 기준

SELECT m FROM Member m where m.username=:username 

query.setParameter("username", usernameParam);

- 위치 기준은 권장하지 않는다.

 

Member member = new Member();
            member.setUsername("member1");
            member.setAge(10);
            em.persist(member);

            Member result = em.createQuery("select m from Member m where m.username = :username", Member.class)
                    .setParameter("username", "member1")
                    .getSingleResult();
            System.out.println("result = " + result.getUsername());

            // chain으로 묶어서 깔끔하게 사용한다.
//            TypedQuery<Member> query1 = em.createQuery("select m from Member m where m.username = :username", Member.class);
//            query1.setParameter("username", "member1");
//            Member result = query1.getSingleResult();
//            System.out.println("result = " + result.getUsername());

            // 하나만 가져올 때, 진짜 결과가 하나일 때만 사용해야 한다.
//            Member result = query1.getSingleResult();
//            System.out.println("result = " + result);

            // 리스트로 여러개를 가져올 때
//            List<Member> resultList = query1.getResultList();
//            for (Member member1 : resultList) {
//                System.out.println("member1 = " + member1);
//            }

- 프로잭션

1. SELECT 절에 조회할 대상을 지정하는 것

2. 프로젝션 대상 : 엔티티, 임베디드 타입, 스칼라 타입(숫자, 문자 등 기본 데이터 타입)

3. SELECT m FROM Member m -> 엔티티 프로젝션

4. SELECT m.team FROM Member m -> 엔티티 프로젝션

5. SELECT m.address FROM Member m -> 임베디드 타입 프로젝션

6. SELECT m.username, m.age FROM member m -> 스칼라 타입 프로젝션

7. DISTINCT로 중복 제거

 

- 엔티티 프로젝션

List<Member> result = em.createQuery("select m from Member m", Member.class)
	.getResultList();

Member findMember = result.get(0);
findMember.setAge(20);

- 엔티티 프로젝션 2

List<Team> result = em.createQuery("select m.team from Member m join m.team t", Team.class)
	.getResultList();

- 엔티티와 연관된 다른 엔티티를 조회 할때는 실제 SQL과 유사하게 작성하는 것이 좋다.

 

- 임베디드 타입 프로젝션

em.createQuery("select o.address from Order o", Address.class)
	.getResultList();

- 여러 값 조회, 스칼라 타입 프로젝션

 

- MemberDTO

public class MemberDTO {

    private String username;
    private int age;

    public MemberDTO(String username, int age) {
        this.username = username;
        this.age = age;
    }

 

List<MemberDTO> result = em.createQuery("select new jpql.MemberDTO(m.username, m.age) from Member m", MemberDTO.class)
	.getResultList();

MemberDTO memberDTO = result.get(0);
System.out.println("memberDTO.getUsername() = " + memberDTO.getUsername());
System.out.println("memberDTO.getAge() = " + memberDTO.getAge());

- 여러 값 조회 할 때는 엔티티에 대한 DTO를 생성하여 작성하는 것이 좋다.


- 페이징

 

- JPA는 페이징을 다음 두 API로 추상화 한다.

1. setFirstResult(int startPosition) : 조회 시작 위치 (0부터 시작)

2. setMaxResults(int maxResult) : 조회할 데이터 수

 

for (int i = 0; i < 100; i++) {
                Member member = new Member();
                member.setUsername("member" + i);
                member.setAge(i);
                em.persist(member);
            }

            em.flush();
            em.clear();

            List<Member> result = em.createQuery("select m from Member m order by m.age desc", Member.class)
                    .setFirstResult(1)
                    .setMaxResults(10)
                    .getResultList();

            System.out.println("result.size() = " + result.size());
            for (Member member1 : result) {
                System.out.println("member1 = " + member1);
            }

728x90