개발새발 블로그
[스프링부트3 자바 백엔드 개발 입문] 12장 - 서비스 계층과 트랜잭션 본문
- 트랜잭션 : 모두 성공해야 하는 일련의 과정(순서)
- 롤백 : 트랜잭션이 실패가 될 경우 진행 초기상태로 다시 돌리는 것.
<ArticleService>
@Slf4j
@Service
public class ArticleService {
@Autowired
private ArticleRepository articleRepository;
public List<Article> index() {
return articleRepository.findAll();
}
public Article show(Long id) {
return articleRepository.findById(id).orElse(null);
}
public Article create(ArticleForm dto) {
Article article = dto.toEntity();
//API에 id값을 전달해줬는데 그게 기존에 있는 id일 경우 수정하면 안되므로 400 뜨도록
if(article.getId() != null)
return null;
return articleRepository.save(article);
}
public Article update(Long id, ArticleForm dto) {
Article article = dto.toEntity();
log.info("id: {}, article:{}", id, article.toString());
Article target = articleRepository.findById(id).orElse(null);
if(target == null || id != article.getId()){
log.info("잘못된 요청! id:{}, article:{}", id, article.toString());
return null; //응답은 컨트롤러가 하므로
}
target.patch(article); //필드중 일부만 수정하는 경우를 위해서.. patch 메서드 별도로 생성
Article updated = articleRepository.save(target);
return updated;
}
public Article delete(Long id) {
//1. 대상 찾기
Article target = articleRepository.findById(id).orElse(null);
//2. 잘못된 요청 처리하기
if(target == null){
return null;
}
//3. 대상 삭제하기
articleRepository.delete(target);
return target;
}
}
@Service
public class ArticleService {
@Autowired
private ArticleRepository articleRepository;
public List<Article> index() {
return articleRepository.findAll();
}
public Article show(Long id) {
return articleRepository.findById(id).orElse(null);
}
public Article create(ArticleForm dto) {
Article article = dto.toEntity();
//API에 id값을 전달해줬는데 그게 기존에 있는 id일 경우 수정하면 안되므로 400 뜨도록
if(article.getId() != null)
return null;
return articleRepository.save(article);
}
public Article update(Long id, ArticleForm dto) {
Article article = dto.toEntity();
log.info("id: {}, article:{}", id, article.toString());
Article target = articleRepository.findById(id).orElse(null);
if(target == null || id != article.getId()){
log.info("잘못된 요청! id:{}, article:{}", id, article.toString());
return null; //응답은 컨트롤러가 하므로
}
target.patch(article); //필드중 일부만 수정하는 경우를 위해서.. patch 메서드 별도로 생성
Article updated = articleRepository.save(target);
return updated;
}
public Article delete(Long id) {
//1. 대상 찾기
Article target = articleRepository.findById(id).orElse(null);
//2. 잘못된 요청 처리하기
if(target == null){
return null;
}
//3. 대상 삭제하기
articleRepository.delete(target);
return target;
}
}
<ArticleApiController>
@Slf4j //로그를 찍을 수 있도록
@RestController
public class ArticleApiController {
// @Autowired //게시글 리파지토리
// private ArticleRepository articleRepository;
@Autowired //서비스
private ArticleService articleService;
//GET
@GetMapping("/api/articles")
public List<Article> index(){
return articleService.index();
}
@GetMapping("/api/articles/{id}")
public Article show(@PathVariable Long id){
return articleService.show(id);
}
//POST
@PostMapping("/api/articles")
public ResponseEntity<Article> create(@RequestBody ArticleForm dto){ //dto로 받아와서
Article created = articleService.create(dto); //엔티티로 변환
return (created != null) ?
ResponseEntity.status(HttpStatus.OK).body(created):
ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
//PATCH
@PatchMapping("/api/articles/{id}")
public ResponseEntity<Object> 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).body(null);
}
//DELETE
@DeleteMapping("/api/articles/{id}")
public ResponseEntity<Article> delete(@PathVariable Long id){
Article target = articleService.delete(id);
return (target != null)?
ResponseEntity.status(HttpStatus.NO_CONTENT).body(null):
ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
}
@RestController
public class ArticleApiController {
// @Autowired //게시글 리파지토리
// private ArticleRepository articleRepository;
@Autowired //서비스
private ArticleService articleService;
//GET
@GetMapping("/api/articles")
public List<Article> index(){
return articleService.index();
}
@GetMapping("/api/articles/{id}")
public Article show(@PathVariable Long id){
return articleService.show(id);
}
//POST
@PostMapping("/api/articles")
public ResponseEntity<Article> create(@RequestBody ArticleForm dto){ //dto로 받아와서
Article created = articleService.create(dto); //엔티티로 변환
return (created != null) ?
ResponseEntity.status(HttpStatus.OK).body(created):
ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
//PATCH
@PatchMapping("/api/articles/{id}")
public ResponseEntity<Object> 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).body(null);
}
//DELETE
@DeleteMapping("/api/articles/{id}")
public ResponseEntity<Article> delete(@PathVariable Long id){
Article target = articleService.delete(id);
return (target != null)?
ResponseEntity.status(HttpStatus.NO_CONTENT).body(null):
ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
}
<트랜잭션 맛보기>
@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();
}
@Transactional
public List<Article> createArticles(List<ArticleForm> dtos) {
//1. dto 묶음을 엔티티 묶음으로 변환하기
List<Article> articleList = dtos.stream()
.map(dto -> dto.toEntity())
.collect(Collectors.toList());
//2. 엔티티 묶음을 DB에 저장하기
articleList.stream()
.forEach(article -> articleRepository.save(article));
//3. 강제 예외 발생시키기
//id가 -1인 데이터 찾고 없으면 예외
articleRepository.findById(-1L)
.orElseThrow(()-> new IllegalArgumentException("결제 실패!"));
//4. 결과 값 반환하기
return articleList;
}
이렇게 list형태로 api를 보내면, articleService에서 만든 강제오류때문에, 서버 오류를 지칭하는 500오류가 뜬다.
근데 그냥 저장하면 에러는 뜨지만, 새로추가한 저 2개의 데이터는 계속 db에 저장되어있는 상태다.
이 문제를 해결하기 위해 @Transaction 어노테이션을 사용하고, 보통 서비스에서 사용한다.
'백 > spring' 카테고리의 다른 글
[스프링부트3 자바 백엔드 개발 입문] 11장 - HTTP와 REST 컨트롤러 (1) | 2024.02.07 |
---|---|
[스프링부트3 자바 백엔드 개발 입문] 8장 - 게시글 삭제하기: Delete (1) | 2024.01.22 |
[스프링부트3 자바 백엔드 개발 입문] 7장 - 게시글 수정하기: Update (0) | 2024.01.21 |
[스프링부트3 자바 백엔드 개발 입문] 6장 - 게시판 페이지 내 이동하기 (0) | 2024.01.21 |
[스프링부트3 자바 백엔드 개발 입문] 5장 - 게시글 읽기:Read (0) | 2024.01.20 |