본문 바로가기

spring

spring aop+oracle procedure 에서의 transaction 처리..

이 포스팅은 개인적인 정리차원에서 적는것이다.

내가 이해하고 있는 정도이며, 맞는지 안맞는지에 대한건 나도 확신을 못한다.

이제껏 서핑을 통해 유추되는 결과이다.

 

spring에서의 aop처리는 ejb ejb-jar.xml에서 처럼 선언적으로 관리할 수 있다.

물른 프로그래밍적으로도 관리 할 수 있다.

침략적이지 않은 코드를 생성하기 위해서는 선언적으로 관리하는게 나아 보인다.

 

Declarative transaction management

Programmatic transaction management

 

다음과 같이 스프링에서 설정하면 되고.. 이것을 사용하기 위해서는 네임스페이스를 정의해주어야 한다.

 

<!-- from the file 'context.xml' -->

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:tx="http://www.springframework.org/schema/tx"

xsi:schemaLocation="

http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd

http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd

http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

 

<!-- this is the service object that we want to make transactional -->

<bean id="fooService" class="x.y.service.DefaultFooService"/>

 

<!-- the transactional advice (i.e. what 'happens'; see the <aop:advisor/> bean below) -->

<tx:advice id="txAdvice" transaction-manager="txManager">

<!-- the transactional semantics... -->

<tx:attributes>

<!-- all methods starting with 'get' are read-only -->

<tx:method name="get*" read-only="true"/>

<!-- other methods use the default transaction settings (see below) -->

<tx:method name="*"/>

</tx:attributes>

</tx:advice>

 

<!-- ensure that the above transactional advice runs for any execution

of an operation defined by the FooService interface -->

<aop:config>

<aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>

<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>

</aop:config>

 

<!-- don't forget the DataSource -->

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>

<property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>

<property name="username" value="scott"/>

<property name="password" value="tiger"/>

</bean>

 

<!-- similarly, don't forget the PlatformTransactionManager -->

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

<property name="dataSource" ref="dataSource"/>

</bean>

 

<!-- other <bean/> definitions here -->

 

</beans>

 

명심해야 될 것은 스프링 aop transaction처리는 내부적으로 SQLException이 발생할 때 DataAccessException으로 바꿔주고 이것을 이용해 처리가 일어난다는 점이다.

 

public abstract class DataAccessException extends NestedRuntimeException

와 같이 DataAccessException NestedRuntimeException을 상속받고 있고

public abstract class NestedRuntimeException extends RuntimeException

NestedRuntimeException RuntimeException을 상속받고 있다..

 

뭐 결론은 RuntimeException이라는 이야기이다..

이게 중요한 이유는 spring Unchecked Exception 의 경우 자동적으로 RollBack, Checked Exception의 경우 Commint을 시킨다는 점이다..

만일 Checked Exception에 대해서도 RollBack을 시키고 싶다면 다음과 같이 tx:method rollback-for라는 속성값을 주어야 한다..

<tx:method name="get*" read-only="false" rollback-for="NoProductInStockException"/>

 

.. 여기까진 스프링 aop를 이용한 transaction처리를 위한 간단한 설명이고..

만일 관계형 데이터베이스의 stored procedure를 이용할 경우 transaction은 어떻게 처리할까?

 

다음과 같이 procedure내에서 EXCEPTION절을 이용해 에러를 잡은 후 raise를 시키면 된다.

그럼 자바에서는 에러가 난 것을 catch하게 되고 SQLException을 만나게 된다.

그럼 위에서 설명한대로 aop transaction 프로세스가 작동하게 되는것이다.

물른 prodecure내에서는 commit이나 rollback을 하지 않고.. JDBC에게 맡기면 되는것이다. 분산 transaction을 구현했다면.. JTS, JTA가 맡게 될것이다.

EXCEPTION절에서 raise를 하였기 때문에 아래의 procedure output parameter를 만날수가 없다...; raise를 하지 않으면 output parameter는 잘 나오겠지만.. 결국 SQLException을 만나지 못하니 aop transaction또한 동작하지 않을것이다. output parameter에서 error code를 받고 싶으면 procedure안에서 transaction을 구현해야 될 것 같다.

 

 

CREATE OR REPLACE PROCEDURE sp_test_insert (

p_cd_code     IN       VARCHAR2,

p_nm_code     IN       VARCHAR2,

p_nm_column   IN       VARCHAR2,

p_id_updt     IN       VARCHAR2,

p_id_inpt     IN       VARCHAR2,

p_rowcount    OUT      NUMBER,

p_tempmsg     OUT      VARCHAR2,

p_errcode     OUT      NUMBER,

p_errmsg      OUT      VARCHAR2

)

IS

BEGIN

p_errcode := 0;

p_errmsg := '';

p_rowcount := 0;

p_tempmsg := '';

 

INSERT INTO st_ccmcm001

(cd_code, nm_code, nm_column, id_updt, id_inpt, date_updt, date_inpt

)

VALUES (p_cd_code, p_nm_code, p_nm_column, p_id_updt, p_id_inpt, SYSDATE, SYSDATE

);

 

p_rowcount := SQL%ROWCOUNT;

p_tempmsg := '임시 메세지..(st_ccmcm001 추가..)';

p_errcode := 3;

p_errmsg := '파라미터 : ' || p_cd_code;

--COMMIT;

EXCEPTION

WHEN NO_DATA_FOUND

THEN

NULL;

WHEN OTHERS

THEN

p_rowcount := TO_CHAR (SQL%ROWCOUNT);

p_tempmsg := '임시 메세지..(st_ccmcm001 추가..)';

p_errcode := SQLCODE;

p_errmsg := SQLERRM || '파라미터 : ' || p_cd_code;

