서비스(service)
컨트롤러
와 리파지터리
사이에 위치한 계층.컨트롤러 - 서비스 - 리파지터리의 관계
(출처)
컨트롤러
(클라이언트의 요청 받기) -> 서비스
(리파지터리에게 클라이언트의 요청에 대한 데이터를 반환해 달라고 요청) -> 리파지터리
-> 서비스
-> 컨트롤러
(클라이언트에 응답)트랜잭션(transaction)
원자성
이라 함.)롤백(rollback)
이라 함.@Service
public class ArticleService {
@Autowired
private ArticleRepository articleRepository; // ArticleRepository 객체 주입.
}
@RestController
@Slf4j
public class ArticleApiController {
@Autowired
private ArticleService articleService; // @Service로 인해 에러표시 사라짐.
}
@Service
(@Autowired) private ArticleService articleService;
@Service
를 사용함으로써 에러표시 사라짐.(DI)
// controller
@GetMapping("/api/articles")
public List<Article> index() { // 모든 데이터 조회.
return articleService.index();
}
// service
public List<Article> index() {
return articleRepository.findAll();
}
controller
service
에 있는 index()
메서드 호출.service
articleRepository
의 findAll()
메서드를 통해 DB
에서 조회한 데이터
들을 반환.// controller
@GetMapping("/api/articles/{id}")
public Article show(@PathVariable Long id) { // 단일 데이터 조회.
return articleService.show(id);
}
// service
public Article show(Long id) {
return articleRepository.findById(id).orElse(null);
}
controller
{id}
변수를 @PathVariable
을 이용해 매개변수로 받아서, service
에 있는 show()
메서드 호출.service
id
를 이용해서 repository
의 findById()
메서드 호출함.있으면
해당 데이터
를 반환.없으면
null
을 반환.// controller
@PostMapping("/api/articles")
public ResponseEntity<Article> create(@RequestBody ArticleForm dto) { // 새로운 데이터 생성.
Article created = articleService.create(dto);
return created != null ? ResponseEntity.status(HttpStatus.OK).body(created) : ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
// service
public Article create(ArticleForm dto) {
Article article = dto.toEntity();
if (article.getId() != null) {
return null;
}
return articleRepository.save(article);
}
controller
REST API
에서 데이터를 생성할 때는 JSON
데이터를 받아와야 하므로, @RequestBody
를 통해 요청 Body(본문)
에 실어 보낸 데이터
를 매개변수(dto)
에 매핑
시켜줌.service
의 create()
메서드 호출.service
에서 반환된 값!= null
이면 HTTP 상태 코드(200
), 응답 Body에 생성한 데이터
를 담아서 반환.= null 이면
HTTP 상태 코드(400
), 응답 Body에 null
을 담아서 반환service
dto
를 Entity
로 변환.id
값은 DB
에서 auto_increment
로 자동 생성되기 때문에 id
가 값
이 있을 경우 null
을 반환.repository
의 save()
메서드를 통해 article
엔터티를 저장하고 반환.정상 처리
비정상 처리
// controller
@PatchMapping("/api/articles/{id}")
public ResponseEntity<Article> update(@PathVariable Long id, @RequestBody ArticleForm dto) { // 데이터 수정.
Article updated = articleService.update(id, dto);
return updated != null ? ResponseEntity.status(HttpStatus.OK).body(updated) : ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
// service
public Article update(Long id, ArticleForm dto) {
Article article = dto.toEntity();
Article target = articleRepository.findById(id).orElse(null);
if (target == null || id != article.getId()) {
return null;
}
target.patch(article);
return articleRepository.save(target);
}
controller
{id}
변수를 @PathVariable
을 이용해 매개변수
id
에 매핑.JSON 데이터
를 받아와야 하므로, @RequestBody
를 통해 요청 Body(본문)
에 실어 보낸 데이터
를 매개변수(dto)
에 매핑.service
에 있는 update()
메서드 호출.!= null
이면 HTTP 상태 코드(200
), 응답 Body에 수정된 데이터
를 담아서 반환.= null
이면 HTTP 상태 코드(400
), 응답 Body에 null
을 담아서 반환.service
매개변수
로 받은 dto
를 Entity
로 변환.매개변수
로 받은 id
와 repository
의 findById()
메서드를 이용해서 값을 조회.있다면
, 해당 데이터
를 저장.없다면
, null
을 저장.id
와 요청Body에 실려온 id
값이 다를 경우 null
반환.if
문이 false
여서 지나쳐왔다면 patch()
메서드를 호출해서 수정된 값이 있는
필드만 값을 갱신하고, save
메서드를 통해 저장한 뒤 반환.URL 요청id와 요청Body의 id가 다를 경우
일부 데이터만 수정한 경우
모든 데이터를 수정
// controller
@DeleteMapping("/api/articles/{id}")
public ResponseEntity<Article> delete(@PathVariable Long id) { // 데이터 삭제.
Article deleted = articleService.delete(id);
return deleted != null ? ResponseEntity.status(HttpStatus.NO_CONTENT).build() : ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
// service
public Article delete(Long id) {
Article target = articleRepository.findById(id).orElse(null);
if (target == null) {
return null;
}
articleRepository.delete(target);
return target;
}
controller
{id}
변수를 @PathVariable
을 이용해 매개변수
로 받아서, service
에 있는 delete()
메서드 호출.service
의 반환값.!= null
: HTTP 상태 코드(204
) 반환.= null
: HTTP 상태 코드(400
) 반환.service
매개변수
로 받은 id
와 repository
의 findById()
를 이용해서 값을 조회.if
문이 false
일 경우, repository
의 delete()
메서드를 호출해서 삭제한 뒤 반환.일련의 과정
.롤백
됨.// controller
@PostMapping("/api/transaction-test")
public ResponseEntity<List<Article>> transactionTest(@RequestBody List<ArticleForm> dtos) {
List<Article> createdList = articleService.createArticles(dtos);
return createdList != null ? ResponseEntity.status(HttpStatus.OK).body(createdList) : ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
// service
@Transactional
public List<Article> createArticles(List<ArticleForm> dtos) {
List<Article> articleList = dtos.stream().map(dto -> dto.toEntity()).collect(Collectors.toList());
// List<Article> articleList = new ArrayList<>();
// for (int i = 0; i < dtos.size(); i++) {
// ArticleForm dto = dtos.get(i);
// Article entity = dto.toEntity();
// articleList.add(entity);
// }
articleList.stream().forEach(article -> articleRepository.save(article));
// for (int i = 0; i < articleList.size(); i++) {
// Article article = articleList.get(i);
// articleRepository.save(article)
// }
articleRepository.findById(-1L).orElseThrow(() -> new IllegalArgumentException("실패!")); // 강제로 예외 발생.
return articleList;
}
Controller
@PostMapping("/api/transaction-test")
@PostMapping
으로 /api/transaction-test
요청 URL 받음.public ResponseEntity<List<Article>> transactionTest(@RequestBody List<ArticleForm> dtos)
List<>
, 메서드 반환타입도 List<>
로 해줌으로써 응답시 List
형태로 보냄.ResponseEntity
ResponseEntity<List<Article>>
이러한 형태.REST API
방식의 Post
요청을 받으므로 @RequestBody
어노테이션 추가.Post
요청 시 Body
에 실려있는 데이터
를 메서드 매개변수
로 매핑시킴.Service
@Transactional
createArticles
메서드는 하나의 트랜잭션
으로 묶임.롤백
됨.List<Article> articleList = dtos.stream().map(dto -> dto.toEntity()).collect(Collectors.toList());
dtos
묶음을 entity
묶음으로 변환.dtos.stream()
dtos
를 stream
으로 변환시킴..map(dto -> dto.toEntity())
map
은 스트림
의 각 요소를 다른 값으로 변환
하는 메서드.dto 객체
하나하나를 dto.toEntity()
메서드를 호출하여 변환..collect(Collectors.toList())
stream
에서 다시 리스트 형태로.스트림(stream)
문법은 List와 같은 자료구조에 저장된 요소를 하나씩 순회하면서 처리하는 코드 패턴.articleList.stream().forEach(article -> articleRepository.save(article));
articleList.stream()
List
를 Stream
으로 변환.forEach(article -> articleRepository.save(article))
forEach
는 스트림의 각 요소를 순회하는 메서드. article -> articleRepository.save(article)
을 사용하여 각 Article 객체
를 articleRepository
를 통해 저장하는 작업을 수행.articleRepository.findById(-1L).orElseThrow(() -> new IllegalArgumentException("실패!"));
id
값에 -1(없는 값)
을 넣어서 강제로 예외 발생시킴.