일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 스프링부트핵심가이드
- 스프링 시큐리티
- baeldung
- d
- Kernighan의 C언어 프로그래밍
- 리눅스
- 목록처리
- ㅒ
- 코드로배우는스프링웹프로젝트
- 자료구조와함께배우는알고리즘입문
- GIT
- 알파회계
- 구멍가게코딩단
- 서버설정
- 이터레이터
- 처음 만나는 AI수학 with Python
- 선형대수
- 친절한SQL튜닝
- /etc/network/interfaces
- 자료구조와 함께 배우는 알고리즘 입문
- 데비안
- network configuration
- 티스토리 쿠키 삭제
- 자바편
- 페이징
- 코드로배우는스프링부트웹프로젝트
- 네트워크 설정
- 처음 만나는 AI 수학 with Python
- resttemplate
- iterator
- Today
- Total
bright jazz music
백엔드 CRUD 테스트 + 페이징 테스트 본문
1. 데이터 추가 테스트
package com.test.mallapi;
import com.test.mallapi.domain.Todo;
import com.test.mallapi.repository.TodoRepository;
import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.time.LocalDate;
@SpringBootTest
@Log4j2
public class TodoRepositoryTests {
@Autowired
private TodoRepository todoRepository;
@Test
public void testInsert(){
for (int i =1; i<=100; i++) {
Todo todo = Todo.builder()
.title("title..." + i)
.dueDate(LocalDate.of(2023,12,31))
.writer("user00")
.build();
todoRepository.save(todo);
}
}
}
Hibernate:
insert
into
tbl_todo
(complete, due_date, title, writer)
values
(?, ?, ?, ?)
2. 데이터 조회 테스트
package com.test.mallapi;
import com.test.mallapi.domain.Todo;
import com.test.mallapi.repository.TodoRepository;
import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.time.LocalDate;
@SpringBootTest
@Log4j2
public class TodoRepositoryTests {
@Autowired
private TodoRepository todoRepository;
...
@Test
public void testRead() {
// 존재하는 번호로 확인
Long tno = 33L;
java.util.Optional<Todo> result = todoRepository.findById(tno);
Todo todo = result.orElseThrow();
log.info(todo);
}
}
ptional은 감싸고 있는 값이 null일 수 있는 객체를 처리하기 위한 컨테이너이다. Optional 클래스는 Java 8에서 도입되었으며, null을 직접 다루는 것보다 더 안전한 방법을 제공하여 NullPointerException을 방지할 수 있도록 설계되었다.
Optional 객체는 어떤 값이 존재할 수도 있고, 존재하지 않을 수도 있는 상황에 유용하다. 예를 들어, 데이터베이스에서 특정 값을 조회했을 때 그 값이 존재하지 않을 경우를 안전하게 처리하고자 할 때 Optional을 사용할 수 있다.
예제 코드에서 Optional<Todo>은 Todo 객체가 존재할 수도 있고 아닐 수도 있음을 나타낸다. 여기서 findById(tno) 메소드는 tno에 해당하는 Todo 객체를 찾아 Optional 형태로 반환한다. 객체가 존재하지 않을 경우 Optional은 비어 있는 상태가 된다.
orElseThrow() 메소드는 Optional 객체가 값을 갖고 있으면 그 값을 반환하고, 값이 없을 경우 즉시 예외를 발생시킨다. 이렇게 함으로써 값의 존재를 강제할 수 있다.
Todo todo = result.orElseThrow();
이 코드 라인은 result가 비어있다면 예외를 발생시키고, 값이 있다면 Todo 객체를 todo 변수에 할당한다.
3. 데이터 수정
엔티티 객체는 가능한 불변하게 만들어지는 것이 좋지만 상황에 따라 수정 가능한 객체를 만들기도 한다.
package com.test.mallapi.domain;
import jakarta.persistence.*;
import lombok.*;
import java.time.LocalDate;
//엔티티를 사용해서 DB와 애플리케이션 사이의 데이터를 동기화 하고 관리
@Entity
@Table(name="tbl_todo")
@Getter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Todo {
@Id // DB의 pk가 됨
// 고유한 pk를 가지게 하기 위해서 자동생성 방식을 사용. 이는 PK를 DB에서 자동 생성한다는 의미임.( 마리아디비의 경우 auto_increment)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long tno;
private String title;
private String writer;
private boolean complete;
private LocalDate dueDate;
// 아래 함수 추가
public void changeTitle(String title) {
this.title = title;
}
public void chnageComplete(boolean complete) {
this.complete = complete;
}
public void changeDueDate(LocalDate dueDate) {
this.dueDate = dueDate;
}
}
@Test
public void testModify() {
Long tno = 33L;
// java.util.Optional
Optional<Todo> result = todoRepository.findById(tno);
Todo todo = result.orElseThrow();
todo.changeTitle("Modified 33...");
todo.changeComplete(true);
todo.changeDueDate(LocalDate.of(2023,10,10));
todoRepository.save(todo);
}
Hibernate:
select
t1_0.tno,
t1_0.complete,
t1_0.due_date,
t1_0.title,
t1_0.writer
from
tbl_todo t1_0
where
t1_0.tno=?
Hibernate:
select
t1_0.tno,
t1_0.complete,
t1_0.due_date,
t1_0.title,
t1_0.writer
from
tbl_todo t1_0
where
t1_0.tno=?
Hibernate:
update
tbl_todo
set
complete=?,
due_date=?,
title=?,
writer=?
where
tno=?
findById()에서 한 번 조회, 그리고 save()에서 다시 한 번 select 후 update가 이루어짐
4. 데이터 삭제
@Test
public void testDelete() {
Long tno = 33L;
todoRepository.deleteById(tno);
}
Hibernate:
select
t1_0.tno,
t1_0.complete,
t1_0.due_date,
t1_0.title,
t1_0.writer
from
tbl_todo t1_0
where
t1_0.tno=?
Hibernate:
delete
from
tbl_todo
where
tno=?
JPA가 select이후 update/delete를 수행하는 이뉴는 JPA결과적으로 원하는 것이 애플리케이션의 데이터와 DB의 동기화이기 때문이다. JPA는 객체로 관리되는 상태를 DB에 자동으로 반영해주는데, 이 때문에 DB에만 접근하는 것이 아니라 데이터를 관리하는 존재(엔티티매니저)를 통해서 모든 작업이 이루어진다. 특정한 엔티티 객체를 변화시키기 위해서는 우선 엔티티 매니저의 관리 하에 있어야만 하기 때문에 select문을 통해서 메모리상으로 로딩하는 과정을 수행하게 된다.
5. 페이징 처리 테스트
Spring Data JPA에서는 Pageable타입을 사용해서 별도의 코드 작성 없이 페이징 처리를 할 수 있다.
JpaRepository에는 findAll()메서드를 통해서 한 번에 페이지에 대한 처리가 가능하다. findAll()의 파라미터 타입인 Pageable은 PageRequest.of(페이지번호, 사이즈)의 형태로 생성하는데 주의할 점은 페이지 번호가 0부터 시작한다는 것이다.
findAll()의 결과는 Page<엔티티> 타입으로 생성되는데 데이터의 수가 충분하면 내부적으로 DB에 count쿼리를 같이 실행한다.
package com.test.mallapi;
import com.test.mallapi.domain.Todo;
import com.test.mallapi.repository.TodoRepository;
import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import java.time.LocalDate;
import java.util.Optional;
@SpringBootTest
@Log4j2
public class TodoRepositoryTests {
@Autowired
private TodoRepository todoRepository;
// ..
.
@Test
public void testPaging() {
// import org.springframework.data.domain.Pageable;
Pageable pageable =
PageRequest.of(0, 10, Sort.by("tno").descending());
Page<Todo> result = todoRepository.findAll(pageable);
log.info(result.getTotalElements());
result.getContent().stream().forEach(todo -> log.info(todo));
}
}
Hibernate:
select
t1_0.tno,
t1_0.complete,
t1_0.due_date,
t1_0.title,
t1_0.writer
from
tbl_todo t1_0
order by
t1_0.tno desc
limit
?, ?
Hibernate:
select
count(t1_0.tno)
from
tbl_todo t1_0
2024-05-15T21:34:59.469+09:00 INFO 25012 --- [mallapi] [ Test worker] com.test.mallapi.TodoRepositoryTests : 99
2024-05-15T21:34:59.476+09:00 INFO 25012 --- [mallapi] [ Test worker] com.test.mallapi.TodoRepositoryTests : Todo(tno=100, title=title...100, writer=user00, complete=false, dueDate=2023-12-31)
2024-05-15T21:34:59.477+09:00 INFO 25012 --- [mallapi] [ Test worker] com.test.mallapi.TodoRepositoryTests : Todo(tno=99, title=title...99, writer=user00, complete=false, dueDate=2023-12-31)
2024-05-15T21:34:59.477+09:00 INFO 25012 --- [mallapi] [ Test worker] com.test.mallapi.TodoRepositoryTests : Todo(tno=98, title=title...98, writer=user00, complete=false, dueDate=2023-12-31)
2024-05-15T21:34:59.477+09:00 INFO 25012 --- [mallapi] [ Test worker] com.test.mallapi.TodoRepositoryTests : Todo(tno=97, title=title...97, writer=user00, complete=false, dueDate=2023-12-31)
2024-05-15T21:34:59.478+09:00 INFO 25012 --- [mallapi] [ Test worker] com.test.mallapi.TodoRepositoryTests : Todo(tno=96, title=title...96, writer=user00, complete=false, dueDate=2023-12-31)
쿼리에 limit가 걸려 있다. tno의 역순으로 정렬된 것을 확인할 수 있다.
아래는 전문
package com.test.mallapi;
import com.test.mallapi.domain.Todo;
import com.test.mallapi.repository.TodoRepository;
import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import java.time.LocalDate;
import java.util.Optional;
@SpringBootTest
@Log4j2
public class TodoRepositoryTests {
@Autowired
private TodoRepository todoRepository;
@Test
public void testInsert(){
for (int i =1; i<=100; i++) {
Todo todo = Todo.builder()
.title("title..." + i)
.dueDate(LocalDate.of(2023,12,31))
.writer("user00")
.build();
todoRepository.save(todo);
}
}
@Test
public void testRead() {
// 존재하는 번호로 확인
Long tno = 33L;
java.util.Optional<Todo> result = todoRepository.findById(tno);
Todo todo = result.orElseThrow();
log.info(todo);
}
@Test
public void testModify() {
Long tno = 33L;
// java.util.Optional
Optional<Todo> result = todoRepository.findById(tno);
Todo todo = result.orElseThrow();
todo.changeTitle("Modified 33...");
todo.changeComplete(true);
todo.changeDueDate(LocalDate.of(2023,10,10));
todoRepository.save(todo);
}
@Test
public void testDelete() {
Long tno = 33L;
todoRepository.deleteById(tno);
}
@Test
public void testPaging() {
// import org.springframework.data.domain.Pageable;
Pageable pageable =
PageRequest.of(0, 10, Sort.by("tno").descending());
Page<Todo> result = todoRepository.findAll(pageable);
log.info(result.getTotalElements());
result.getContent().stream().forEach(todo -> log.info(todo));
}
}
'Projects > react-spring' 카테고리의 다른 글
백엔드 생성 및 초기 테스트 (0) | 2024.05.15 |
---|