2011년 5월 25일 수요일

iBATIS in Action (7/14)

1.1. 트랜잭션은 무엇인가?

가장 간단히 말하다면 트랜잭션은 보통 개의 묶음(group)으로, 성공하거나 실패해야 하는 여러 단계들을 포함하고 있는 작업의 단위이다. 트랜잭션 내에서 개의 단계가 실패하면 모든 단계들을 롤백해서 데이터가 일관성 있는 상태를 유지하도록 해야 한다.

7.1.1 간단한 은행 예제

트랜잭션이 중요한지 보여주는 일반적인 예제로는 은행의 자금 이체가 있다. 표에서 있는 것처럼 앨리스와 밥이 가지고 있는 개의 은행 계좌를 생각해보자.

이제 앨리스가 $100000 밥에게 보낸다고 해보자.

앨리스의 계좌로부터 출금하고 밥의 계좌로 입금하는 것은 항상 하나의 트랜잭션 내에서 이뤄져야만 한다. 그렇지 않을 입금이 실패하면 앨리스의 계좌 잔액은 $400000 되지만 밥은 여전히 $1000000만을 갖고 있게 된다. $100000 없는 곳으로 사라지게 된다.

트랜잭션은 입금이 실패하면 출금 이전의 상태로 돌려주는 것을 보장해준다. 롤백된 뒤에는 트랜잭션이 시작되기 이전과 동일한 상태로 데이터가 돌아가게 된다.

상당히 단순한 예제였다. 하지만 실생활에서 은행 시스템의 자금 이체는 훨씬 복잡하다. 이런 까닭에 많은 종류의 트랜잭션이 존재하며 트랜잭션이 훨씬 넓은 범위를 처리할 있도록 한다.

iBATIS 지원하는 가지 트랜잭션은 다음과 같다.

l 자동 트랜잭션 단순한 개의 SQL 구문은 명시적으로 구분된 트랜잭션을 필요로 하지 않는다.

l 로컬 트랜잭션 간단하고 범위가 좁은 트랜잭션으로 여러 SQL 구문을 포함하지만 단일 데이터베이스에서 수행된다.

l 글로벌 트랜잭션 복잡하고 범위가 넓은 트랜잭션으로 여러 SQL 구문을 여러 데이터베이스 혹은 잠재적으로 데이터베이스가 아닌 JMS(Java Message Service) 큐나 JCA(J2EE Connector Architecture) 커넥션 같은 다른 트랜잭션이 가능한 리소스상에서 실행된다.

l 사용자 정의 트랜잭션 – iBATIS 사용자가 원하는 대로 트랜잭션을 관리할 있는 커넥션을 제공한다.

7.1.2 트랜잭션의 특성 이해하기

트랜잭션의 특징은 ACID 라고 알려져 있다. ACID Atomity(원자성), Consistency(일관성), Isolation(격리성 혹은 고립성), Durability(영속성 혹은 내구성 지속성) 약어이다.

- 원자성(Atomicity)
트랜잭션 내의 모든 작업 단계가 개의 묶음으로써 모두 성공하거나 모두 실패하리라고 보장하는 특징을 원자성이라고 부른다. 이러한 특징이 없다면 데이터베이스가 하나의 단계만 실패해도 부정확한 상태로 남을 가능성이 생기게 된다.

- 일관성
좋은 데이터베이스 스키마는 무결성과 일관성을 보장해 주는 제약조건들을 정의한다. ACID 기능 중에서 일관성은 트랜잭션의 시작과 끝에서 데이터베이스가 일관성있는 상태로 남아 있을 것을 요구한다. 일관성있는 상태란 무결성 제약조건, 외래키 그리고 단일성 등을 포함한 모든 제약조건을 충족시키는 것으로 정의된다.

