[Spring DB 1편] 4. 스프링과 트랜잭션

HJ·2023년 1월 27일
0

Spring DB 1편

목록 보기
4/7

김영한 님의 스프링 DB 1편 - 데이터 접근 핵심 원리 강의를 보고 작성한 내용입니다.
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-1/dashboard


1. 어플리케이션 구조

1-1. 어플리케이션 구조

  • 프레젠테이션 계층

    • UI와 관련된 처리 담당

    • 웹 요청과 응답

    • 사용자 요청을 검증

    • 주 사용 기술 : 서블릿과 HTTP 같은 웹 기술, 스프링 MVC

  • 서비스 계층

    • 비지니스 로직을 담당

    • 주 사용 기술 : 가급적 특정 기술에 의존하지 않고, 순수 자바 코드로 작성

  • 데이터 접근 계층

    • 실제 데이터베이스에 접근하는 코드

    • 주 사용 기술 : JDBC, JPA, File, Redis, Mongo ...


1-2. 서비스 계층

  • 핵심 비지니스 로직이 있는 서비스 계층이 가장 중요한 계층

  • 서비스 계층은 특정 기술에 종속적이지 않고 가능한 순수 자바 코드로 작성한다

    • UI( 웹 )과 관련된 부분이나 데이터 저장 기술을 변경해도 비지니스 로직은 최대한 변경없이 유지되어야 하기 때문

    • 기술 종속적인 부분은 프레젠테이션 계층이나 데이터 접근 계층이 가짐으로써 특정 기술로부터 서비스 계층을 보호해준다

  • 서비스 계층에서 데이터 접근 계층을 직접 접근하는 것이 아니라, 데이터 접근 계층이 인터페이스를 제공하고 서비스 계층은 이 인터페이스에 의존하는 것이 좋다

    • 그래야 서비스 코드의 변경 없이 JdbcRepository 를 JpaRepository 로 변경할 수 있다



2. 기존 코드 문제점

2-1. MemberServiceV1

public class MemberServiceV1 {

    private final MemberRepositoryV1 memberRepositoryV1;

    public void accountTransfer(String fromId, String toId, int money) throws SQLException {
        ...
    }
}
  • MemberRepositoryV1이라는 구체 클래스에 직접 의존

    • MemberRepository라는 인터페이스에 의존하면 MemberService의 코드 변경 없이 다른 구현 기술로 변경 가능
  • SQLException

    • JDBC 기술에 의존하는 부분 ( JDBC에 종속적인 예외 )

    • JPA로 변경하면 다른 예외가 올라오기 때문에 컴파일 오류 발생


2-2. MemberServiceV2

  • DB 1편 3번 게시글 7-2-2 Service 코드 참고

  • 트랜잭션은 비지니스 로직이 있는 서비스 계층에서 시작하는 것이 좋지만 트랜잭션 사용을 위해 DataSource, Connection, SQLException 등 JDBC 기술에 의존했다

  • 비지니스 로직과 JDBC 기술이 섞여 있고, 비지니스 로직을 처리하는 코드보다 JDBC를 이용해 트랜잭션을 처리하는 코드가 더 많다

  • JDBC 기술에 의존하기 때문에 JPA로 바꾸면 서비스 계층의 코드도 변경해야한다


2-3. 트랜잭션 문제

  • 트랜잭션을 적용하기 위해 서비스 계층에서 JDBC 기술을 사용하는 문제

    • 구현 기술을 변경해도 서비스 계층 코드는 최대한 유지할 수 있어야 하는데 트랜잭션을 적용하면서 서비스 계층에 JDBC 구현 기술의 누수가 발생
  • 트랜잭션 동기화 문제

    • 같은 트랜잭션을 유지하기 위해 커넥션을 데이터 접근 계층의 메서드에 파라미터로 넘겨야 한다

    • 또한 똑같은 기능도 트랜잭션용 기능과 트랜잭션을 유지하지 않아도 되는 기능으로 분리해야 한다

  • 트랜잭션 적용 반복 문제 : try , catch , finally 가 반복

2-4. 예외 누수

  • 데이터 접근 계층의 JDBC 구현 기술 예외가 서비스 계층으로 전파된다

  • SQLException 은 체크 예외이기 때문에 데이터 접근 계층을 호출한 서비스 계층에서 해당 예외를 잡아서 처리하거나, throws 를 통해서 다시 밖으로 던져야한다

  • SQLException 은 JDBC 전용 기술이기 때문에 나중에 JPA나 다른 데이터 접근 기술을 사용하면, 그에 맞는 다른 예외로 변경해야 하고, 결국 서비스 코드도 수정해야 한다


2-5. JDBC 반복

  • MemberRepository 코드는 순수한 JDBC를 사용했기 때문에 메서드끼리 유사한 코드의 반복이 너무 많다

    • try , catch , finally ...

    • 커넥션 획득, PreparedStatement 사용, 결과 매핑, 커넥션과 리소스를 정리 등




3. 트랜잭션 추상화

3-1. 트랜잭션 추상화와 의존관계

  • 서비스 계층에서 JDBC 기술에 의존하는 문제를 해결하기 위해 트랜잭션을 추상화한다

  • 트랜잭션 추상화 인터페이스를 만들고, 각각의 기술에 맞는 구현체를 만들어 DI를 통해 주입하면 된다

  • 서비스 계층이 특정 트랜잭션 기술이 아니라 추상화된 인터페이스에 의존 + DI 사용

  • ➡️OCP 원칙이 지켜지게 되었고 서비스 계층의 변경 없이 트랜잭션 기술 변경이 가능해짐


3-2. 스프링의 트랜잭션 추상화

public interface PlatformTransactionManager extends TransactionManager {

	TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;

	void commit(TransactionStatus status) throws TransactionException;

	void rollback(TransactionStatus status) throws TransactionException;
}
  • 스프링은 트랜잭션 추상화 기술을 제공하며 데이터 접근 기술에 따른 트랜잭션 구현체도 대부분 만들어두었다

  • 스프링이 제공하는 트랜잭션 매니저는 트랜잭션 추상화와 리소스 동기화 이렇게 2가지 역할을 한다

  • getTransaction() : 트랜잭션을 시작, 기존에 이미 진행 중인 트랜잭션이 있는 경우 해당 트랜잭션에 참여




4. 트랜잭션 동기화

4-1. 설명

  • 스프링이 제공하는 트랜잭션 매니저가 하는 두 번째 역할은 리소스 동기화

  • 트랜잭션 동안 같은 커넥션을 유지하기 위해 이전에는 파라미터로 커넥션을 전달했는데 이렇게 하면 커넥션을 넘기는 메서드와 넘기지 않는 메서드를 중복해서 만들어야하는 등 많은 단점이 존재

  • ➡️트랜잭션 동기화 매니저 사용


4-2. 트랜잭션 동기화 매니저

  • 트랜잭션 매니저는 내부에서 트랜잭션 동기화 매니저를 제공하는데 이것은 ThreadLocal을 사용해서 커넥션을 동기화해준다

  • 커넥션이 필요하면 이전처럼 파라미터로 전달할 필요 없이 트랜잭션 동기화 매니저를 통해 커넥션을 획득한다

  • ThreadLocal을 사용하기 때문에 멀티 Thread 환경에서 안전하게 커넥션 동기화 가능


4-3. 동작 방식

  1. 트랜잭션을 시작하려면 커넥션이 필요하기 때문에 트랜잭션 매니저는 데이터 소스를 통해 커넥션을 만들고 트랜잭션을 시작

  2. 트랜잭션 매니저는 트랜잭션이 시작된 커넥션을 트랜잭션 동기화 매니저에 보관

  3. Repository는 트랜잭션 동기화 매니저에 보관된 커넥션을 꺼내서 사용 ( 파라미터 전달 X )

  4. 트랜잭션이 종료되면 트랜잭션 매니저트랜잭션 동기화 매니저에 보관된 커넥션을 통해 트랜잭션을 종료하고 커넥션도 닫는다




5. 트랜잭션 매니저 적용하기

5-1. Repository

public class MemberRepositoryV3 {

    // public Member findById(Connection con, String memberId) throws SQLException { ... }  ➜ 제거

    // public void update(Connection con, String memberId, int money) throws SQLException { ... }   ➜ 제거

    private Connection getConnection() throws SQLException {
        // Connection con = dataSource.getConnection();  ➜ 제거
        Connection con = DataSourceUtils.getConnection(dataSource); // 추가
        ...
    }

