Spring BootからJPA処理データベースへ(04)
12121 ワード
04.登録/変更/照会APIの作成
3つのクラスは作成にAPIが必要です
Dto
コントローラ
4-1)登録機能の実現:PostsAPIコントローラ、PostsSaveRequestDto、PostsServiceクラス
PostsApiコントローラソースコード
import com.cutehuman.springboot.service.posts.PostsService;
import com.cutehuman.springboot.web.dto.PostsSaveRequestDto;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RequiredArgsConstructor //선언된 모든 final 필드가 포함된 생성자 생성
@RestController //컨트롤러가 JSON을 반환하도록
public class PostsApiController {
private final PostsService postsService;
@PostMapping("/api/v1/posts") //POST 요청을 받을 수 있는 API 만들어 줌
public Long save(@RequestBody PostsSaveRequestDto requestDto){
return postsService.save(requestDto);
}
}
PostsSaveRequestDtoソースコードimport com.cutehuman.springboot.domain.posts.Posts;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter //선언된 모든 필드의 get 메소드 생성
@NoArgsConstructor //기본 생성자 자동 추가
public class PostsSaveRequestDto {
private String title;
private String content;
private String author;
@Builder //해당 클래스의 빌더 패턴 클래스 생성
//생성자 상단에 선언 시 생성자에 포함된 필드만 빌더에 포함
public PostsSaveRequestDto(String title, String content, String author){
this.title = title;
this.content = content;
this.author = author;
}
public Posts toEntity(){
return Posts.builder()
.title(title)
.content(content)
.author(author)
.build();
}
}
PostsServiceソースimport com.cutehuman.springboot.domain.posts.PostsRepository;
import com.cutehuman.springboot.web.dto.PostsSaveRequestDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@RequiredArgsConstructor //선언된 모든 final 필드가 포함된 생성자 생성
@Service
public class PostsService {
private final PostsRepository postsRepository;
@Transactional
public Long save(PostsSaveRequestDto requestDto){
return postsRepository.save(requestDto.toEntity()).getId();
}
}
にスプリングからbeanオブジェクトを取得する方法に注意
=>
4-2)登録機能検証テストコード
1.WebパッケージにPostsApiControllerTestを作成する
import com.cutehuman.springboot.domain.posts.Posts;
import com.cutehuman.springboot.domain.posts.PostsRepository;
import com.cutehuman.springboot.web.dto.PostsSaveRequestDto;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class PostsApiControllerTest {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Autowired
private PostsRepository postsRepository;
@After //메소드 실행 후 Advice 실행
public void tearDown() throws Exception{
postsRepository.deleteAll();
}
@Test
public void Posts_등록된다() throws Exception{
//given
String title = "title";
String content = "content";
PostsSaveRequestDto requestDto = PostsSaveRequestDto.builder()
.title(title)
.content(content)
.author("author")
.build();
String url = "http://localhost:" + port + "/api/v1/posts";
//when
ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, requestDto, Long.class);
//then
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(responseEntity.getBody()).isGreaterThan(0L);
List<Posts> all = postsRepository.findAll();
assertThat(all.get(0).getTitle()).isEqualTo(title);
assertThat(all.get(0).getContent()).isEqualTo(content);
}
}
@WebMvcTestテスト:@WebMvcTestはJPA機能をサポートしていません.
(コントローラ、コントローラ、機器等の外部可動部品のみ)
2.テストの実行
4-3)修正/照会機能の実現
PostsApiController
@RequiredArgsConstructor
@RestController
public class PostsApiController {
...
@PutMapping("/api/v1/posts/{id}")
public Long update(@PathVariable Long id, @RequestBody PostsUpdateRequestDto requestDto){
return postsService.update(id, requestDto);
}
@GetMapping("/api/v1/posts/{id}")
public PostsResponseDto findById(@PathVariable Long id){
return postsService.findById(id);
}
}
PostsResponseDtoweb.dtoパッケージで作成
import com.cutehuman.springboot.domain.posts.Posts;
import lombok.Getter;
@Getter
public class PostsResponseDto {
private Long id;
private String title;
private String content;
private String author;
// Entity의 필드 중 일부만 사용하므로
// 생성자로 Entity를 받아 필드에 값을 넣음
public PostsResponseDto(Posts entity){
this.id = entity.getId();
this.title = entity.getTitle();
this.content = entity.getContent();
this.author = entity.getAuthor();
}
}
PostsUpdateRequestDtoweb.dtoパッケージで作成
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
public class PostsUpdateRequestDto {
private String title;
private String content;
@Builder
public PostsUpdateRequestDto(String title, String content){
this.title = title;
this.content = content;
}
}
Postspublic class Posts {
...
public void update(String title, String content){
this.title = title;
this.content = content;
}
}
PostsService@RequiredArgsConstructor
@Service
public class PostsService {
...
@Transactional
public Long update(Long id, PostsUpdateRequestDto requestDto){
Posts posts = postsRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("해당 게시글이 없습니다. id=" + id));
posts.update(requestDto.getTitle(), requestDto.getContent());
return id;
}
public PostsResponseDto findById (Long id){
Posts entity = postsRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("해당 게시글이 없습니다. id="+id));
return new PostsResponseDto(entity);
}
}
❗update機能ではデータベースへのクエリーは許可されていません:JPAの永続性コンテキスト
永続性コンテキストは、永続的なストレージエンティティの環境であり、論理概念です.JPAでは、エンティティが永続性コンテキストに含まれているかどうかが重要です.
JPAのエンティティーマネージャがDBからトランザクションにデータをインポートした場合、そのデータは永続性コンテキストのままになります.
この状態でデータの値を変更すると、トランザクションの終了時にテーブルに変更が反映されます.
->エンティティオブジェクトの値を変更する場合は、更新クエリX=>ダーティ処理の概念を個別に発行する必要があります.
4-4)修正機能テストコードを使用して検証する
1.PostsApiControllerTestでのコード作成
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class PostsApiControllerTest {
@Test
public void Posts_수정된다() throws Exception{
//given
Posts savedPosts = postsRepository.save(Posts.builder()
.title("title")
.content("content")
.author("author")
.build());
Long updateId = savedPosts.getId();
String expectedTitle = "title2";
String expectedContent = "content2";
PostsUpdateRequestDto requestDto = PostsUpdateRequestDto.builder()
.title(expectedTitle)
.content(expectedContent)
.build();
String url = "http://localhost:" + port + "/api/v1/posts/" + updateId;
HttpEntity<PostsUpdateRequestDto> requestEntity = new HttpEntity<>(requestDto);
//when
ResponseEntity<Long> responseEntity = restTemplate.exchange(url, HttpMethod.PUT, requestEntity, Long.class);
//then
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(responseEntity.getBody()).isGreaterThan(0L);
List<Posts> all = postsRepository.findAll();
assertThat(all.get(0).getTitle()).isEqualTo(expectedTitle);
assertThat(all.get(0).getContent()).isEqualTo(expectedContent);
}
}
2.テストの実行更新クエリーを実行するかどうかを確認できます
4-5)Tomcatを使用したクエリーチェック機能
ローカル環境では、DBとしてH 2を使用します.メモリに直接アクセスするには、Webコンソールを使用する必要があります.
1.Webコンソールの有効化
application.属性へのオプションの追加
spring.h2.console.enabled=true
2.アプリケーションクラスを実行する主な方法
SELECT * FROM posts;
insert into posts (author, content, title) values ('author', 'content', 'title');
:ブラウザにhttp://localhost:8080/api/v1/posts/1と入力し、APIクエリー機能をテストする
Reference
この問題について(Spring BootからJPA処理データベースへ(04)), 我々は、より多くの情報をここで見つけました https://velog.io/@cutehuman/03テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol