비즈니스 요구사항
회원 등록, 조회
회원 데이터가 변경될 때 변경 이력을 DB LOG로 남겨야 함.
일단 등록 시에만 LOG 남기도록 함.
도메인부터 새로 만듦.
@Entity
@Getter @Setter
public class Member {
@Id
@GeneratedValue
private Long id;
private String username;
public Member() {
}
public Member(String username) {
this.username = username;
}
}
Long id 가 @Id이고,
@GeneratedValue 줘서 이거는 DB나 어디선가 할당해 주는 값을 받는 거임. 이거 안 넣어주면 우리가 Id 직접 넣어줘야 함.
JPA에서 기본 생성자(비어 있는 거) 있어야 함.
간단하게 username만.
@Slf4j
@Repository
@RequiredArgsConstructor
public class MemberRepository {
private final EntityManager em;
@Transactional
public void save(Member member){
log.info("member 저장");
em.persist(member);
}
public Optional<Member> findByUsername(String username){
return em.createQuery("select m from Member m where m.username = :username", Member.class)
.setParameter("username", username)
.getResultList().stream().findAny();
}
}
리포지토리를 만듦.
이번엔 따로 SpringDataJPA 이용 안하고 그냥 EntityManager, 즉 JPA 이용해서 해 보겠음.
저장은 그냥 하면 됨.
id로 찾는 건 em에 있어서 상관없는데,
다른 걸로 찾는 건 쿼리(JPQL) 날려줘야 함.
쿼리 생성(쿼리, 반환받을타입)
.파라미터셋(키,밸류)
.반환.stream().findAny();
.반환.stream().findAny();
이거는 findAny() 위해서 stream()으로 바꾼 것 같은데,
findAny()가 반환 된 것 중 가장 첫번째 그냥 주는 거고, Optional로 줌.
그래서 사용함.
보통 SpringDataJPA도 optional로 주니까.
@Entity
@Getter @Setter
public class Log {
@Id @GeneratedValue
private Long id;
private String message;
public Log() {
}
public Log(String message) {
this.message = message;
}
}
로그도 간단하게 message만.
@Slf4j
@Repository
@RequiredArgsConstructor
public class LogRepository {
private final EntityManager em;
@Transactional
public void save(Log logMessage){
log.info("log 저장");
em.persist(logMessage);
if(logMessage.getMessage().contains("로그예외")){
log.info("log 저장 시 예외 발생");
throw new RuntimeException("예외 발생");
}
}
public Optional<Log> findByMessage(String message){
return em.createQuery("select l from Log l where l.message = :message", Log.class)
.setParameter("message", message)
.getResultList().stream().findAny();
}
}
똑같음.
Log log로 안한 이유는 lombok이랑 겹쳐서
그리고 그냥 테스트를 위해 메시지를 "로그예외"라고 하면 런타임 오류를 뱉게끔.
런타임 오류니 예외를 뱉으며 rollback될거임.
find도 똑같음.
@Slf4j
@Service
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
private final LogRepository logRepository;
public void joinV1(String username){
Member member = new Member(username);
Log logMessage = new Log(username);
log.info("== memberRepository 호출 시작 ==");
memberRepository.save(member);
log.info("== memberRepository 종료 ==");
log.info("== logRepository 호출 시작 ==");
logRepository.save(logMessage);
log.info("== logRepository 종료 ==");
}
public void joinV2(String username){
Member member = new Member(username);
Log logMessage = new Log(username);
log.info("== memberRepository 호출 시작 ==");
memberRepository.save(member);
log.info("== memberRepository 종료 ==");
try{
log.info("== logRepository 호출 시작 ==");
logRepository.save(logMessage);
}catch (RuntimeException e){
log.info("log 저장에 실패했습니다.", logMessage.getMessage());
log.info("정상 흐름 반환");
}
log.info("== logRepository 종료 ==");
}
}
서비스.
로그는 멤버 저장할 때 로그도 같이 남게끔 그냥 그런 역할.
둘다 username으로 저장하고, (Log는 message가 username)
뭐 그냥 그렇게 저장 하는거임.
joinV2는 한번 try catch로 잡아서 처리해 보는.
왜냐하면 로그 남기는 것 보다 Member 정상적으로 등록되고 클라이언트에게 정상적으로 등록되었다고 하는 게 더 중요함.
그래서 그냥 예외 먹어버림. 그러면 로그 롤백되도 그냥 정상 흐름됨.
뭐 트랜잭션 동작 보려는 것 같은데,
어차피 지금 MemberRepository, LogRepository 트랜잭션 범위가 따로라서 서로 상관 없음.
이거 같이 보려면 밖에서 한번 묶어야 함. 서비스나 어디서 @Transactional 해서
@Slf4j
@SpringBootTest
class MemberServiceTest {
@Autowired
MemberService memberService;
@Autowired
MemberRepository memberRepository;
@Autowired
LogRepository logRepository;
@Test
void outerTxOff_success(){
String username = "outerTxOff_success";
memberService.joinV1(username);
assertTrue(memberRepository.findByUsername(username).isPresent());
assertTrue(logRepository.findByMessage(username).isPresent());
}
}
테스트.
지금은 그냥 정상적으로 되는 경우 보는 거.
이번에 쓴 assertTrue는 말 그대로 true인지 보는 건데,
이거는 core꺼 아니고 junit꺼 Assertions임.
Optional 메소드 중에 isPresent(), 즉 존재하는지(null이 아닌지) 여부 검사하는 메소드가 있어서,
존재하는 것이 참인지 확인.
assertThat 했었던 것들이랑 똑같음. 조건에 만족하면 넘어가는 거고 만족하지 않으면 예외 일으킴.
저 junit꺼 Assertions도 core 꺼랑 하는 것은 비슷비슷함.
'스프링 > 6. 스프링 DB-2' 카테고리의 다른 글
56. 트랜잭션 전파 활용 3 (0) | 2023.10.17 |
---|---|
55. 트랜잭션 전파 활용 2 (0) | 2023.10.17 |
53. 트랜잭션 전파 6 (0) | 2023.10.16 |
52. 트랜잭션 전파 5 (0) | 2023.10.16 |
51. 트랜잭션 전파 4 (0) | 2023.10.16 |