- @Service 어노테이션이 사용되는 영역이다.
- 사실 스프링 빈으로 등록해주는 @Component와 큰 차이는 없지만, 명시적 구분을 위해서 @Service를 사용함.
- @Controller와 dao의 중간 영역이고, @transactional이 사용되는 공간이다.
- @transactional이 트랜잭션과 도메인 간 순서를 보장하는 역할을 함
package com.springbootaws.book.springboot.service.posts;
import com.springbootaws.book.springboot.domain.posts.PostRepository;
import com.springbootaws.book.springboot.domain.posts.Posts;
import com.springbootaws.book.springboot.web.dto.PostResponseDto;
import com.springbootaws.book.springboot.web.dto.PostSaveRequestDto;
import com.springbootaws.book.springboot.web.dto.PostUpdateRequestDto;
import com.springbootaws.book.springboot.web.dto.PostsListResponseDto;
import javafx.geometry.Pos;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class PostsService {
// 생성자로 PostRepository를 DI받을 수 있도록 해서 확장성을 높임.
private final PostRepository postRepository;
@Transactional
public Long save(PostSaveRequestDto postSaveRequestDto){
// 결국 요청으로 받는 데이터는 Posts가 아닌 PostSaveRequestDto 형태이다.
// --> Posts 테이블 정보가 수정되면 PostSaveRequestDto만을 수정하면 됨.
// postRepository.save()가 저장된 객체를 반환함.
return postRepository.save(postSaveRequestDto.toEntity()).getId();
}
@Transactional
public Long update(Long id, PostUpdateRequestDto postUpdateRequestDto){
// jpa의 영속성 컨텍스트 덕분에 entity를 수정하는 것만으로 @Transactional이 종료된 후 변경된 entity값을 반영한다.
Posts posts = postRepository.findById(id).orElseThrow(()-> new IllegalArgumentException("해당 데이터가 없습니다 id=" + id));
posts.update(postUpdateRequestDto.getTitle(), postUpdateRequestDto.getContent());
return id;
}
public PostResponseDto findById(Long id){
Posts posts = postRepository.findById(id).orElseThrow(()->new IllegalArgumentException("해당 데이터 없음 id=" + id));
return new PostResponseDto(posts);
}
@Transactional
public List<PostsListResponseDto> findAllDesc(){
return postRepository.findAllDesc().stream()
.map(PostsListResponseDto::new)// .map(posts -> new PostsListResponseDto(posts))
.collect(Collectors.toList());
}
@Transactional
public void delete(Long id){
Posts posts = postRepository.findById(id).orElseThrow(()->new IllegalArgumentException("해당 게시글이 없음 id=" + id));
postRepository.delete(posts);
}
}
- 스프링은 @Transactional를 통해서 선언전 트랜잭션을 지원한다.
- 트랜잭션은 클래스 메소드 → 클래스 → 인터페이스 메소드 → 인터페이스 순의 우선순위를 갖는다.
- @Transactional가 있는 클래스, 메소드의 경우, 해당 클래스에 트랜잭션이 적용된 프록시 객체가 생성되고 예외가 발생하면 전체 작업을 취소한다.
트랜잭션 주의 사항
- 트랜잭션을 사용할 때 Entity 클래스와 관계가 있는 다른 Entity 클래스의 FetchType가 Lazy라면 문제가 발생할 수 있다.
|
|
FetchType.LAZY |
|
FetchType.EAGER |
|