//TypeqQuery TypeQuery<Member> query = em.createQuery("SELECT m FROM Member m", Member.class); List<Member> resultList = query.getResultList(); //Query Query qeury = em.createQuery("SELECT m.username, m.age From Member m"); List resultList = query.getReusltList();
- 결과 조회
query.getResultList() : 결과를 예제로 반환한다. 만약 결과가 없으면 빈 컬렉션을 반환.
query.getSingleReulst() : 결과가 정확히 하나일때 사용한다.
결과가 없으면 javax.persistence.NoResultException 예외가 발생한다.
결과과 1 개보다 많으면 javax.persistence.NonUniqueResultException
예외가 발생한다.
getSingleResult ()는 결과가 정확히 1 개가 아니면 예외가 발생한다는 점에 주
의해야 한다
@Entitiy(name="Member") public class Member{ @Column(name = "name") private String username; }
//JPQL사용 String jpql = "select m from member as m where m.username= "kim"; List<Member> resultList = em.createQuery(jpql, Member.class).gtResultList(): 회원이름이 kim인 엔티티를 조회한다 m.username은 테이블 컬럼명이 아니라 엔티티 객체의 필드명이다.
// 이름 기준 파라미터 사용 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();
위치 기준 파라미터 사용
위치 기준 파라미터를 사용하려면 ? 다음에 위치 값을 주면 된다. 위치 값은 1부터 시작한다.
List<Member> members = em.createQuery("SELECT m FROM Member m where m.username = ?1", Member.class) .setParameter(1, usernameParam) .getResultList();
위치 기준 파라미터 방식보다는 이름 기준 파라미터 바인딩 방식을 사용하는것이 더 명확하다.
JPQL을 수정해서 다음 코드처럼 파라미터 바인딩 방식을 사용하지 않고 직접 문자를 더해 만들어 넣으면 악의적인 사용자에 의해 SQL 인젝션 공격을 당할수 있다.
파라미터 바인딩 방식을 사용하면 파라미터의 값이 달라도 같은 쿼리로 인식해서 JPQ는 JPQL을 SQL로 파싱한 결과를 재사용 가능해서 성능이 좋아진다.
파라미터 바인딩 방식은 선택이 아닌 필수다.
SELECT m FROM Member m //회원 SELECT m.team FROM Member m //팀
둘 다 엔티티를 프로젝션 대상으로 사용했다.조회한 엔티티는 영속성 컨텍스트에서 관리된다.
임베디드 타입은 엔티티와 거의 비슷하게 사용된다.
임베디드 타입은 조회의 시작점이 될 수 없다는 제약이 있다.
String query = "SELECT a FROM Address a"; 잘못된 쿼리 String query = "SELECT o.address FROM Order o"; List<Address> addresses = em.createQuery(query, Address.class).getResultList(); 임베디드 타입인 Address를 시작점으로 사용하면 안되기 떄문에 Order 엔티티를 시작점으로 해서 조회하였다.
임베디드 타입은 엔티티 타입이 아닌 값 타입이기에 이렇게 조회 된 경우에는 영속성 컨텍스트에 관리 되지 않는다.
숫자, 문자, 날짜와 같은 기본 데이터 타입들을 스칼라 타입이라 한다.
전체 회원의 이름을 조회 할땐 다음처럼 쿼리하면 된다.
List<String> usernames = em.createQuery("SELECT username FROM Member m", String.class) .getResultList();
중복데이터를 제거하려면 DISTINCT를 사용한다.
SELECT DISTINCT username FROM Member m
다음과 같은 통계 쿼리도 주로 스칼라 타입을 조회한다. 통계 쿼리용 함수들은 뒤에서 설명하겠다.
Double orderAmountAvg = em.createQuery("SELECT AVG(o.orderAmount) FROM Order o", Double.class) .getSingResult();
꼭 필요한 데이터만 조회해야 할때
프로젝션에 여러 값을 선택하면 TypeQuery를 사용할 수 없고 대신에 Query를 사용해야 한다.
Query query = em.createQuery("SELECT m.username, m.age FROM Member m"); List resultList = query.getResultList(); Iterator iterator = resultList.iterator(); while(iterator.hasNext()){ Object[] row = (Object[]) iterator.next(); String username = (String) row[0]; Integer age = (Integer) row[1]; }
스칼라 타입 뿐만 아니라 엔티티 타입도 여러값을 함께 조회할 수 있다.
List<Object[]> resultList = em.createQuery("SELECT o.member, o.prodcut, o.orderAmount FROM Order o") .getResultList(); for(Object[] row:resultList){ Member member = (Member) row[0]; //엔티티 Product product = (Prodcut) row[1]; //엔티티 int OrderAmount = (Integer) row[2]; }
실제 개발자는 UserDTO 처럼 의미있는 객체로 변환해서 사용할 것이다.
TypeQuery<UserDTO> query = em.createQuery("SELECT new jpabook.jqpl(UserDTO(m.username, m.age) FROM Member m", UserDTO.class); List<UserDTO> resultList = query.getResultList();
NEW 명령어를 사용한 클래스로 TypeQuery를 사용할 수 있어서 지루한 객체 변환 작업을 줄일 수 있다.
NEW 명령어 사용할 때는 다음 2가지를 주의해야 한다.
1. 패키지 명을 포함한 전체 클래스 명을 입력해야 한다
2. 순서와 타입이 일치하는 생성자가 필요하다.
- setFirstResult(int startPosition) : 조회 시작위치(0부터 시작된다)
- setMaxResult(int maxResult) 조회할 데이터수
더 최적화 하고 싶다면 JPA가 제공하는 페이징 API가 아닌 네이티브 SQL을 직접 사용해야 한다.TypeQuery<Member> query = em.createQuery("SELECT m FROM Member m ORDER BY m.username DESC", Member.class); query.setFirstResult(10); query.setMaxResults(20); query.getResultList();
집합은 집합 함수와 함계 통계정보를 구할때 사용된다.
예를 들어 다음 코드는 순서대로 회원수, 나이 합, 평균 나이, 최대 나이, 최소 나이를 조회한다.
select COUNT (m) z //회원수 SUM(m.age), 〃나이 합 AVG(m.age), //평균 나이 MAX (m.age), //최대 나이 MIN (m age) //최소 나이 from Member m
집합 함수의 종류와 그 리턴값을 정리한 내용은 아래와 같다
COUNT : 결과 수를 구한다. 반환 타입은 LONG
MAX, MIN : 최대, 최소 값을 구한다. 문ㅈ, 숫자, 날짜 등에 사용한다.
AVG : 평균값을 구한다. 숫자타입만 사용할 수 있다. 반환 타입은 DOUBLE
SUM : 합을 구한다. 숫자 타입만 사용이 가능하다. 반환 타입 : 정수의 합(LONG), 소수의 합(DOUBLE)
집합 함수 사용 시 참고사항
NULL 값은 무시하므로 통계에 잡히지 않는다(D1STエNCT가 정의되어 있어도 무시된다).
만약 값이 없는데 SUM, AVG, MAX, MIN 함수를 사용하면 NULL 값이 된다. 단
COUNT는 0이 된다.
DISTINCT 를 집합 함수 안에 사용해서 중복된 값을 제거하고 나서 집합을 구할
수 있다.
예 select COUNT ( DISTINCT m.age ) from Member m
DISTINCT를 COUNT에서 사용할 때 임베디드 타입은 지원하지 않는다
참고 : 자바 ORM 표준 jpa 프로그래밍