Member

package hello.springtx.propagation;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import lombok.Getter;
import lombok.Setter;

@Entity
@Getter @Setter
public class Member {

    @Id
    @GeneratedValue
    private Long id;
    private String username;

    public Member() { // 기본 생성자는 jpa 스펙상 무조건 무조건 무조건 있어야한다.
    }

    public Member(Long id) {
        this.id = id;
    }
}

JPA를 통해 관리하는 회원 엔티티이다.

MemberRepository

package hello.springtx.propagation;

import jakarta.persistence.EntityManager;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;

@Slf4j
@Repository
@RequiredArgsConstructor
public class MemberRepository {

    private final EntityManager em;

    @Transactional // jpa의 모든 변경은 트랜잭션 안에서 이루어져야만 한다.
    public void save(Member member) {
        log.info("member 저장");
        em.persist(member);
    }

    public Optional<Member> find(String username) { // (먼저 1개 걸리면) 만약 값이 없으면 옵셔널 안에 엠프티기 ㅇㅣㅆ음
        return em.createQuery("select m from Member m where m.username = :username", Member.class)
                .setParameter("username", username)
                .getResultList().stream().findAny(); // 만약에 결과가 2개가 나왔으면 그 둘 중에 1개만 찾아서 반환해주는 것이다.
    }
}

JPA를 사용하는 회원 리포지토리이다. 저장과 조회 기능을 제공한다.

Log

package hello.springtx.propagation;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import lombok.Getter;
import lombok.Setter;

@Entity // 데이터 베이스에 남겨놓는 로그이다.
@Getter @Setter
public class Log {

    @Id // 이어노테이션만 되어있으면 jpa에서는 내가 id를 직접 할당해줘야한다.
    //@Getter 실수.
    @GeneratedValue // db에서 뭐 할당하거나 아니면 값을 할당하는걸 사용하게 된다.
    private Long id;
    private String message;

    // jpa 쓰려면 기본 생성자는 필수임.
    public Log() {}

    public Log(String message) {
        this.message = message;
    }
}

JPA를 통해 관리하는 로그 엔티티이다.

LogRepository

package hello.springtx.propagation;

import jakarta.persistence.EntityManager;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;

@Slf4j
@Repository
@RequiredArgsConstructor
public class LogRepository {

    private final EntityManager em;

    @Transactional // 이 트랜잭션을 그 aop 프록시에서 트랜잭션 프록시에서 어떻게 하면 여기 메서드에서 런타임 익셉션이네. 롤백하면서 이 메서드 부분이 롤백 된다.
    public void save(Log logMessage) {
        log.info("log 저장");
        em.persist(logMessage);

        // 예외상황 만듦
        if (logMessage.getMessage().contains("로그예외")) {
            log.info("log 저장시 예외 발생");
            throw new RuntimeException("예외 발생");
        }
    }

    public Optional<Log> find(String message) {
            return em.createQuery("select l from Log l where l.message = :message", Log.class)
                    .setParameter("message", message)
                    .getResultList().stream().findAny();
        }
}

MemberService