spring에서 디비접근시에 트랜잭션을 관리하는 방법은 아주 추상화가 잘 돼 있습니다. 바로 @Transactional 을 사용해주면 트랜잭션이 되기 때문입니다.
이 마법의 내부를 공부해 봤습니다.
import java.sql.Connection; Connection connection = dataSource.getConnection(); // (1)
try (connection) { connection.setAutoCommit(false);
// (2)
// execute some SQL statements...
connection.commit();
// (3)
} catch (SQLException e)
{ connection.rollback(); // (4)
}
@Transactional(propagation=TransactionDefinition.NESTED, isolation=TransactionDefinition.ISOLATION_READ_UNCOMMITTED)
트랜잭션 격리 레벨이 중첩되었을 때의 동작보다 격리레벨이 아래와 같은 기본 jdbc코드로 정리된다는 것을 아는게 중요하다.
프로그래밍스타일 트랜잭션 관리 방법.
@Service
public class UserService {
@Autowired private
TransactionTemplate template;
public Long registerUser(User user) {
Long id = template.execute(status -> {
// SQL 실행
// ex) inserts the user into the db and returns the autogenerated id
return id;
});
}
}
위와같이 TransactionTemplate를 사용하거나 직접 PlatfromTransactionManager를 이용하면 된다.
프로그래밍적인 방식은 잘 사용안한다. 예시일뿐
public class UserService {
public Long registerUser(User user) {
Connection connection = dataSource.getConnection(); // (1)
try (connection) { connection.setAutoCommit(false);
// (1)
// execute some SQL that e.g.
// inserts the user into the db and retrieves the autogenerated id
// userDao.save(user); <(2)
connection.commit(); // (1)
} catch (SQLException e) {
connection.rollback();
// (1)
}
}
}
위와같이 @Transactional을 사용하면 위에 트랜젝션을 관리하는 코드가 자동으로 삽입이되면서 트렌젝션 관리를 해주게 됩니다.
스프링은 기본적으로 ioc를 사용한다.
그럼 위 UserService를 인스턴스화 할때 해당 클래스의 트랜잭션 프록시도 인스턴스화 하는것입니다.그렇게 CGLIB라이브러리(인터페이스가 없는 클래스를 프록시패턴적용시사용하는 라이브러리)
의 도움을 받아서 프록시를 통하는 방식으로 마치 코드를 넣은 것 처럼 동작하게 한다.
모든 트랜잭션(open,commit,close) 를 처리하는 것은 프록시 자체에서가 아니라 트랜잭션 매니저에 위임하여 처리하는 것!
1. 스프링은 @Transactional 을 발견하면 그 빈의 다이나믹 프록시를 만들어준다.
2. 그 프록시 객체는 트랜잭션 매니저에 접근하고 트랜잭션이나 커넥션을 열고 닫도록 요청한다
3. 트랜잭션 매니저는 JDBC 방식으로 코드를 실해해 준다.
트랜잭션 전파 레벨에는 여러가지가 있다.
데이터베이스의 격리 수준은 복잡한 주제다. 트랜잭션중에 격리 수준을 전환할때 데이터베이스나 jdbc드라이버에서 기능이 지원되는지를 분명하게 먼저 확인해야할 필요가
있습니다. 프록시를 통하지 않고 다시말해 ioc에게 제어권을 넘기지 않고 @Transactional이 붙은 내부 메소드를 호출하면 ioc에 등록되기전 가로첼때 프록시가
작동할 수 없기때문에 적용되지 않습니다.
두트랜 잭션 통합하는 문제를 고치는 방법은 DataSourcePlatformTransactionManager 대신 HibernateTransactionManager 를 쓰는 것이다.
JPA를 통해 Hibernate를 사용한다면 JpaTransactionManager를 사용하면 된다.
HibernateTransactionManager 는 하이버네이트를 직접 사용할 때 트랜잭션을 관리하고 JpaTransactionManager는 jpa를 통해서 간접적으로 사용할 때 트랜
잭션 관리를 합니다.
스프링에서는 spring-boot-starter-data-jpa같은 라이브러리를 쓰면 자동으로 JpaTransactionManager를 씁니다.
어떤 기술을 쓰든 jdbc 기본 (getConnection(), setAutoCommit(false), commit())이라는 것이다.
트랜잭션이 적용되는 method에서는 try - catch문을 사용하시면 안됩니다.
RuntimeException이 발생해 롤백이 되어야하는 상황에서 catch문에 걸려버리면 spring transaction rollback로직까지 실행되지 않고 끝나버리기 때문에
주의하셔야합니다.