    private void close(Connection con, Statement stmt, ResultSet rs) {
        ...
        // JdbcUtils.closeConnection(con);  ➜ 제거
        DataSourceUtils.releaseConnection(con, dataSource); // 추가
    }
}
  • 동일한 커넥션을 사용하도록 하기 위해 추가했던 메서드( 파라미터로 커넥션을 받는 메서드 ) 제거

  • Connection con = DataSourceUtils.getConnection(dataSource);

    • 트랜잭션 동기화 매니저가 관리하는 커넥션이 있으면 해당 커넥션을 반환

    • 트랜잭션 동기화 매니저가 관리하는 커넥션이 없는 경우 새로운 커넥션을 생성해서 반환

  • DataSourceUtils.releaseConnection(con, dataSource);

    • 트랜잭션을 사용하기 위해 동기화된 커넥션은 커넥션을 닫지 않고 그대로 유지해준다

    • 트랜잭션 동기화 매니저가 관리하는 커넥션이 없는 경우 해당 커넥션을 닫는다

    • con.close()를 사용해서 닫아버리면 커넥션이 유지되지 않는다


5-2. Service

public class MemberServiceV3_1 {
    
    private final PlatformTransactionManager transactionManager;
    private final MemberRepositoryV3 memberRepository;

    public void accountTransfer(String fromId, String toId, int money) throws SQLException {
        
        // 트랜잭션 시작
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());

        try {
            bizLogic(fromId, toId, money);  // 비지니스 로직 수행
            transactionManager.commit(status);

        } catch (Exception e) {
            transactionManager.rollback(status);
            throw new IllegalStateException(e);
        } 
    }
}
  • PlatformTransactionManager에 사용 기술에 따른 구현체를 DI 받는다

    • JDBC의 경우 DataSourceTransactionManager
  • transactionManager.getTransaction()

    • 트랜잭션을 시작

    • TransactionStatus를 반환하는데 이는 현재 트랜잭션 상태 정보가 포함되어 있다 ( 커밋이나 롤백 시 필요 )

  • 커밋이나 롤백 시, 트랜잭션 매니저가 release를 해주기 때문에 직접 release를 할 필요가 없다


5-3. 흐름 정리

  • 트랜잭션 매니저는 데이터소스를 알고 있기 때문에 커넥션을 획득하고 수동 커밋 모드 설정까지 수행

  • 그 후에 트랜잭션 동기화 매니저에 커넥션을 보관

  • 비지니스 로직 수행 ( 서비스 계층 )

  • 비지니스 로직 내에서 repository의 메서드를 호출하는데 그 메서드를 보면 커넥션을 획득함

  • 획득하는 커넥션은 트랜잭션 동기화 매니저에 있는 트랜잭션을 시작한 커넥션

    • DataSourceUtils를 통해 획득한 커넥션
  • repository에서 release를 할 때 트랜잭션 매니저를 통해 얻은 커넥션이라면 닫지 않는다

    • DataSourceUtils가 자동으로 수행
  • 비지니스 로직 수행이 끝나면 서비스 계층에서 트랜잭션 매니저를 통해 성공하면 커밋, 실패하면 롤백을 수행

  • 그렇게 하면 트랜잭션 매니저가 리소스를 반납하고 트랜잭션 동기화 매니저에 있는 커넥션도 정리한다


5-4. 초기화코드

class MemberServiceV3_1Test {

    private MemberRepositoryV3 memberRepository;
    private MemberServiceV3_1 memberService;


    @BeforeEach
    void before() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource(URL, USERNAME, PASSWORD);
        memberRepository = new MemberRepositoryV3(dataSource);
        PlatformTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
        memberService = new MemberServiceV3_1(transactionManager, memberRepository);
    }
}
  • JDBC 기술을 사용하므로, JDBC용 트랜잭션 매니저( DataSourceTransactionManager )를 선택해서 서비스에 주입 ( 트랜잭션 매니저에 주입 )

  • 5-3번을 보면 트랜잭션 매니저는 데이터소스를 알고 있다고 했는데 위의 코드를 보면 알 수 있는데

  • 데이터소스를 생성하고 이를 통해 트랜잭션 매니저를 생성하기 때문에 트랜잭션 매니저는 데이터소스를 알고 있는 것이다

  • 데이터소스를 알고 있기 때문에 커넥션을 획득할 수 있다




6. 트랜잭션 매니저 전체 흐름