- 격리성
데이터베이스는 종종 여러 사용자들이 공유하는 중앙 집중적인 자원의 역할을 한다. 얼마나 많은 사용자가 있는지는 상관없이 이상의 사용자가 있다면, 각각 다른 사용자들간의 충돌로부터 트랜잭션을 유지하는 것이 매우 중요하게 된다. ACID 기능은 이를 격리성이라고 부른다.
격리 단계는 다음과 같다.
* READ UNCOMMITTED –
최하위 격리 단계이며 전혀 격리성이 없다. 비록 완전히 종료되지 않은 트랜잭년의 결과라도 그대로 테이블에서 데이터를 읽어들인다.
* READ COMMITTED –
격리 단계는 커밋되지 않은 데이터가 리턴되는 것을 막는다. 그렇지만 트랜잭션 수행 중에 트랜잭션 내부에서 레코드를 조회하였다면 비록 트랜잭션이 완료되지 않았다 하더라도 다른 사용자가 레코드를 수정하는 것을 무엇도 막을 없다. 이는 트랜잭션의 시작 부분과 부분에서 동일한 행에 각각 번씩 쿼리가 실행될 동일한 값을 리턴하리라고 보장하지 않음을 의미한다.
* REPEATABLE READ –
이는 오직 커밋된 데이터만을 읽고, 추가적으로 읽기 락을 쿼리를 수행중인 레코드에 걸어 둠으로써 트랜잭션이 종료될 때까지 다른 사용자가 해당 행을 수정하지 못하게 한다. 하지만 보호 단계는 어떤 쿼리를 사용하느냐에 전적으로 의존한다. 쿼리가 영역을 나타내는 절을 포함하고 있다면 영역 락을 확보하지 못할 것이고, 따라서 해당 레코드가 다른 사용자에 의해 변경되면 또다시 동일 트랜잭션 내에서 동일한 범위 퀴리가 실행될 서로 다른 결과를 리턴받게 된다.
*SERIALIZABLE –
이것은 사용 가능한 가장 높은 격리 단계이다. 본질적으로 이는 모든 트랜잭션을 순서대로 하나를 실행하고 나서 다음에 다른 것을 실행하여 마치 트랜잭션 간에 어떠한 충돌도 없는 것처럼 된다. 명백히 이는 동시 사용자가 많은 시스템에서 현저한 성능 저하를 유발한다. 왜냐하면 다른 작업이 모두 완료되기까지 모든 트랜잭션이 대기해야만 하기 때문이다.

- 영속성
데이터를 영구적으로 지속시킬 없는 데이터베이스는 차를 운전하지 못하게 막아둔 다리와 같다. ACID 기능 중에서 영속성은 데이터베이스가 트랜잭션이 성공적으로 수행되었다는 보고를 받으면 결과가 안전할 것을 요구한다. 트랜잭션이 종료된 후에 시스템에 오류가 발생해도 데이터는 안전해야만 한다.

1.2. 자동 트랜잭션

iBATIS 자동 커밋은 지원하지 않지만, 자동 트랜잭션을 지원한다. 이는 간단한 update 구문이나 쿼리를 개의 메서드 호출로 수행하면서 트랜잭션을 어떻게 구분할지에 대해서는 걱정할 필요가 없게 해준다. 해당 구문은 여전히 트랜잭션 내부에서 실행될 것이지만 명시적으로 트랜잭션을 시작하고 커밋하고 종료할 필요가 없다.

자동 트랜잭션 내부에서 SQL 구문을 실행하기 위해 특별히 필요한 것은 아무것도 없다. 그냥 구문을 실행하기만 하면 된다. 설정 방법은 다음 절에서 설명할 로컬 트랜잭션 설정과 동일하다. 다음의 예를 보자. queryForObject() update() 메서드 호출을 포함한 각각의 구문이 서로 다른 트랜잭션이라는 것에 주목하라.

public void runStatementsUsingAutomaticTransactions()

{

SqlMapClient sqlMapClient =

SqlMapClientConfig.getSqlMapClient();

Person p = (Person)

sqlMapClient.queryForObject("getPerson",

new Integer(9));

p.setLastName("Smith");

sqlMapClient.update("updatePerson", p);

}