--ROLLBACK;

--Consider logging the error and then re-raise

RAISE;

END sp_test_insert;

/

 

RAISE

The RAISE statement stops normal execution of a PL/SQL block or subprogram and transfers control to the appropriate exception handler. Normally, predefined exceptions are raised implicitly by the runtime system. However, RAISE statements can also raise predefined exceptions. User-defined exceptions must be raised explicitly by RAISE statements.

 

Usage Notes

PL/SQL blocks and subprograms should RAISE an exception only when an error makes it undesirable or impossible to continue processing. You can code a RAISE statement for a given exception anywhere within the scope of that exception.

 

When an exception is raised, if PL/SQL cannot find a handler for it in the current block, the exception propagates. That is, the exception reproduces itself in successive enclosing blocks until a handler is found or there are no more blocks to search. In the latter case, PL/SQL returns an unhandled exception error to the host environment.

 

Omitting the exception name in a RAISE statement, which is allowed only in an exception handler, reraises the current exception. When a parameterless RAISE statement executes in an exception handler, the first block searched is the enclosing block, not the current block.

 

 

 

다음의 transaction에 대한 spring의 중요한 log들이다.

 

시작..

2007. 10. 4. 오후 6:21:38 org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:203)

DEBUG : Returning cached instance of singleton bean 'cscm_01001_Service'

à spring bean을 가져옴

 

2007. 10. 4. 오후 6:21:38 org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:319)

DEBUG : Using transaction object [org.springframework.transaction.jta.JtaTransactionObject@1469a69]

à transaction object를 가져옴

 

2007. 10. 4. 오후 6:21:38 org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:347)

DEBUG : Creating new transaction with name [cs.cm.cm.service.CSCM_01001_Service.getSelectCodeList]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT

à 해당 method에 대해 transaction생성 propagation. isolation level 설정..

à propagation이란..?

Propagation behavior defines the boundaries of the transaction with respect to
the client and to the method being called.

 

2007. 10. 4. 오후 6:21:38 org.springframework.transaction.support.TransactionSynchronizationManager.initSynchronization(TransactionSynchronizationManager.java:222)

DEBUG : Initializing transaction synchronization

à transaction 싱크

 

2007. 10. 4. 오후 6:21:38 org.springframework.transaction.interceptor.TransactionAspectSupport.prepareTransactionInfo(TransactionAspectSupport.java:282)

DEBUG : Getting transaction for [cs.cm.cm.service.CSCM_01001_Service.getSelectCodeList]

à transaction 준비상태 오케이

 

Commit일 경우..

007. 10. 4. 오후 6:21:40 org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:312)

DEBUG : Completing transaction for [cs.cm.cm.service.CSCM_01001_Service.getSelectCodeList]

à commit 시작..

 

2007. 10. 4. 오후 6:21:40 org.springframework.transaction.support.AbstractPlatformTransactionManager.triggerBeforeCommit(AbstractPlatformTransactionManager.java:833)

DEBUG : Triggering beforeCommit synchronization

2007. 10. 4. 오후 6:21:40 org.springframework.transaction.support.AbstractPlatformTransactionManager.triggerBeforeCompletion(AbstractPlatformTransactionManager.java:846)

DEBUG : Triggering beforeCompletion synchronization

à commit

 

2007. 10. 4. 오후 6:21:40 org.springframework.transaction.support.TransactionSynchronizationManager.unbindResource(TransactionSynchronizationManager.java:193)

DEBUG : Removed value [org.springframework.jdbc.datasource.ConnectionHolder@4b29cf] for key [org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy@1ff8c2a] from thread [http-8080-1]

à resource unbinding..

 

2007. 10. 4. 오후 6:21:40 org.springframework.jdbc.datasource.DataSourceUtils.doReleaseConnection(DataSourceUtils.java:312)

DEBUG : Returning JDBC Connection to DataSource

à connecion 반환..

 

Rollback일 경우

2007. 10. 4. 오후 6:24:47 org.springframework.transaction.interceptor.RuleBasedTransactionAttribute.rollbackOn(RuleBasedTransactionAttribute.java:130)

DEBUG : Applying rules to determine whether transaction should rollback on org.springframework.dao.DataIntegrityViolationException: SqlMapClient operation; SQL [];\

2007. 10. 4. 오후 6:24:47 org.springframework.transaction.interceptor.RuleBasedTransactionAttribute.rollbackOn(RuleBasedTransactionAttribute.java:148)

DEBUG : Winning rollback rule is: null

2007. 10. 4. 오후 6:24:47 org.springframework.transaction.interceptor.RuleBasedTransactionAttribute.rollbackOn(RuleBasedTransactionAttribute.java:153)

DEBUG : No relevant rollback rule found: applying superclass default

à rollback..

 

2007. 10. 4. 오후 6:22:34 org.springframework.transaction.support.TransactionSynchronizationManager.unbindResource(TransactionSynchronizationManager.java:193)

DEBUG : Removed value [org.springframework.jdbc.datasource.ConnectionHolder@154e38b] for key [org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy@1ff8c2a] from thread [http-8080-1]

à resource unbinding..

 

2007. 10. 4. 오후 6:22:34 org.springframework.jdbc.datasource.DataSourceUtils.doReleaseConnection(DataSourceUtils.java:312)

DEBUG : Returning JDBC Connection to DataSource

à connecion 반환..


'spring' 카테고리의 다른 글

webAppRootKey  (2) 2008.03.06
spring+ibatis+jsp+jstl  (3) 2007.10.11
spring aop without java interface  (0) 2007.09.10
Spring 라이브러리  (0) 2007.08.21
flex data management services with spring  (0) 2007.08.20