4 minute read

JPQL은 아직도 오류가 제일 많이 나는 부분중에 하나이다…

뭔가 mysql 같으면서도 join이 없는 더 편한 부분도 있지만 limit가 안되고 count하기가 복잡한 …

그런 것때문에 mybatis 가고싶은적이 천만번,,,

1) JPQL

JPQL 문법은 SQL문법과 유사하다.

ex)

SELECT s
FROM Student s
WHERE s.department.id = 1

-> Student 엔터티 클래스에는 departmentId 멤버 변수가 없고, department 멤버 변수가 있다.

SELECT s
FROM Student s
WHERE s.department.id IN (1, 2)
ORDER BY name DESC
SELECT s
FROM Student s
WHERE s.name LIKE '김%'

2) 조인 방법 - single valued association

  • SQL 학생 목록을 조회할 때, 조회 결과에 학과명을 포함하려면 Student 테이블과 Department 테이블을 조인해야 한다. 예를들어, 학번, 이름, 학과명 목록을 조회하는 SQL은
    SELECT s.studentNo, s.name, d.name
    FROM Student s LEFT JOIN Department d ON s.departmentId = d.id
    
  • JPQL Student 엔터티 클래스에는 department 멤버 변수가 포함되어 있다.
    Department department;
    

    그래서 JPQL 언어로 구현할 때는 Department와 조인을 구현할 필요가 없다.

    SELECT s.studentNo, s.name, s.department.name
    FROM Student s
    

3) 조인 방법 - collection valued association

  • SQL

‘200032010’ 학번 학생이 수강(registration)하고 있는 강좌(course) 이름을 조회하는 SQL 명령

SELECT c.name
FROM Student s 
  JOIN Registration r ON s.id = r.studentId
  JOIN Course c ON r.courseId = c.id
WHERE s.studentNo = '200032010'
  • JPQL

Student 엔터티 클래스에는 registrations 멤버 변수가 포함되어 있다.

List<Registration> registrations;

Registration 엔터티 클래스에는 course 멤버 변수가 포함되어 있다

Course course;

‘200032010’ 학번 학생이 수강(registration)하고 있는 강좌(course) 이름을 조회하는 JPQL 명령은 다음과 같다.

SELECT r.course.name
FROM Student s JOIN s.registrations r
WHERE s.studentNo = '200032010'

SELECT r.course.name
FROM Registration r
WHERE r.student.studentNo = '200032010'

4) JPQL 예제

SELECT s
FROM Department d JOIN d.students s
WHERE d.id = 1

SELECT s.studentNo, s.name, d.name
FROM Department d JOIN d.students s
WHERE d.id = 1

SELECT r.course.name
FROM Student s JOIN s.registrations r
WHERE s.id = 1

SELECT s
FROM Student s
WHERE SIZE(s.registrations) > 5  //수강 강좌의 수가 5 초괴하는 학생목록

SELECT s
FROM Student s
WHERE SIZE(s.registrations) = 0  //s.registrations IS EMPTY

SELECT s
FROM Student s
WHERE EXISTS (SELECT r.course
              FROM s.registrations r
              WHERE r.course.professor.name = '김국어') //'김국어' 교수의 과목을 하나라도 수강하는 학생

5) Group by

학과별 학생 
SELECT s.department, COUNT(s)
FROM Student s
GROUP BY s.department

강좌별 학생 
SELECT r.course.name, COUNT(r.student)
FROM Registration r
GROUP BY r.course

6) JPQL 함수

JPQL 명령에서 사용할 수 있는 함수

ABS(number)		절대값을 리턴한다

CONCAT(s1, s2)		 문자열을 연결하여 리턴한다

CURRENT_DATE()		현재 날짜를 리턴

CURRENT_TIME()		현재 시각을 리턴

CURRENT_TIMESTAMP()	현재 날짜 시각을 리턴

INDEX(identification variable)	identification variable 참조하는 엔터티의, 컬렉션(collection)에서 인덱스를 리턴

LENGTH(string)		문자열 길이를 리턴

LOCATE(s1, s2 [, start]) s2 문자열에서 s1 문자열의 위치를 리턴한다. 
			start 값이 주어졌으면 start 위치부터 찾기 시작한다. 
			start1 문자열이 start2 문자열의 시작 부분과 일치하면 1 리턴한다. 			
			일치하는 부분이 없으면 0 리턴한다.