1.3. 로컬 트랜잭션

로컬 트랜잭션은 가장 보편적인 트랜잭션 타입이다. 위에서 논한 자동 트랜잭션 조차도 로컬 트랜잭션의 형태이다. 로컬 트랜잭션은 단일 애플리케이션 안에 포함된 것이며, 관계형 데이터베이스 같은 트랜잭션을 지원하는 리소스를 오직 가지만 사용한다.( 개의 대에터베이스에 대한 트랜잭션만 관리한다.)

로컬 트랜잭션은 iBATIS SQL Maps XML 설정 파일에서 JDBC 트랜잭션 관리자로 설정된다. 다음은 로컬 트랜잭션 관리자 설정이 어떤 형태인지 보여준다.

type="JDBC">

Type=”JDBC” 속성으로 iBATIS에게 표준 JDBC 커넥션 API 사용해서 트랜잭션을 관리하라고 지정한다. SqlMapClient API 사용하여 트랜잭션을 구분짓는 것은 정말 쉽다. 다음은 트랜잭션을 구분짓는 전형적인 패턴을 보여준다.

public void runStatementsUsingLocalTransactions() {

SqlMapClient sqlMapClient =

SqlMapClientConfig.getSqlMapClient();

try {

sqlMapClient.startTransaction();

Person p =

(Person)sqlMapClient.queryForObject

("getPerson", new Integer(9));

p.setLastName("Smith");

sqlMapClient.update("updatePerson", p);

Department d =

(Department)sqlMapClient.queryForObject

("getDept", new Integer(3));

p.setDepartment(d);

sqlMapClient.update("updatePersonDept", p);

sqlMapClient.commitTransaction();

} finally {

sqlMapClient.endTransaction();

}

}

update 구문은 동일한 트랜잭션 범위 내에서 실행될 것이다. 따라서 하나가 실패하면 실패하게 된다.

트랜잭션을 구분짓는 메서드들을 감싸고 있는 try/finally 블록을 주의해서 봐야만 한다. 이러한 패턴의 코딩은 비록 오류가 발생하더라도 트랜잭션이 적절하게 종료될 것을 보장해준다. Try/finally 블록을 사용하는 것은 try/catch 블록을 사용하는 것보다 간단하고 효율적이다. 왜냐하면 개발자가 직접 예외를 잡을 필요가 없기 때문이다. 어차피 잡아봤자 예외를 가지고는 처리할 있는 없을 것이다.

1.4. 글로벌 트랜잭션

글로벌 트랜잭션은 로컬 트랜잭션보다 훨씬 넓은 트랜잭션 범위를 정의한다. 범위에는 다른 데이터베이스, 메시지 혹은 다른 애플리케이션조차도 포함될 있다. 그림은 이러한 시스템을 보여주고 복잡한 글로벌 트랜잭션이 사용되는 방법을 설명한다.

그림 7‑2. 글로벌 트랜잭션 범위의

7.4.1 능동(active) 혹은 수동(passive) 트랜잭션 사용하기

iBATIS 가지 방법, 능동적 혹은 수동적 방법 가지로 글로벌 트랜잭션에 참여할 있다. 능동적으로 참여하도록 설정하였다면 iBATIS 글로벌 트랜잭션의 컨텍스트를 찾아서 이를 적절하게 관리하려는 시도를 한다.

iBATIS 글로벌 트랜잭션에 수동적으로 참여하라고 설정하면 iBATIS 트랜잭션을 시작, 커밋 그리고 종료하라는 모든 명령을 간단히 무시해버리게 된다. iBATIS 오류가 발생하면 예외를 던지고 예외로 인해 트랜잭션이 롤백될 것이라고 가정하고 작동한다.

다음은 능동적인 참여와 수동적인 참여 방식으로 설정하는 것을 각각 보여준다.

---- 능동적인 참여

value="java:/ctx/con/someUserTransaction"/>

value="java:comp/env/jdbc/someDataSource"/>