6-1. 트랜잭션 시작

  1. 서비스 계층에서 transactionManager.getTransaction()을 호출해서 트랜잭션을 시작한다

  2. 트랜잭션을 시작하려면 먼저 데이터베이스 커넥션이 필요하다. 트랜잭션 매니저는 내부에서 데이터소스를 사용해서 커넥션을 생성한다

  3. 커넥션을 수동 커밋 모드로 변경해서 실제 데이터베이스 트랜잭션을 시작한다

  4. 커넥션을 트랜잭션 동기화 매니저에 보관한다

  5. 트랜잭션 동기화 매니저는 ThreadLocal에 커넥션을 보관한다. 따라서 멀티 Thread 환경에 안전하게 커넥션을 보관할 수 있다


6-2. 로직 실행

  1. 서비스는 비즈니스 로직을 실행하면서 리포지토리의 메서드들을 호출한다. 이때 커넥션을 파라미터로 전달하지 않는다

  2. 리포지토리 메서드들은 트랜잭션이 시작된 커넥션이 필요하다. 리포지토리는 DataSourceUtils.getConnection() 을 사용해서 트랜잭션 동기화 매니저에 보관된 커넥션을 꺼내서 사용한다. 이 과정을 통해서 자연스럽게 같은 커넥션을 사용하고, 트랜잭션도 유지된다

  3. 획득한 커넥션을 사용해서 SQL을 데이터베이스에 전달해서 실행한다

  • 참고> Repository에서 메서드 수행이 끝날 때 con.close()가 아닌 DataSourceUtils.releaseConnection() 을 호출

6-3. 트랜잭션 종료

  1. 비즈니스 로직이 끝나고 트랜잭션을 종료한다. 트랜잭션은 커밋하거나 롤백하면 종료된다

  2. 트랜잭션을 종료하려면 동기화된 커넥션이 필요하다. 트랜잭션 동기화 매니저를 통해 동기화된 커넥션을 획득한다

  3. 획득한 커넥션을 통해 데이터베이스에 트랜잭션을 커밋하거나 롤백한다

  4. 전체 리소스를 정리한다

    • 트랜잭션 동기화 매니저를 정리한다. 쓰레드 로컬은 사용후 꼭 정리해야 한다

    • 커넥션 풀을 고려해서 con.setAutoCommit(true) 로 되돌린다

    • con.close() 를 호출해셔 커넥션을 종료한다. 커넥션 풀을 사용하는 경우 con.close() 를 호출하면 커넥션 풀에 반환된다




7. 트랜잭션 템플릿

7-1. 설명

  • 트랜잭션을 사용하는 코드를 보면 트랜잭션 시작 ➜ 비지니스 로직 수행 ➜ 성공하면 커밋, 실패하면 롤백

  • 위의 과정이 트랜잭션을 사용하는 다른 서비스 계층에서도 동일하게 반복됨

  • ➡️템플릿 콜백 패턴을 활용하면 이런 반복을 해결할 수 있다

  • ➡️템플릿 콜백 패턴을 적용하려면 템플릿을 제공하는 클래스를 작성해야하는데, 스프링은     TransactionTemplate 라는 템플릿 클래스를 제공


7-2. TransactionTemplate

public class TransactionTemplate {

    private PlatformTransactionManager transactionManager;

    public <T> T execute(TransactionCallback<T> action) {..}

    void executeWithoutResult(Consumer<TransactionStatus> action) {..}
}
  • execute() : 응답 값( 리턴 값 )이 있을 때 사용한다

  • executeWithoutResult() : 응답 값( 리턴 값 )이 없을 때 사용한다


7-3. 트랜잭션 템플릿 적용

public class MemberServiceV3_2 {

    private final TransactionTemplate txTemplate;
    private final MemberRepositoryV3 memberRepository;

    public MemberServiceV3_2(PlatformTransactionManager transactionManager, MemberRepositoryV3 memberRepository) {
        this.txTemplate = new TransactionTemplate(transactionManager);
        this.memberRepository = memberRepository;
    }

    public void accountTransfer(String fromId, String toId, int money) throws SQLException {

        txTemplate.executeWithoutResult((status) -> {
            try {
                bizLogic(fromId, toId, money);
            } catch (SQLException e) {
                throw new IllegalStateException(e);
            }
        });
    }
}
  • 수정 전 코드는 5-2번 참고

  • TransactionTemplate을 사용하려면 transactionManager가 필요하기 때문에 생성자에서 transactionManager를 주입 받으면서 TransactionTemplate을 생성

  • executeWithoutResult() 안에서 트랜잭션을 시작하고 비지니스 로직을 수행

  • executeWithoutResult() 이 끝났을 때 비지니스 로직이 정상 수행되면 커밋, 언체크 예외가 터지면 롤백

  • 트랜잭션 템플릿 패턴을 적용함으로써 트랜잭션을 시작하고 커밋하거나 롤백하는 코드가 제거되었다