LOWER(string)	문자열을 소문자로 변환하여 리턴한다.

MOD(number1, number2)	number1 number2 나눈 나머지를 리턴한다.

SIZE(collection)	컬렉션(collection) 크기를 리턴한다. 컬렉션에 포함된 객체의 수를 리턴.

SQRT(number)	제곱근을 리턴한다.

SUBSTRING(string, start, end)	부분 문자열을 리턴한다.

UPPER(string)	문자열을 대문자로 변환하여 리턴한다.

TRIM(LEADING FROM string)	문자열  부분의 공백을 제거하고 리턴한다.

TRIM(TRAILING FROM string)	문자열  부분의 공백을 제거하고 리턴한다.

TRIM(BOTH FROM string)	문자열  뒤의 공백을 제거하고 리턴한다.

TRIM(string)	문자열  뒤의 공백을 제거하고 리턴한다.

7) UPDEATE / DELETE

SELECT 뿐만 아니라, UPDATE, DELETE 명령도 구현할 수 있다. JPA Repository의 기본 메소드 save 가 충분하기 때문에, INSERT 명령은 필요 없다. 그래서 JPQL 에 INSERT 명령은 없다.

JPA repository 메소드 이름이 정해진 규칙에 맞으면, 메소드를 자동으로 구현해주는 query create 기능이 충분하기 때문에, DELETE 명령을 사용할 일도 거의 없다. JPQL query를 직접 구현해야 하는 경우는, 대부분 SELECT, UPDATE 명령이다. 레코드의 필드 몇 개만 업데이트 하면 될 때, JPA Repository의 기본 메소드 save 는 불편하다.

save 메소드는 업데이트 할 때, 기본 키를 제외하고 모든 필드 값을 전부 저장하기 때문이다. 이런 때 JPQL UPDATE 명령을 사용한다. WHERE 절의 조건에 일치하는 레코드들을 일괄 업데이트 할 때도 JPQL UPDATE 명령이 편리하다.

  • Query creation Method의 Example★

이 메소드들의 이름이 JPA query creation 규칙에 맞기 때문에, 메소드가 자동으로 구현된다.

void delete(int id);
void deleteByName(String name);
void deleteByStudentNo(String studentNo);
void deleteByNameOrStudentNo(String name, String studentNo);
void deleteByDepartmentName(String departmentName);
void deleteByDepartmentId(int departmentId);

8) 실제 예제

  • find 메소드를 호출하면 @Query(…) 어노테이션의 JPQL 명령이 실행되고 조회 결과가 리턴된다.
     @Query("SELECT s FROM Student s ORDER BY name")
     List<Student> findStudents();
    
  • JPQL에서 ?1 ?2 부분은 각각 메소드 첫째 파라미터와 둘째 파라미터를 의미한다. studentNo 파라미터 값이 ?1부분에, name 파라미터 값이 ?2 부분에 삽입되어 실행된다.
    @Query("SELECT s FROM Student s WHERE s.department.id = ?1")
    List<Student> findStudentsByDepartmentId(int departmentId);
    @Query("SELECT s FROM Student s WHERE s.studentNo = ?1 OR s.name = ?2")
    List<Student> findStudentsByStudentNoOrName(String studentNo, String name);
  • JPQL에서 :studentNo :name 부분에 파라미터 값이 삽입되어 실행된다. studentNo 파라미터 값이 :studentNo 부분에, name 파라미터 값이 :name 부분에 삽입되어 실행된다.
    @Query("SELECT s FROM Student s WHERE s.studentNo = :studentNo OR s.name = :name")
    List<Student> findStudentsByStudentNoOrName2(@Param("studentNo") String studentNo, @Param("name") String name);
  • UPDATE / DELETE 명령인 경우에 @Modifying(clearAutomatically = true) 어노테이션을 반드시 붙여야 한다.
     @Modifying(clearAutomatically = true)
     @Query("UPDATE Student s SET name = :name WHERE id = :id")
     void updateStudentName(@Param("id") int id, @Param("name") String name);
    }
    

초반에는 jpq query method로도 가능한 것을 사용할줄 몰라서 jpql로 사용했던 기억이 난다

사람은 적응의 동물인가,,, 벌써 저게 좀 적응.. 된것같지는 않고 익숙해진건가ㅎㅅㅎ

초밥먹고싶은 새벽 2시 50분….

Categories:

Updated:

Comments