---- 수동적인 참여

value="java:comp/env/jdbc/someDataSource"/>

경우에 JNDI 컨텍스트로부터 DataSource 어떻게 가져오는지 주목해서 보라. 개발자에게 필요한 커넥션은 글로벌 트랜잭션 관리자 범위에서 관리해야만 하기 때문에 DataSource 가져오는 방법이 실제 요구사항이 된다. JTA 경우에는 능동적으로 트랜잭션에 참여할 있도록 JNDI 컨텍스트가 UserTransaction 인스턴스도 필요로 한다. EXTERNAL 트랜잭션 관리자의 경우에는 UserTransaction 인스턴스가 불필요하다. iBATIS 외부 시스템이 트랜잭션 참여를 관리한다고 간주하기 때문이다.

7.4.2 트랜잭션을 시작하고 커밋하고 종료하기

트랜잭션을 글로벌로 정의하는 데는 가지 이유가 있다. 첫째로, 이를 통해 iBATIS 데이터베이스에 대한 커넥션과 같은 라른 리소르를 관리하게 함으로써 불필요하게 데이터소스에 커넥션을 요청하고 커넥션을 데이터소스로 돌려주는 일을 필요가 없게 한다. 둘째로, 이는 어떠한 코드 변경도 없이 로컬 트랜잭션과 글로벌 트랜잭션 사이를 왔다갔다 있게 해준다. 다음은 로컬 트랜잭션 예제에서와 완전히 동일한데 이는 로컬 트랜잭션이든 글로벌 트랜잭션이든 iBATIS에게는 어떠한 차이도 없음을 보여주기 위한 것이다.

public void runStatementsUsingGlobalTransactions() {

SqlMapClient sqlMapClient =

SqlMapClientConfig.getSqlMapClient();

try {

sqlMapClient.startTransaction();

Person p =

(Person)sqlMapClient.queryForObject

("getPerson", new Integer(9));

p.setLastName("Smith");

sqlMapClient.update("updatePerson", p);

Department d =

(Department)sqlMapClient.queryForObject

("getDept", new Integer(3));

p.setDepartment(d);

sqlMapClient.update("updatePersonDept", p);

sqlMapClient.commitTransaction();

} finally {

sqlMapClient.endTransaction();

}

}

7.4.3 글로벌 트랜잭션이 필요한가?

즉시 대답을 하라면 이렇게 말할 것이다. “아마도 필요 없을 것이다”. 일반적으로 글로벌 트랜잭션을 사용하기 위해서는 크나큰 오버헤드를 감수해야 한다. 이는 대개 글로벌 트랜잭션이 분산 환경하에 있으며 로컬 트랜잭션에 비해 많은 네트워크 트래픽과 관리를 필요로 하기 때문이다. 만약 아무런 비용도 들지 않는다면 모든 경우에 글로벌 트랜잭션을 사용하면 것이다. 성능 저하와 함께 단순하게도 글로벌 트랜잭션은 설정하기가 어렵다. 이를 위해 많은 인프라스트럭처와 많은 소프트웨어 그리고 많은 리소스가 필요하다. 그러므로 비록 컨테이너가 관리하는 트랜잭션을 사용할 때조차도 글로벌 트랜잭션이 필요할 때에만 사용해야 함을 명심하라. 대부분의 좋은 어플리케이션 서버는 매우 간단하게 분산 트랜잭션을 활성화하거나 비활성화하는 옵션을 설정할 있다.

1.5. 사용자 정의 트랜잭션

지금까지 트랜잭션 관리 방법으로도 개발자의 수요를 충족시키주지 못했다면 개발자 스스로 트랜잭션을 관리할 있는 옵션 가지가 있다. 번째는 트랜잭션 관리자를 직접 작성하는 것으로 iBATIS 인터페이스를 사용하여 작성한 코드를 SQL Map 설정 파일에 끼워 넣으면 된다. 접근 방법은 12장에서 알아볼 것이다. 번째 접근 방법은 단순히 iBATIS JDBC Connection 인스턴스를 받아서 그것을 통해 개발자가 커넥션과 트랜잭션에 대해 완전한 제어권을 행사하는 것이다. SqlMapClient Connection 객체를 전달하는 방법은 가지가 있다. 첫째는 setUserConnection (Connection) 메서드로 다음에서 있다.