8. 트랜잭션 AOP

8-1. 필요성

  • 서비스에는 가능하면 비지니스 로직만 있어야하는데 트랜잭션 기술을 사용하기 위해 어쩔 수 없이 트랜잭션 코드가 필요하기 때문에 비지니스 로직과 트랜잭션 처리 기술이 함께 포함되어 있는 문제가 존재

  • ➡️AOP를 통해 핵심 기능인 비지니스 로직과 부가 기능인 트랜잭션을 분리한다


8-2. 프록시 도입 후

  • 프록시를 사용하면 트랜잭션을 처리하는 객체와 비지니스 로직을 처리하는 서비스 객체를 분리할 수 있다

  • 프록시는 대신 처리해주는 대리인 느낌이며 스프링이 자동으로 생성하고 스프링 빈으로 등록해준다

  • 클라이언트가 서비스를 직접 호출하는 것이 아닌 트랜잭션 프록시를 호출하면 프록시가 서비스를 호출한다

  • 결과적으로 서비스에는 순수 비지니스 로직만 남길 수 있다


8-3. 스프링이 제공하는 AOP

  • 스프링은 AOP를 제공하여 프록시를 편리하게 사용할 수 있게 해주며 트랜잭션 AOP를 처리하기 위한 모든 기능을 제공한다

  • 스프링부트를 사용하면 트랜잭션 AOP를 처리하기 위해 필요한 스프링 빈들도 자동으로 등록해준다

  • @Transactional을 사용하면 스프링의 트랜잭션 AOP는 이 어노테이션을 인식해서 트랜잭션 프록시를 적용해준다

  • @Transactional은 메서드에 붙여도 되고, 클래스에 붙여도 된다. 클래스에 붙이면 외부에서 호출 가능한 public 메서드가 AOP 적용 대상이 된다

  • 참고> 스프링 AOP를 적용하려면 어드바이저, 포인트컷, 어드바이스가 필요하고 스프링부트를 사용하면 해당 빈들은 스프링 컨테이너에 자동으로 등록된다

    • 어드바이저 : BeanFactoryTransactionAttributeSourceAdvisor

    • 포인트컷 : TransactionAttributeSourcePointcut

    • 어드바이스 : TransactionInterceptor




9. 트랜잭션 AOP 적용

9-1. Service

public class MemberServiceV3_3 {

    private final MemberRepositoryV3 memberRepository;

    public MemberServiceV3_3(MemberRepositoryV3 memberRepository) {
        this.memberRepository = memberRepository;
    }

    @Transactional
    public void accountTransfer(String fromId, String toId, int money) throws SQLException {
        bizLogic(fromId, toId, money);
    }
}
  • @Transactional의 의미

    • 이 어노테이션이 붙은 메서드가 호출될 때 트랜잭션을 걸고 시작하겠다

    • 메서드가 끝났을 때 성공하면 커밋, 런타임 예외가 발생하면 롤백하겠다


9-2. 스프링 빈 등록

@SpringBootTest
class MemberServiceV3_3Test {

    @Autowired
    private MemberRepositoryV3 memberRepository;
    @Autowired
    private MemberServiceV3_3 memberService;


    @TestConfiguration
    static class TestConfig {
        @Bean
        DataSource dataSource() {
            return new DriverManagerDataSource(URL, USERNAME, PASSWORD);
        }

        @Bean
        PlatformTransactionManager transactionManager() {
            return new DataSourceTransactionManager(dataSource());
        }

        @Bean
        MemberRepositoryV3 memberRepositoryV3() {
            return new MemberRepositoryV3(dataSource());
        }

