JPA에서는 이전에 서블릿이나 스프링에서 사용하던 DAO 클래스와 비슷한 역할을 하는 클래스를 우리가 정의해야 한다. Jpa의 표준 명세라는 것이 있고 우리가 그것을 가져다 쓰는 방식으로 구현을 하게 되는 것이다. 그래서 간단한 쿼리 같은 것은 메서드 하나로 뚝딱이지만 복잡한 쿼리는 이러한 메서드들로 해결이 되지 않는다는 문제점이 있다. 하지만 JPA 입문 초반에는 복잡한 쿼리에 대한 고민보다는 이러한 표준 명세에 대하여 익히는 것을 우선으로 하되 JpaRepository API 인터페이스 공부를 제대로 잡고 하는 것이 필요하다. 여기서는 '진짜 심플한 예제'부터 시작한다. 해당 엔터티를 하나 정의하고 그에 대한 엔터티 Repository를 만든다.
명명 방식은 다음과 같다.
- 엔터티명 + Repository (클래스명은 자유이긴한데 일반적으로 이렇게 정의)
이렇게 우리가 정의한 클래스는 JpaRepository를 상속 받아서 써야 한다.
- 이 때 상속을 받게 되면 기본적으로 부모 클래스에서 이미 정의된 메서드를 사용할 수 있다.
- JpaRepository의 클래스 계층구조는 다음과 같다.
- CrudRepository에서는 existsById, findById, save, saveAll, findAll, count, deleteById, delete, deleteAll 등의 메서드를 정의하고 있다. 말 그대로 Crud에 관련한 기본 메서드 정의이다.
- PagingAndSortingRepository
- 페이징 처리를 위한 메서드를 제공
- QueryByExampleExecutor
- 어떤 명세를 인자로 받으면 그에 맞는 동적으로 쿼리를 제공해준다.
사용자 정의 메소드
- 위에서 상속하는 클래스가 제공하는 메서드를 쓰지 않고 JPA가 약속한 규칙에 따라 사용자가 메서드를 정의해서 써야 한다. 주로 목적은 테이블 칼럼에 맞추고 Where절 부분에 해당하는 조건을 파라미터로 받기 위한 것이다.
- 메서드 이름은 CamelCase로 입력해야 한다.
- 예시)
- findBy○○○(String ○○○)
- findBy○○○Null
- findAllByOrderBy○○○
- findAllByOrderBy○○○Desc
- findBy○○○In(String[] strings)
- 예시)
- ItemRepository.java
public interface ItemRepository extends JpaRepository<Item, String>{
// 여기다가 이제 메서드 정의만 추가 ↓↓↓↓↓↓↓
}
- m9
- where 조건이 붙은 쿼리를 정의하는 느낌으로 구성할 수 있다.
- findByName("마우스") ----> ItemRepository에 Item findByName(String name); 추가
- 아래의 표현은 결과가 동일하다.
- Item findByName(String name);
- Item findByNameIs(String name);
- Item findByNameEquals(String name);
- itemRepo.findByPrice(100000) ----> ItemRepository에 Item findByPrice(int price); 추가
@GetMapping("/item/m9")
public String m9(Model model) {
//findByName으로 자동으로 구현
// Item item = itemRepo.findByName("마우스");
// Item item = itemRepo.findByPrice(100000);
// 메서드 이름 패턴이 중요하다.
// 가장 중요하게 생각하는 패턴이 > findBy이다.
// findBy컬럼명
// - By : Po
//Item item = itemRepo.findByNameIs("키보드");
Item item = itemRepo.findByNameEquals("키보드");
model.addAttribute("result", item);
return "item/result";
}
- m10
- where절에 And 혹은 Or 사용
- where절 다중조건
- itemRepo.findByColorAndOwnerAndPrice("black", "홍길동", 150000);
@GetMapping("/item/m10")
public String m10(Model model) {
// And, Or
//Item item = itemRepo.findByColorAndOwner("yellow", "홍길동");
//Item item = itemRepo.findByColorAndOwnerAndPrice("black", "홍길동", 150000);
Item item = itemRepo.findByColorOrPrice("gold", 300000);
//where item0_.color=? and item0_.owner=? and item0_.price=?
model.addAttribute("result", item);
return "item/result";
}
- m11
- findBy에 정렬 추가
- 파라미터 두번째 인자 Sort.by
- 첫번째 인자로 오름, 내림차순을 정하고, properties를 가변인자로 받아서 정렬 기준을 정한다.
- findBy에 정렬 추가
- Like 쿼리 : 패턴 매칭
- findBy○○○Like(패턴)
- findBy○○○IsLike(패턴)
- findBy○○○NotLike(패턴)
- findBy○○○IsNotLike(패턴)
- 패턴으로 %, _ 등을 넣어서 찾을 수 있다.
- StartingWith, EndingWith, Containing : ~로 시작거나 끝나거나 포함하는걸로 매칭
- findBy○○○[Is]StartingWith(검색어)
- findBy○○○[Is]EndingWith(검색어)
- findBy○○○[Is]Containing(검색어)
@GetMapping("/item/m11")
public String m11(Model model) {
//List<Item> list = itemRepo.findByColor("white");
//List<Item> list= itemRepo.findByColor("white", Sort.by(Sort.Direction.ASC, "price"));
//color를 조건 price를 기준으로 order by를 했다.
//List<Item> list= itemRepo.findByColorAndPrice("yellow", 95000);
//List<Item> list= itemRepo.findByColorOrOwner("white", "홍길동");
//- findByNameLike()
//- findByNameIsLike()
//- findByNameNotLike() // 이거 빼고 찾아줘 ~
//- findByNameIsNotLike()
//- %, _를 직접 명시
//List<Item> list= itemRepo.findByNameLike("키보드");
//List<Item> list= itemRepo.findByNameLike("%키보드");
// - findByName[Is]StartingWith
// - findByName[Is]EndingWith
// - findByName[Is]Containing
// List<Item> list= itemRepo.findByNameStartingWith("마우스");
List<Item> list= itemRepo.findByNameEndingWith("마우스");
model.addAttribute("list", list);
return "item/result";
}
- m12
- findBy○○○[Is]Null, findBy○○○[Is]NotNull
- 컬럼값이 null인 레코드 검색
- findBy○○○[Is]Empty, findBy○○○[Is]NotEmpty
- 컬럼값이 null이거나 빈문자열인 레코드 검색
- 정렬
- ex) List<Item> list = itemRepo.findAll(Sort.by(Sort.Direction.DESC, "price"));
- findAll에 Sort를 받도록 오버로딩 되어 있다.
- List<T> findAll(Sort sort);
- 좀 더 가독성 있는 메서드명 버전
- List<Item> list = itemRepo.findAllByOrderByColor();
- List<Item> list = itemRepo.findAllByOrderByColorDesc();
- 정렬조건 추가
- List<Item> list = itemRepo.findByOwnerOrderByColorDesc("홍길동");
- 조건 추가
- 비교
- findBy○○○[IS]GreaterThan, [IS]LessThan, [Is]Between, [Is]GreaterThanEqual, [Is]LessThanEqual
- List<Item> list = itemRepo.findByPriceGreaterThan(100000);
- List<Item> list = itemRepo.findByPriceGreaterThan(100000, Sort.by("price"));
- List<Item> list = itemRepo.findByPriceLessThan(100000, Sort.by("price"));
- List<Item> list = itemRepo.findByPriceBetween(90000, 120000);
- List<Item> list = itemRepo.findByOrderdateBetween("2023-06-25", "2023-06-27");
- findBy○○○[IS]GreaterThan, [IS]LessThan, [Is]Between, [Is]GreaterThanEqual, [Is]LessThanEqual
- In, NotIn
- Where color in ('yellow', 'blue')
- List 형식으로 인자로 넣는다.
- findBy○○○In
- findBy○○○NotIn
- findBy○○○[Is]Null, findBy○○○[Is]NotNull
List<String> colors = new ArrayList<String>();
colors.add("yellow");
colors.add("blue");
List<Item> list = itemRepo.findByColorIn(colors);
List<Item> list = itemRepo.findByOwnerNotIn(new String[]{"홍길동", "아무개"});
@GetMapping("/item/m12")
public String m12(Model model) {
// [Is]Null, [Is]NotNull
// - 컬럼값이 null인 레코드 검색
// [Is]Empty, [Is]NotEmpty
// - 컬럼값이 null이거나 빈문자열인 레코드 검색
//List<Item> list = itemRepo.findByOwnerNull();
//List<Item> list = itemRepo.findByOwnerEmpty();
//List<Item> list = itemRepo.findByOwnerNotNull();
// 정렬
// List<Item> list = itemRepo.findAll(Sort.by(Sort.Direction.DESC, "price")); // > findAll
// List<Item> list = itemRepo.findAll(Sort.by("name")); // 오름차순
// List<Item> list = itemRepo.findAllByOrderByColor();
// List<Item> list = itemRepo.findAllByOrderByColorDesc();
// List<Item> list = itemRepo.findByOwnerOrderByColorDesc("홍길동");
// where item0_.owner=? order by item0_.color desc
// [IS]GreaterThan, [IS]LessThan, [Is]Between
// [Is]GreaterThanEqual, [Is]LessThanEqual
// List<Item> list = itemRepo.findByPriceGreaterThan(100000);
// List<Item> list = itemRepo.findByPriceGreaterThan(100000, Sort.by("price"));
// List<Item> list = itemRepo.findByPriceLessThan(100000, Sort.by("price"));
// List<Item> list = itemRepo.findByPriceBetween(90000, 120000);
// List<Item> list = itemRepo.findByOrderdateBetween("2023-06-25", "2023-06-27");
//IgnoreCase
//- 특정 컬럼의 대소문자를 구분하지 않고 검색
// List<Item> list = itemRepo.findByColor("white");
// List<Item> list = itemRepo.findByColorIgnoreCase("White");
// In, NotIn
// - Where color in ('yellow', 'blue')
// List<String> colors = new ArrayList<String>();
// colors.add("yellow");
// colors.add("blue");
//
// List<Item> list = itemRepo.findByColorIn(colors);
//List<Item> list = itemRepo.findByOwnerIn(new String[]{"홍길동", "아무개"});
List<Item> list = itemRepo.findByOwnerNotIn(new String[]{"홍길동", "아무개"});
model.addAttribute("list", list);
return "item/result";
}
- m13, m14
- JPA 도메인 클래스 컨버터(Domain Class Converter)
- PK를 URL Path로 넣거나 쿼리스트링으로 리퀘스트 했을 뿐인데 그걸 베이스로 쿼리를 만들어준다.
- 컨트롤러 메서드 파라미터 객체에 결과 값이 바로 채워진다.
- 코드량이 상당히 줄어든다. 값 조회 용도로만 쓰자
- 두가지 방식이 있다.
- RequestParam 방식
- PathVariable 방식
@GetMapping("/item/m13")
public String m13(Model model, @RequestParam("name") Item result) {
// PK를 리퀘스트 했을 뿐인데 그걸 베이스로 쿼리를 만들어서 아이템에 넣어주는 작업도 해주는 것이다.
// 도메인 클래스 컨버터(Domain Class Converter)
// - PK를 넘겨서, 바로 Entity를 조회할 수 있다.
System.out.println(result);
model.addAttribute("result", result);
// item/m13?name=키보드
//"마우스" > PK
// Optional<Item> result = itemRepo.findById(name);
// model.addAttribute("result", result.get()); // Optional은 꺼내야 한다.
return "item/result";
}
// 이 떄는 @PathVariable 이나 @RequestParam를 생략하면 안된다.
@GetMapping("/item/m14/{name}")
public String m14(Model model, @PathVariable("name") Item result) {
//item/m13?name=마우스
//item/m13/마우스
model.addAttribute("result", result);
return "item/result";
}
- m15
- First, Top 등의 최상위 한개, 상위 몇개, 범위 지정등의 쿼리를 정의할 수 있다.
- findFirstBy, findTopBy는 레코드 하나를 반환한다.
- findFirstBy, findTopBy는 결과는 동일
- findTop숫자By 등의 범위 지정 쿼리도 가능하다.
@GetMapping("/item/m15")
public String m15(Model model) {
// First
// Top
// 둘다 첫번쨰거 가져오는데 First는 무조건하나를 가져오는데 ★ Top은 원하는 범위를 선택한다.
// Item result = itemRepo.findFirstByOrderByPriceAsc();
// Item result = itemRepo.findTopByOrderByPriceAsc();
List<Item> list = itemRepo.findTop3ByOrderByPriceDesc();
model.addAttribute("list", list);
return "item/result";
}
- m16
- PageRequest : 페이징 기능을 담당한다.
- 레코드가 여러개 있을 때 페이징을 하는데 PageRequest.of 메서드로 기능을 처리한다. page로 몇 페이지, size로 몇개씩 가져올 것인가를 지정하고 Sort.by를 넣어서 정렬기준을 정할 수 있다.
- 반환값은 PageRequest라는 객체를 반환하며 findPageListBy라는 메서드로 엔터티 형식 객체로 바꿔준다.
- List<Item> findPageListBy(PageRequest pageRequest); 이건 ItemRepository에 이렇게 정의가 필요하다.
요청URL : http://localhost:8092/item/m16?page=1
@GetMapping("/item/m16")
public String m16(Model model, int page) {
// PageRequest 이게 페이징을 담당한다.
PageRequest pageRequest = PageRequest.of(page, 5, Sort.by("name"));
// Page는 0부터다 !!!
List<Item> list = itemRepo.findPageListBy(pageRequest);
model.addAttribute("list", list);
return "item/result";
}
- m17, m18
- @Query라는 애너테이션을 사용한다. @Query의 value에 들어가는 부분이 JPQL을 작성할 수 있는데 nativeQuery=true로 지정하면 SQL을 직접 정의할 수 있다. 이렇게 셋팅하면 JPQL과는 성격이 다름
- @Query(value="select * from Item where color = :color", nativeQuery=true)
- : 을 써서 파라미터 바인딩이 가능한데 :#을 쓰는 경우도 있다.
@GetMapping("/item/m17")
public String m17(Model model) {
//@Query
// - 사용자 쿼리 작성
// - 쿼리 메소드 키워드로 작성 불가능 쿼리 > 직접 SQL 작성
// select * from Item
List<Item> list = itemRepo.findAllItem(); // 메서드 이름이 중요하지 않다. 쿼리를 내맘대로 짠다. 쿼리를 내가 만든다.
model.addAttribute("list", list);
return "item/result";
}
@GetMapping("/item/m18")
public String m18(Model model, String color) {
List<Item> list = itemRepo.findAllItemByColor(color);
model.addAttribute("list",list);
return "item/result";
}
- ItemRepository.java 부분
// 이렇게 붙여주면 된다. 쿼리가 길고 복잡하면 이방법으로 쓴다.
@Query(value="select * from Item", nativeQuery=true)
List<Item> findAllItem();
// 이 쿼리를 JPQL 이라고 한다 > Java Persistence Language
@Query(value="select * from Item where color = :color", nativeQuery=true)
List<Item> findAllItemByColor(String color);
'JPA' 카테고리의 다른 글
스프링 부트 + JPA(1) (0) | 2023.07.18 |
---|