public void runStatementsUsingSetUserConnection() {

SqlMapClient sqlMapClient =

SqlMapClientConfig.getSqlMapClient();

Connection conn = null;

try {

conn = dataSource.getConnection();

conn.setAutoCommit(false);

sqlMapClient.setUserConnection(conn);

Person p =

(Person)sqlMapClient.queryForObject

("getPerson", new Integer(9));

p.setLastName("Smith");

sqlMapClient.update("updatePerson", p);

Department d =

(Department)sqlMapClient.queryForObject

("getDept", new Integer(3));

p.setDepartment(d);

sqlMapClient.update("updatePersonDept", p);

conn.commit();

} finally {

sqlMapClient.setUserConnection(null);

if (conn != null) conn.close();

}

}

번째 방법으로 openSession (Connection) 사용하는 것이 있다. 방법이 나은 방법으로 iBATIS 리소스 관리를 있기 때문이다. 다음은 openSession() 어떻게 이용하는지 보여준다.

public void runStatementsUsingSetUserConnection() {

SqlMapClient sqlMapClient =

SqlMapClientConfig.getSqlMapClient();

Connection conn = null;

SqlMapSession session = null;

try {

conn = dataSource.getConnection();

conn.setAutoCommit(false);

session = sqlMapClient.openSession(conn);

Person p =

(Person)session.queryForObject("getPerson",

new Integer(9));

p.setLastName("Smith");

session.update("updatePerson", p);

Department d =

(Department)session.queryForObject

("getDept", new Integer(3));

p.setDepartment(d);

session.update("updatePersonDept", p);

conn.commit();

} finally {

if (session != null) session.close();

if (conn != null) conn.close();

}

}

코드가 멋지다고 보긴 어렵다. 이는 개발자가 스스로 트랜잭션 관리자를 직접 작성하도록 하는 좋은 이유가 되기도 한다. 게다가 여전히 EXTERNAL 타입의 트랜잭션 관리자를 정의해야 하며 최소한 SIMPLE DataSource 제공해줘야만 한다. 그렇지 않으면 적재 지연 같은 특정한 기능들이 제대로 작동하지 않을 것이다.

위와 같은 접근 방법은 되도록 피하라. 그리고 항상 개발자 스스로 트랜잭션 관리자를 작성해야 하는지 여부를 고려해 보라.

1.6. 트랜잭션 구분하기

간단한 경험에서 나온 법칙을 제시한다.

트랜잭션의 범위는 가능한 넓게 만들어야 하지만, 단일 사용자 액션의 범위를 벗어나서까지 확장되어서는 된다.”

예를 들어 애플리케이션에서 사용자가 폼을 제출하는 버튼을 클릭했을 트랜잭션의 영역이 즉시 시작된다. 하지만 사용자의 브라우저에 응답 페이지가 출력되는 순간에 트랜잭션도 완료되어야 한다. 리치 클라이언트 애플리케이션에서도 규칙은 일반적으로 동일하게 적용된다. 트랜잭션은 단일 사용자 작업에 포함된것과 관련된 모든 작업을 포함한다. 이는 일반적으로 번의 버튼 클릭과 일치한다. 트랜잭션 범위를 결정하는 것에 대해 생각해 있는 다른 방식으로 이런 것도 있다.: 사용자가 트랜잭션을 열어 두거나 완료시키지 않고서는 그의 컴퓨터에서 절대로 떠날 없게 하라.

