본문 바로가기

스프링

[spring] JTA 트랜잭션 2

1.     Com.springsource.junit : junit

2.     Commons-logging : Spring-context가 사용

3.     Mysql-connector : Mysql JDBC

4.     Spring-aop : 스프링 기능 자체의 aop

5.     Spring-bean : 스프링 빈을 활용하는 경우 필요. 스프링의 XML 설정파일과 자바 애노테이션을 파싱 하는데 필요한 클래스 포함

6.     Spring-context : 스프링 코어를 확장한 많은 클래스가 들어 있는데 모든 클래스는 EJB, JNDI(Java Naming Directory Interface), JMX용 클래스와 연동하는데 applicationcontext기능을 사용해야 하며 스프링 리모팅 클래스, 동적 스크립팅 언어(제이루비, 그루비등)와 연동하는 클래스, 빈 유효성검증(JSR-303) API, 스케줄링을 하는 클래스도 포함되어 있다.

7.     Spring-core : 모든 스프링 모듈에서 필요한 모듈. 다른 스프링 모듈에서 사용하는 공통 클래스가 포함됨.

8.     Spring-dao : EmpltyResultDataAccessException 등 사용을 위한 jar

9.     Spring-expression : 스프링 표현언어(SpEL) 지원 클래스 포함.

10.   Spring-jdbc : 스프링이 지원하는 jdbc.

11.   Spring-test.jar

-       @RunWith : Junit 프레임워크의 테스트 실행방법을 확장시 사용.

-       SpringJUnit4ClassRunner : 어플리케이션컨텍스트를 만들고 관리하는 확장 클래스

-       @ContextConfiguration(경로) : 자동으로 만들어줄 어플리케이션 컨텍스트 설정파일

12.   Spring-tx : DuplicateKeyException.class 파일 존재 및 스프링 트랜잭션

 

이전 트랜잭션은 훌륭하지만, 여러 개의 DB에 넣는 작업을 해야할 때는 문제가 발생한다. JDBC의 로컬 트랜잭션은 하나의 DB Connection에 종속되기 때문이다. 따라서, 글로벌 트랜잭션 방식을 사용해야한다.

 

글로벌 트랜잭션

-       여러 개의 DB가 참여하는 작업을 하나의 트랜잭션으로 만들 수 있다.

-       JMS와 같은 트랜잭션 기능을 지원하는 서비스도 트랜잭션에 참여시킬 수 있다.

JTA(Java Transaction API)

-       자바는 JDBC 외에 이런 글로벌 트랜잭션을 지원하는 트랜잭션 매니저를 지원하는 API를 제공하고 있다.

-       사용방법

기존 방법대로 DB JDBC, 메시징 서버라면 JMS 같은 API를 사용해서 필요한 작업을 수행한다. , 트랜잭션은 JDBCJMS API를 사용해서 직접 제어하지 않고, JTA를 통해 트랜잭션 매니저가 관리하도록 위임한다.

>  트랜잭션 매니저는 DB와 메시징 서버를 제어하고 관리하는 각각의 리소스 매너저와 XA 프로토콜을 통해 연결된다. 이를 통해 트랜잭션 매니저가 실제 DB와 메시징 서버의 트랜잭션을 종합적으로 제어할 수 있게 되는 것이다.

 

하나 이상의 DB가 참여하는 트랜잭션을 만들려면 JTA를 사용해야 한다.

XA프로토콜 : 분산 트랜잭션을 위한 표준 프로토콜

 

위처럼 적용하면 트랜잭션 처리할 수 있지만, UserService의 코드를 수정해야하는 문제가 발생한다.

, 어느 회사의 다중 DB사용을 위한 글로벌 트랜잭션을 필요로 하는 곳을 위해서는 JTA를 이용한 트랜잭션 관리코드를 적용해야 하므로, UserService는 자신의 로직이 바뀌지 않았음에도 기술 환경에 따라서 바뀌는 코드가 돼버리고 말았다.

