JPA 쿼리 메서드(Query Method)
Spring Data JPA는 Repository 인터페이스에 선언된 메소드를 그 이름에서 쿼리를 생성하고 자동으로 생성 해주는 것이 편리한 기능이 있다. 어떤 명명 규칙이 있는지에 대해서 알아보자.
구문
다음의 3가지 요소를 규칙에 따라 결합된 메소드 이름을 Repository 인터페이스에 선언하면 자동 구현이 가능하다.
- 접두사 (
find ... By
,read ... By
,query ... By
,count ... By
,get ... By
) - 키워드
- 필드 이름
다음와 같은 엔티티를 예로 사용한다.
Employee.java
@Entity
public class Employee implements Serializable {
private static final long serialVersionUID = 3453583737318640866L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String firstname;
private String lastname;
private int age;
@Temporal(TemporalType.DATE)
private Date hiredAt;
private Boolean active;
@ManyToOne
@JoinTable(
name="department_employee",
joinColumns=@JoinColumn(name="employee_id"),
inverseJoinColumns=@JoinColumn(name="department_code")
)
private Department department;
// 이하 Getter/Setter...
Department.java
@Entity
public class Department implements Serializable {
private static final long serialVersionUID = -6771704436232452390L;
@Id
private String code;
private String name;
private boolean flg;
@OneToMany(fetch=FetchType.LAZY, mappedBy="department")
private transient List<Employee> employeeList;
// 이하 Getter/Setter...
메소드 생성 키워드
Keyword | Sample | JPQL snippet |
---|---|---|
Distinct |
findDistinctByLastnameAndFirstname |
select distinct ... where x.lastname = ?1 and x.firstname = ?2 |
And |
findByLastnameAndFirstname |
... where x.lastname = ?1 and x.firstname = ?2 |
Or |
findByLastnameOrFirstname |
... where x.lastname = ?1 or x.firstname = ?2 |
Is、Equals |
findByFirstname , findByFirstnameIs , findByFirstnameEquals |
... where x.firstname = ?1 |
Between |
findByStartDateBetween |
... where x.startDate between ?1 and ?2 |
LessThan |
findByAgeLessThan |
... where x.age < ?1 |
LessThanEqual |
findByAgeLessThanEqual |
... where x.age <= ?1 |
GreaterThan |
findByAgeGreaterThan |
... where x.age > ?1 |
GreaterThanEqual |
findByAgeGreaterThanEqual |
... where x.age >= ?1 |
After |
findByStartDateAfter |
... where x.startDate > ?1 |
Before |
findByStartDateBefore |
... where x.startDate < ?1 |
IsNull、Null |
findByAge(Is)Null |
... where x.age is null |
IsNotNull、NotNull |
findByAge(Is)NotNull |
... where x.age not null |
Like |
findByFirstnameLike |
... where x.firstname like ?1 |
NotLike |
findByFirstnameNotLike |
... where x.firstname not like ?1 |
StartingWith |
findByFirstnameStartingWith |
... where x.firstname like ?1 ( % 가 뒤에 추가된 매개 변수) |
EndingWith |
findByFirstnameEndingWith |
... where x.firstname like ?1 ( % 가 앞에 추가된 매개 변수) |
Containing |
findByFirstnameContaining |
... where x.firstname like ?1 ( % 가 래핑된 매개 변수) |
OrderBy |
findByAgeOrderByLastnameDesc |
... where x.age = ?1 order by x.lastname desc |
Not |
findByLastnameNot |
... where x.lastname <> ?1 |
In |
findByAgeIn(Collection<Age> ages) |
... where x.age in ?1 |
NotIn |
findByAgeNotIn(Collection<Age> ages |
... where x.age not in ?1 |
True |
findByActiveTrue() |
... where x.active = true |
False |
findByActiveFalse() |
... where x.active = false |
IgnoreCase |
findByFirstnameIgnoreCase |
... where UPPER(x.firstname) = UPPER(?1) |
예제
Is / Equals / Not
// SELECT e FROM Employee e
Employee findById(Long id);
// SELECT e FROM Employee e WHERE e.firstname = ?1
List<Employee> findByFirstnameEquals(String firstname);
// SELECT e FROM Employee e WHERE e.age = ?1
List<Employee> findByAgeIs(int age);
// SELECT e FROM Employee e WHERE e.lastname != ?1
List<Employee> findByLastnameNot(String lastname);
// SELECT e FROM Employee e WHERE e.department = ?1
List<Employee> findByDepartment(Department department);
필드의 뒤에 Equals
/ Is
를 붙이면 일치, Not
를 넣으면 불일치 레코드를 검색한다. 대소문자를 구분되는지는 데이터베이스의 데이터 설정 등에 따라 달라진다.
LessThan / GreaterThan
등호 조건을 사용하려고 할 때, 사용하는 키워드는 LessThan
, GreaterThan
이다.
// SELECT e FROM Employee e WHERE e.age < ?1
List<Employee> findByAgeLessThan(int age);
// SELECT e FROM Employee e WHERE e.age > ?1
List<Employee> findByAgeGreaterThan(int age);
// SELECT e FROM Employee e WHERE e.hired_at > ?1
List<Employee> findByHiredAtGreaterThan(Date date);
경계 값을 포함하면 위의 키워드 Equal
을 더하여 GreaterThanEqual
, LessThanEqual
으로 하면 된다. 또한, Between
키워드으로도 경계 값을 포함한 범위 검색이 가능하다.
// SELECT e FROM Employee WHERE e.age <= ?1
List<Employee> findByAgeLessThanEqual(int age);
// SELECT e FROM Employee WHERE e.age >= ?1
List<Employee> findByAgeGreaterThanEqual(int age);
// SELECT e FROM Employee WHERE e.hired_at BETWEEN ?1 AND ?2
List<Employee> findByHiredAtBetween(Date since, Date until);
추가로, findByFirstnameLessThan(String name)
과 같이 String 형식의 필드에 LessThan
, GreaterThan
를 붙일 수도 있다. 다만, DBMS 종속되긴 하지만…
Like / NotLike / StartingWith / EndingWith / Containing
와일드 카드를 사용한 부분 일치 검색은Like
, NotLike
, StartingWith
, EndingWith
, Containing
을 사용한다.
// SELECT e FROM Employee WHERE e.firstname LIKE ?1
List<Employee> findByFirstnameLike(int age);
// SELECT e FROM Employee WHERE e.firstname NOT LIKE ?1
List<Employee> findByFirstnameNotLike(String firstname);
// SELECT e FROM Employee WHERE e.lastname LIKE ?1 (앞에 일치)
List<Employee> findByLastnameStartingWith(String lastname);
// SELECT e FROM Employee WHERE e.lastname LIKE ?1 (뒤에 일치)
List<Employee> findByLastnameEndingWith(String lastname);
// SELECT e FROM Employee WHERE e.lastname LIKE ?1 (모두 일치)
List<Employee> findByLastnameContaining(String lastname);
이 중에 StartingWith
, EndingWith
, Containing
내용은 각각 순서대로 전방 일치, 후방 일치, 부분 일치를 의미한다. 임의의 위치에 와일드 카드를 삽입 한 경우 Like
, NotLike
을 사용하다.
Between
// SELECT e FROM Employee e WHERE e.age BETWEEN ?1 AND ?2
List<Employee> findByAgeBetween(int olderThan, int youngerThan);
// SELECT e FROM Employee e WHERE e.hiredAt BETWEEN ?1 AND ?2
List<Employee> findByHiredAtBetween(Date since, Date until);
IsNull / (Is)NotNull
// SELECT e FROM Employee WHERE e.hiredAt IS NULL
List<Employee> findByHiredAtIsNull();
// SELECT e FROM Employee WHERE e.hiredAt IS NOT NULL
List<Employee> findByHiredAtIsNotNull();
// SELECT e FROM Employee WHERE e.hiredAt IS NOT NULL
List<Employee> findByHiredAtNotNull();
True / False
// SELECT e FROM Employee WHERE e.active = true
List<Employee> findByActiveTrue();
// SELECT e FROM Employee WHERE e.active = false
List<Employee> findByActiveFalse();
IN
여러개의 값으로 검색하는 경우에는 SQL과 마찬가지로 In
을 사용한다.
// SELECT e FROM Employee WHERE e.lastname in ?1
List<Employee> findByLastnameIn(List<String> lastname);
After / Before
날짜 비교는 LessThan(Equal)
, GreaterThan(Equal)
를 사용할 수 있지만, After
, Before
에서도 표현할 수 있다.
// SELECT e FROM Employee WHERE e.lastname > ?1
List<Employee> findByHiredAtAfter(Date date);
// SELECT e FROM Employee WHERE e.lastname < ?1
List<Employee> findByHiredAtBefore(Date date);
OrderBy
조회 결과를 정렬하려면 OrderBy
를 사용한다. 필드 이름은 OrderBy
뒤에 작성한다. 오름차순, 내림차순는 필드 이름 뒤에 작성한다. 그러면 OrderBy + 필드이름 + Asc(Desc)
와 같은 형태가 된다.
// SELECT e FROM Employee e WHERE e.lastname = ?1 ORDER BY e.age
List<Employee> findByLastnameOrderByAge(String lastname);
// SELECT e FROM Employee e WHERE e.firstname = ?1 ORDER BY e.firstname ASC
List<Employee> findByFirstnameOrderByHiredAtAsc(String firstname);
여러 필드로 정렬하려면, Asc
, Desc
을 지정하여 아래와 같이 연결하면 된다.
// SELECT e FROM Employee e WHERE e.lastname = ?1 ORDER BY e.age ASC, e.firstname DESC
List<Employee> findByLastnameOrderByAgeAscFirstnameDesc(String lastname);
Top / First
find
이후에 First
또는 Top
을 붙이는 것으로 조회 건수를 줄일 수 있다.
Employee findFirstByLastname(String lastname);
Employee findTopByLastname(String lastname);
List<Employee> findFirst3ByActiveTrueOrderByAgeDesc();
List<Employee> findTop5ByHiredAtIs(Date date);
키워드를 결합
And
, Or
으로 조건의 조합 수 있다.
// SELECT e FROM Employee e WHERE e.age = ?1, e.active = true
List<Employee> findByAgeIsAndActiveTrue(int age);
// SELECT e FROM Employee e WHERE e.lastname LIKE ?1 OR e.lastname LIKE ?2
List<Employee> findByLastnameStartingWithOrFirstnameEndingWith(String lastname, String firstname);
// SELECT e FROM Employee e WHERE e.age BETWEEN ?1 AND ?2 AND e.department = ?3
List<Employee> findByAgeBetweenAndDepartmentIs(int startAge, int endAge, Department department);