그러면 , 정확히 어디서 트랜잭션을 시작하고 종료하라는 것인가? 이상적으로는 어디서도 트랜잭션을 시작하지 않는 것이다. 다시 말해서 컨테이너가 스스로 트랜잭션을 관리하도록 놔두는 것이다. 컨테이너에 의해 트랜잭션이 구분지어지도록 애플리케이션의 설정을 선언적으로 하면 된다. 애플리케이션 서버에서 무상태 세션 빈즈(stateless session beans) 사용하거나 혹은 Spring 프레임워크 같은 경량 컨테이러를 사용한다면 선언적으로 트랜잭션을 설정할 있다.컨테이너가 트랜잭션이 적절하게 시작되고 커밋되고 종료될 것을 보장해 줄것이다. iBATIS 트랜잭션을 위한 통합적인 프로그래밍 모델을 제공해 주는데, 이는 비록 컨테이너가 스스로 실제 트랜잭션을 구분짓는다 하여도 개발자가 startTransaction(), commitTransaction(), endTransaction() 자신의 코드에서 사용할 있고 사용해야만 함을 의미한다. 이를 통해 개발자는 자신의 퍼시스턴스 계층 코드를 컨테이너 외부로 포팅해도 상대적으로 깔끔한 트랜잭션 구성을 유지할 있음을 의미한다. EXTERNAL 트랜잭션 관리자로 설정하면 iBATIS 컨테이너가 트랜잭션을 제어하도록 놔둘 것이다.

만약 여러분이 컨테이너가 트랜잭션을 관리하도록 허가할 있는 직급에 있지 않다면, 여러분 스스로 트랜잭션을 관리할 있다. 계층화된 아키텍처에서는 트랜잭션을 시작하고 종료하는 수많은 방법이 존재한다. 여러분이 무엇을 선택하든지에 관계없이 일관성이 있어야만 한다는 것이 중요하다. 다음의 계층화된 아키텍처에 관한 그림을 참조하여 프레젠테이션 계층, 비즈니스 로직 계층 혹은 퍼시스턴스 계층에서 트랜잭션을 구분 지을 있다.

그림 7‑3 계층화된 아키텍처

계층 각각에서 어떻게 트랜잭션을 구분짓는지 자세히 알아보자.

7.6.1프레젠테이션 계층에서 트랜잭션 구분 짓기

프레젠테이션 계층에서 트랜잭션을 구분짓는 것은 매우 크고 넓은 범위로 장시간 지속되는 트랜잭션을 만들어 버린다. 하지만 대단히 안전하고 최고의 데이터 무결성을 제공하며 구현하기 쉽다. 서블릿 필터나 플러그인을 사용해서 프레젠테이션 계층에서 트랜잭션을 시작하고 커밋하고 종료시킬 있다. 방식의 단점은 장시간 유지되는 트랜잭션 때문에 성능저하를 일으킬 있다는 점이다. 또한 일반적으로 프레젠테이션 계층은 퍼시스턴스 계층과 멀리 떨어져 있기 때문에 정확히 프레젠테이션 계층에서 트랜잭션을 시작해야 하는지 알기가 어렵다. 결과적으로 많은 트랜잭션들이 불필요하게 오랜 시간 지속하고 나서 종료하게 된다. 다른 단점으로, 애플리케이션이 사이트나 서비스 API 그리고 리치 클라이언트 애플리케이션 처럼 다중 사용자 인터페이스를 가지고 있을 경우 UI마다 트랜잭션 범위에 관한 구현을 또다시 해야 하고 이로 인해 일관성을 깨뜨리는 일이 생기게 된다.

프레젠테이션 계층은 퍼시스턴스 계층에서 너무 멀리 떨어져 있기 때문에 논리적으로 트랜잭션을 구분 짓는 위치로는 부적합하다. 그냥 퍼시스턴스 계층으로 가는 어떨까?

7.6.2 퍼시스턴스 계층에서 트랜잭션 구분 짓기