        @Bean
        MemberServiceV3_3 memberServiceV3_3() {
            return new MemberServiceV3_3(memberRepositoryV3());
        }
    }
}
  • 트랜잭션 AOP를 적용하려면 스프링 컨테이너에 스프링 빈을 등록하고 사용하도록 변경해야함

  • @SpringBootTest 테스트를 돌릴 때 스프링 컨테이너를 생성해서 필요한 스프링 빈을 등록하고 의존관계를 주입받을 수 있게 해준다

  • @Autowired 를 통해 의존관계를 주입받는다

  • 스프링 빈으로 등록해야 다른 곳에서 의존관계를 주입받을 수 있기 때문에 @TestConfiguration 을 통해 스프링 빈으로 등록한다

  • DataSource, PlatformTransactionManager는 트랜잭션 AOP 적용을 위해 필요

    • 트랜잭션 AOP도 transactinManager를 통해 트랜잭션을 시작하고 종료하기 때문에 스프링 빈으로 등록되어 있어야한다

    • 트랜잭션 AOP는 스프링 빈에 등록된 트랜잭션 매니저를 찾아서 사용한다

  • 위처럼 스프링 빈으로 등록되어 있지 않으면 예외가 발생해도 롤백되지 않는다


9-3. 프록시 적용 확인

@Test
void AopCheck() {
    log.info("memberService class={}", memberService.getClass());
    log.info("memberRepository class={}", memberRepository.getClass());
    assertThat(AopUtils.isAopProxy(memberService)).isTrue();
    assertThat(AopUtils.isAopProxy(memberRepository)).isFalse();
}
#결과확인
memberService class=class ghkwhd.jdbc.service.MemberServiceV3_3$$EnhancerBySpringCGLIB$$7261db42
memberRepository class=class ghkwhd.jdbc.repository.MemberRepositoryV3
  • @Transactional이 붙어있으면 스프링이 해당 로직을 상속받아서 트랜잭션 코드를 만들어내는데 이렇게 만들어진 것이 트랜잭션 프록시

  • 트랜잭션 프록시는 내부에 트랜잭션을 처리하는 로직을 가지고 있으며 실제 서비스( 타겟 )를 호출하는 코드도 가지고 있다

  • 트랜잭션 프록시가 스프링 빈에 등록되기 때문에 의존관계를 주입받을 때 프록시를 받는다

  • EnhancerBySpringCGLIB라는 부분을 통해 서비스에 프록시( CGLIB )가 적용된 것을 확인


9-4. 전체 흐름 정리




10. 트랜잭션 관리

10-1. 선언적 트랜잭션 관리

  • @Transactional 어노테이션 하나만 선언해서 매우 편리하게 트랜잭션을 적용하는 것을 선언적 트랜잭션 관리라 한다

  • 선언적 트랜잭션 관리는 과거 XML에 설정하기도 했다

  • 이름 그대로 해당 로직에 트랜잭션을 적용하겠다 라고 어딘가에 선언하기만 하면 트랜잭션이 적용되는 방식


10-2. 프로그래밍 방식 트랜잭션 관리

  • 트랜잭션 매니저 또는 트랜잭션 템플릿 등을 사용해서 트랜잭션 관련 코드를 직접 작성하는 것을 프로그래밍 방식의 트랜잭션 관리라 한다



11. 자동 리소스 등록

  • 9-2번의 코드를 보면 데이터소스와 트랜잭션 매니저를 직접 스프링 빈으로 등록했는데 스프링부트는 이러한 리소스들을 자동으로 등록해준다

11-1. DataSource

  • 스프링부트는 DataSourcedataSource라는 이름으로 스프링 빈에 자동 등록한다

  • 개발자가 직접 데이터소스를 빈으로 등록하면 스프링부트는 자동으로 등록하지 않는다

  • application.properties에 있는 속성을 사용해서 DataSource를 생성하고 스프링 빈에 등록한다

  • 스프링부트가 기본으로 생성하는 데이터소스는 커넥션 풀을 제공하는 HikariDataSource

  • 커넥션 풀과 관련된 설정도 application.properties에서 할 수 있다

  • spring.datasource.url 속성이 없으면 내장 데이터베이스( 메모리 DB )를 생성하려고 시도한다


11-2. 트랜잭션 매니저

  • 스프링부트는 적절한 PlatformTransactionManagertransactionManager라는 이름으로 스프링 빈에 자동 등록한다

  • 개발자가 직접 트랜잭션 매니저를 빈으로 등록하면 스프링부트는 자동으로 등록하지 않는다

  • 어떤 트랜잭션 매니저를 선택할지는 등록된 라이브러리를 보고 각 구현기술에 맞는 구현체를 스프링 빈으로 등록한다

  • 참고> JPA와 JDBC를 둘 다 사용하는 경우 JpaTransactionManager 를 등록하고 JpaTransactionManagerDataSourceTransactionManager 가 제공하는 기능도 대부분 지원한다

0개의 댓글