-
코드로 배우는 스프링부트 웹 프로젝트 Day1Spring/코드로 배우는 스프링부트 웹 프로젝트 2022. 8. 10. 16:14
/* Annotation 들은 스프링부트 프로젝트의 Annotation 정리 페이지에 따로 정리해두었습니다. */
@Test public void TestInsertDummies() { IntStream.rangeClosed(1, 100).forEach(i -> { Memo memo = Memo.builder().memoText("Sample..." + i).build(); memoRepository.save(memo); }); }
IntStream.rangeClosed는 IntStream.range와 달리 끝 개수를 포함하게 됨
[조회 작업 테스트]
findById()
getOne()
// findById() @Test public void testSelect() { // 데이터베이스에 존재하는 mno Long mno = 100L; Optional<Memo> result = memoRepository.findById(mno); System.out.println("================================"); if (result.isPresent()) { Memo memo = result.get(); System.out.println(memo); } }
findById() 는 실행한 순간에 SQL은 처리가 되었고, ====은 SQL처리 이후에 실행된다.
// getOne() @Transactional @Test public void testSelect2() { //데이터베이스에 존재하는 mno Long mno = 100L; Memo memo = memoRepository.getOne(mno); System.out.println("================================"); System.out.println(memo); }
getOne()은 deprecated라고 한다.. (취소선)
그래도 알아보자면, getOne()은 실제 객체가 필요한 순간까지 SQL을 실행하지 않는다.
[수정 작업 테스트]
@Test public void testUpdate() { Memo memo = Memo.builder() .mno(100L) .memoText("Update Text") .build(); System.out.println(memoRepository.save(memo)); }
JPA는 엔티티 객체들을 메모리상에 보관하려고 하기 때문에 특정한 엔티티 객체가 존재하는지 확인하는 select가 먼저 실행되고 해당 @Id를 가진 엔티티객체가 있다면 update하고, 그렇지 않다면 insert한다.
[삭제 작업 테스트]
// 삭제 작업 테스트 @Test public void testDelete() { Long mno = 100L; memoRepository.deleteById(mno); }
삭제하려는 번호의 엔티티가 있는지 확인한 후, 삭제함
만약, deleteById()의 리턴 타입이 void이고, 해당 데이터가 존재하지 않으면,
org.framework.dao.EmptyResultDataAccessException 예외를 발생시킴
Pageable 인터페이스
Pageable 인터페이스는 페이지 처리에 필요한 정보를 전달하는 용도의 타입으로, 인터페이스이기 때문에 실제 객체를 구현할 때는 구현체인 org.springframework.data.domain.PageRequest라는 클래스를 사용함
PageRequest는 protected로 선언되어 있어서 new로 생성할 수 없음
=> 객체 생성을 하려면 static한 of()를 사용함
of (int page, int size) 0부터 시작하는 페이지 번호와 개수(size), 정렬이 지정되지 않음 of (int page, int size, Sort.Direction direction, String...props) 0부터 시작하는 페이지 번호와 개수, 정렬의 방향과 정렬 기준 필드들 of (int page, int size, Sort sort) 페이지 번호와 개수, 정렬 관련 정보 PageRequest는 page, size, Sort 정보를 이용해서 객체를 생성함
페이징처리: 한 화면에서 보여주는 데이터의 범위 처리
(ex. 100개의 데이터를 한 화면에 다 보여줄 순 없으므로 10개씩 나누어 보여주는 처리)
import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @Test public void testPageDefault() { //1페이지 10개 Pageable pageable = PageRequest.of(0, 10); Page<Memo> result = memoRepository.findAll(pageable); System.out.println(result); }
PageRequest.of(0, 10) : 1페이지에 데이터를 10개 가져오겠다는 뜻
Page타입은 해당 목록을 가져올 뿐 아니라, 실제 페이지 처리에 필요한 전체 데이터의 개수도 가져오는 쿼리도 같이 처리함!! (만일 데이터가 충분하지 않으면 데이터의 개수를 가져오는 쿼리는 실행하지 않음)
findAll()에 Pageable 타입의 파라미터를 전달하면 페이징 처리에 관련된 쿼리들을 실행하고, 이 결과들을 이용해서 리턴 타입으로 지정된 Page<엔티티 타입> 객체로 저장
@Test public void testPageDefault() { //1페이지 10개 Pageable pageable = PageRequest.of(0, 10); Page<Memo> result = memoRepository.findAll(pageable); System.out.println(result); System.out.println("--------------------------------------"); System.out.println("Total Pages: " + result.getTotalPages()); // 총 페이지 수 System.out.println("Total Count: " + result.getTotalElements()); // 전체 개수 System.out.println("Page Number: " + result.getNumber()); // 현재 페이지 번호 System.out.println("Page Size: " + result.getSize()); // 페이지 당 데이터 개수 System.out.println("has next page?" + result.hasNext()); // 다음 페이지 존재 여부 System.out.println("first page?: " + result.isFirst()); // 시작 페이지(0) 여부 } }
[정렬 조건 추가하기]
PageRequest에는 정렬과 관련된 org.springframework.data.domain.Sort 타입을 파라미터로 전달 가능
순차적 정렬, 역순 정렬 지정 가능
@Test public void testSort() { Sort sort1 = Sort.by("mno").descending(); Pageable pageable = PageRequest.of(0, 10, sort1); Page<Memo> result = memoRepository.findAll(pageable); result.get().forEach(memo -> { System.out.println(memo); }); }
📎정렬 조건을 다르게 지정하는 것도 가능! (and() 이용)
Memo 클래스의 memoText는 asc로 하고, mno는 desc로 지정하고 싶은 상황
Sort sort1 = Sort.by("mno").descending(); Sort sort2 = Sort.by("memoText").ascending(); Sort sortAll = sort1.and(sort2); // and를 이용한 연결 Pageable pageable = PageRequest.of(0, 10, sortAll); // 결합된 정렬 조건 사용
[쿼리 메서드(Query Methods) 기능과 @Query]
다양한 검색 조건 설정 가능하게 하는 기능
- 쿼리 메서드 : 메서드의 이름 자체가 쿼리의 구문으로 처리되는 기능
- @Query : SQL과 유사하게 엔티티 클래스의 정보를 이용해서 쿼리를 작성하는 기능
- Querydsl 등의 동적 쿼리 처리 기능 (다음 장에서 ... )
📎 쿼리 메서드(Query Methods)
: 주로 findBy 나 getBy로 시작하고 And나 Or같은 키워드를 이용해서 메서드의 이름 자체로 질의 조건을 만듦
< Memo 객체의 mno값이 70부터 80사이의 객체들을 구하고 mno의 역순으로 정렬 >
public interface MemoRepository extends JpaRepository<Memo, Long> { List<Memo> findByMnoBetweenOrderByMnoDesc(Long from, Long to); }
@Test public void testQueryMethods() { List<Memo> list = memoRepository. findByMnoBetweenOrderByMnoDesc(70L, 80L); for (Memo memo : list) { System.out.println(memo); } }
[쿼리 메서드와 Pageable의 결합]
public interface MemoRepository extends JpaRepository<Memo, Long> { List<Memo> findByMnoBetweenOrderByMnoDesc(Long from, Long to); Page<Memo> findByMnoBetween(Long from, Long to, Pageable pageable); }
@Test public void testQueryMethodWithPageable() { Pageable pageable = PageRequest.of(0, 10, Sort.by("mno").descending()); Page<Memo> result = memoRepository.findByMnoBetween(10L, 50L, pageable); result.get().forEach(memo -> System.out.println(memo)); }
일반적으로 쿼리 메소드에 정렬 조건은 Pageable 파라미터는 모든 쿼리 메소드에 적용할 수 있으므로 생략하고 만드는 경우가 많음
[deletBy로 시작하는 삭제처리]
쿼리 메소드를 이용해서 'deleteBy'로 매소드 이름을 시작하면 특정한 조건에 맞는 데이터 삭제 가능
<mnoRepository 인터페이스에 추가>
void deleteMemoByMnoLessThan(Long num);
@Commit @Transactional @Test public void testDeleteQueryMethods() { memoRepository.deleteMemoByMnoLessThan(10L); }
메모의 번호인 mno가 10보다 작은 데이터들을 삭제 ↑
실제 개발을 할 때에는 @Query 어노테이션을 주로 사용
[@Query 어노테이션]
Spring Data JPA가 제공하는 쿼리 메소드는 나중에 join이나 복잡한 조건을 처리해야 하는 경우, And, Or 등이 사용되면서 불편한 경우가 많음
=> @Query를 사용하는 경우가 더 많음
- 필요한 데이터만 선별적으로 추출 가능
- 데이터베이스에 맞는 순수한 SQL을 사용하는 기능
- insert, update, delete와 같은 select가 아닌 DML 등을 처리하는 기능(@Modifying과 함께 사용)
@Query의 value는 JPQL(Java Persistence Query Language)로 작성함 => 흔히 객체지향 쿼리라 불리는 구문들
테이블 -> 엔티티 클래스
테이블의 칼럼 -> 클래스에 선언된 필드
JPQL은 SQL과 매우 유사함
[@Query의 파라미터 바인딩]
- '?1. ?2' 와 1부터 시작하는 파라미터의 순서를 이용하는 방식
- ':xxx' 와 같이 ':파라미터 이름'을 활용하는 방식
- ':#{ }' 과 같이 자바 빈 스타일을 활용하는 방식
스프링 MVC와 Thymeleaf
재시작을 하지 않아도 Thymeleaf의 수정된 결과를 새로고침만으로 볼 수 있는 방법이 Modify options에서 Spring Boot의 On 'update' action 의 Update classes and resources를 설정하는 것이라고 한다.
하지만 지금 내가 쓰는 버전은 community 버전이기 때문에 ... 할 수 없는 것으로 보인다. (아예 Spring Boot 목록이 안 보인다.) 대학생은 무료로 Ultimate 버전을 받을 수 있는 것을 최근에 알았다. 이번 책만 끝내고 Ultimate 버전을 다운받아야 겠다.
SampleController 클래스
import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/sample") @Log4j2 public class SampleController { @GetMapping("/ex1") public void ex1() { log.info("ex1............."); } }
보통 html파일은 templates 폴더를 사용
ex1.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1 th:text="${'Hello World'}"></h1> </body> </html>
기본적인 Thymeleaf의 사용 방법: 'th:' 를 붙이고 속성값을 지정하는 것
Thymeleaf의 장점: HTML구조를 훼손(ex. JSP)하지 않고 필요한 내용을 추가하는 것이 가능함
'Spring > 코드로 배우는 스프링부트 웹 프로젝트' 카테고리의 다른 글
코드로 배우는 스프링부트 웹 프로젝트 Day 6 (0) 2022.08.18 코드로 배우는 스프링부트 웹 프로젝트 Day 5 (0) 2022.08.17 코드로 배우는 스프링부트 웹 프로젝트 Day 4 (0) 2022.08.16 코드로 배우는 스프링부트 웹 프로젝트 Day 3 (0) 2022.08.14 코드로 배우는 스프링부트 웹 프로젝트 Day 2 (0) 2022.08.12