>  또다른 회사는 하이버네이트를 이용한다고 연락이 왔다. UserServiceUserDaoDI를 통해 연결되어 있기 때문에 얼마든지 구현이 가능하기에 괜찮지만, 하이버네이트를 이용한 트랜잭션 관리 코드는 JDBCJTA의 코드와는 또 다르다. 그러므로 트랜잭션 경계설정 코드로 또 변경해야하는 문제가 발생한다.

 

트랜잭션을 도입하기 전 UserServiceUserDao 인터페이스에만 의존하는 구조였다. 그래서 구현기술이 JDBC에서 하이버네이트나 여타 기술로 바뀌더라도 UserService 코드는 영향을 받지 않았다. 전형적인 OCP(개방폐쇄원칙: 확장에는 열려있고, 변경에는 폐쇄)원칙을 지키는 코드였다. 문제는 트랜잭션 코드가 등장하면서 UserServiceConnection을 사용함으로 인해 UserDaoJdbc에 간접적으로 의존하는 코드가 되어 버렸다.

JDBC라는 추상화 기술이 있기 때문에 자바개발자는 DB종류에 관계없이 일관된 방법으로 데이터 엑세스 코드를 작성할 수 있다. 트랜잭션 처리해도 추상화를 도입해볼 수 있지 않을까?

 

스프링이 제공하는 트랜잭션 추상화 계층구조

 

 

 

 

 

 

 

 

 

 

 

 

 

28. PlatformTransactionManager

> JDBC 트랜잭션 추상 오브젝트 생성. 스프링이 제공하는 트랜잭션 경계설정을 위한 추상 인터페이스.

> JDBC의 트랜잭션을 이용한다면 해당 인터페이스를 구현한 DataSourceTransactionManager를 사용하면 된다. 이 구현 객체는 JdbcTemplate에서 사용될 수 있는 방식을 트랜잭션을 관리해준다.

> 사용할 DBDataSource를 생성자 파라미터로 넣으면서 DataSourceTransactionManager 오브젝트를 만든다.

> 이것으로 시작한 트랜잭션은 앞서(024.서비스추상화_트랜잭션편) 살펴본 트랜잭션 동기화 저장소(TransactionSynchronizationManager)에 저장된다.

> 그래서 위 인터페이스를 통해 시작한 트랜잭션은 UserDaoJdbcTemplate 안에서 사용된다.

29. getTransaction() 메소드의 호출로 트랜잭션 시작된다. 이렇게 시작된 트랜잭션은 TransactionStatus 타입의 변수에 저장된다. TransactionStatus는 트랜잭션에 대한 조작이 필요할 때 PlatformTransactionManager 메소드의 파라미터로 전달해주면 된다.

> , 트랜잭션 조작(commit, rollback)이 필요한 경우 파라미터로 status를 전달하면됨.

 

 

 

 

 

 

80. UserServicetestService를 만들어도 동일한 dataSource를 수동DI 스프링의 장점

87. fail : 테스트가 catch에 안 걸리고 성공된다면 테스트를 실패처리함

88. 예외를 잡았지만 아무 처리도 하지 않았으므로 걍 지나감

 

만약 트랜잭션 추상화API를 적용한 UserService 코드를 JTA를 이용하는 글로벌 트랜잭션으로 변경하려면 위와 같이 UserService 를 변경해주면 된다. 하이버네이트라면 new Hibernate~를 사용하면 된다.

getTransaction(), commit(), rollback() 메소드를 사용한 코드는 전혀 손댈 필요가 없다.

하지만, UserService처럼 어떤 트랜잭션 매니저 구현클래스를 사용해야할지 구체적인 구현 클래스를 알고 있는 것은 DI원칙에 위배된다. 스프링 빈으로 등록하고, DI방식으로 사용하게 해야한다.

PlatformTransactionManager의 경우 관례적으로 transactionManager라는 이름을 사용한다. 스프링이 PlatformTransactionManager라는 긴 이름을 붙인 이유는 JTATransactionManager 인터페이스와 혼동되지 않도록 하기 위해서일 뿐이다.

 

Bean 등록시 유의사항

>  여러 스레드에서 동시에 사용해도 괜찮은가? 즉 상태정보를 가지고 있지 않은가 따져볼것.( 026.SpringSingletonScope편 참조)