대부분의 사람들이 자연스럽게 퍼시스턴스 계층에서 트랜잭션을 구분 지어야 한다고 생각한다. 하지만 의외로 이것은 좋지 않은 선택이다. 이유는 간단하다. 좋은 퍼시스턴스 계층은 어느 정도 좁은 범위의 메서드들로 이루어져 있고 결합도가 낮으며 데이터 베이스 작업에 관해 높은 응집도를 지니고 있다. 실제로, 트랜잭션에서 개의 퍼시스턴스 계층 메서드만을 사용하는 경우는 흔치 않다. 이런 경우에는 사실상 트랜잭션이 별로 필요가 없다. 몇몇 유용한 비즈니스 기능을 수행하기 위해서 여러 개의 데이터 베이스 작업을 하나의 묶음으로 실행해야 하는 경우가 일반적이다. 이런 면에서 , 퍼시스턴스 계층은 너무 세밀하게 나뉘어져 있다. 퍼시스턴스 작업들은 함께 묶어주는 추가적인 계층을 하나 만드는 것도 가능하며 이를 통해 트랜잭션을 구분짓는 것도 괜찮을 것이다. 하지만 이러한 작업은 불필요한 일을 해야하고, 투명성을 완벽하게 유지하려면 설계가 너무 복잡해지게 된다.

7.6.3 비즈니스 로직 계층에서 트랜잭션 구분 짓기

프레젠테이션 계층과 퍼시스턴스 계층을 빼고나면 우리에게 남은 것은 아마도 그럼 여기가 맞겠거니 하고 예상했늘 비즈니스 로직 계층이다. 이것은 상황에 따라 다르다. 애플리케이션의 요구사항에 따라서 가지 계층 어느 것이라도 옳은 선택이 있다. 하지만 경험에 비추어볼 일반적으로 비즈니스 로직 계층이 트랜잭션을 구분짓는데 매우 좋은 선택임이 사실이다. 이는 EJB 개발자나 Spring 프레임워크 등의 개발자들에게 보편적이고 익숙한 방식이다. 무상태 세션 빈즈는 비즈니스 로직의 컴포넌트로 보통 빈즈를 설정할 트랜잭션에 관한 요구사항도 여기서 선언한다. 유사하게 Spring 트랜잭션을 거의 모든 메서드에 선언할 있는데 DAO 대신 보통은 비즈니스 로직 컴포넌트에 선언한다. 이것은 단순하게도 개의 비즈니스 작업이 이상의 DAO 필요로 하기 때문에 그렇다.다른 기술적인 글에 따르면 , 비즈니스 로직 계층에서 트랜잭션을 구분 지으면, 개의 일관성 있는 트랜잭션 모델을 유지하면서도 애플리케이션에 여러 인터페이스를 사용할 있게 된다.

그럼 여러분은 어떤 생각이 드는가? 비즈니스 로직에서 트랜잭션을 구분 짓는 것이 자연스럽게 느껴지는가? 그래야만 한다. 아키텍트, 데이터베이스 관리자 그리고 개발자들은 보통 트랜잭션을 기술적인 개념으로 이해한다. 결국 트랜잭션은 데이터베이스가 수행하는 어떤 이라는 것이다. 하지만 트랜잭션은 그것보다 상위 단계의 것이라고 보아야만 한다. 트랜잭션의 범위는 비즈니스 작업이나 비즈니스 기능을 포함한다. 이는 그저 데이터 베이스의 기능쯤으로 간주해서는 되는 것이다. 더욱이 데이터 베이스가 유일하게 트랜잭션을 지원하는 인프라스트럭처도 아니다. 우리의 비즈니스 작업은 메인 프레임 접속기를 통해서 수행될 수도 있고 메시지 큐로 메시지를 발송할 있으며 혹은 사람의 개입(워크플로우를 생각해보라.) 트랜잭션의 일부로 포함할 수도 있다. 그리고 모든 것이 분명히 트랜잭션을 지원한다.

논리적으로나 기술적인 경우 모두에게 비즈니스 계층은 트랜잭션을 구분 짓는 완벽한 장소이다.

댓글 없음:

댓글 쓰기