본문 바로가기

Dev.../플밍 관련 자료

[펌] Weblogic8.1 Client에서 UserTransaction의 이용

작성자 :  최지웅

이번글을 쓰게 된 동기부터 살펴보아야겠다. 옆에 앉아계신 동료(이경민씨)가 현재 고객에 관련된
가입을 처리하고 있는데 실제 콜센터의 상담화면에서 가장 중요한 부분으로서 가입신청시
한꺼번에 10개의 table에 transaction을 걸어야하는 데서 테스트가 되어졌다.

문제는 javapattern에도 있는 ComponentDAO를 가지고 사용하게 되는데 문제점은 finally의
connection closing 때문에 transaction의 무결성을 보장못한다는 것이었다.

그렇다면 어떻게 처리를 할것인가? 웹로직을 사용하기는 하지만 시간상의 문제로 인하여
EJB는 사용하지 아래와 같은 구조를 사용하였다.

    JSP +++++++> Beans +++++++(VO)++++++++> DAO ++++++++++++> DB

그렇다면 각종 클래스들은 화면상에 뿌려지기 위하여 weblogic server상에 위치하게 되는데
문제는 UserTransaction을 가지고 있는 JNDI Name space에 접근하여 우리가 사용할 transaction을
얻어내는 방법이었다.

JDBC pure Tx를 사용하지 않고 UserTransaction을 사용하는 이유는 resource manager를 관리하는
측면에서 XA configure인 weblogic transaction manager를 사용하려는 의도에서 였다.


JDBC Pure Transaction의 처리방법

JDBC트랜잭션처리방법은 누구나 알고 있는 방법일 것이다.

    try{
        conn = getConnection();
        conn.setAutoCommit(false);
        ....
        ....
        conn.commit();
    }catch(Exception e) {
        conn.rollcack()
    }

보통 위의 방법으로 처리하게 되며 연결에 대하여 복잡한 statement가 나타나는 것은 물론이다.

그래서 생각하게 된것이 위에서 말한 JTS(Java Transaction Service)를 이용해보자는 것이다.
당근 EJB Context내부에서는 UserTransaction을 간단하게 얻어낼수 있지만 컨테이너외부에서
얻어내는 방법을 설명하고자 하는 것이다.

   
Weblogic Transaction Manager를 이용한 JTS의 사용

우선 간단하게 전략을 세워보도록 하자.

먼저 EJB server로부터 javax.transaction.UserTransaction을 달라고 요청해야 한다.
그런 후 begin(), commit(), rollback()을 사용하여 TxManager에게 적절한 상황의 처리를 하도록
할 수 있는데 UserTransaction은 현재 트랜잭션의 상태까지 모니터링할수 있는 방법을
제공하기 때문에 훨씬 더 효과적으로 분산DB의 제어가 가능하다는 특징을 가지고 있다.

우선 Weblogic server로부터 UserTransaction객체를 얻어내도록 하자.

/**
    * 해당 컨테이너로부터 UserTransaction을 얻어내어 반환한다.
    *
    * @param dbName lookup할 ut jndiname
    * @return javax.transaction.UserTransaction
    */
    public UserTransaction getUserTransaction() throws Exception{
        Context ctx = null;
        try{
            Properties props = new Properties();
            props.put(Context.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");
            props.put(Context.PROVIDER_URL,url);

            ctx = new InitialContext(props);
            UserTransaction ut = (UserTransaction) ctx.lookup("javax.transaction.UserTransaction");
            return ut;
        }catch(NamingException e){
            e.printStackTrace();
        }
        return null;
    }

위의 형태대로 하게 된다면 쉽게 원격 서버로부터 UserTransaction을 얻어낼수 있다. 우선
생소한것이 JNDI lookup에 관련한 이름이었을텐데, server의 name space에 있는 이름은
javax.transaction.UserTransaction를 사용하도록 하고 있다.

그러면 여러개의 메소드를 어떤 방식으로 호출해야 하나?

한가지 주의할 점은 connection을 관리하는 resource manager의 상위에 transaction manager가
위치하고 있다는 것만 알아두면 된다. 또한 Weblogic 8.1에서는 기본적인 XA Driver를 사용하므로
2-phase-commit이 되는건 당연하다.
TxManager는 여러개의 resource에 대하여 관장을 하게 되지만 같은 DB의 여러개의 테이블일경우는
하나의 resource manager를 통하여 해결해내야 한다.
그렇게 하지 않으면 weblogic의 서로 다른 resource manager를 사용하는 부분에서
SerialException이 발생한다.

즉 위의 Exception 이 발생하는 경우는

    txTest() {
        ut = getUserTransaction();
        ut.begin();
        try{
            methodA();
            methodB();
            ut.commit();
        }catch(Exception e) {
            ut.rollback();
        }
    }

    methodA(){
        ds.getConnection();
        pstmt execute();
        commit(); or rollback();
        conn.close();
    }

    methodB(){
        ds.getConnection();
        pstmt execute();
        commit(); or rollback();
        conn.close();
    }

와 같은 경우로서 이미 connection pool로의 객체반환으로 인하여 TxManager의 손아귀밖으로
밀려나버리기 때문에 다음 transaction은 기대할수 없게 되서 에러가 발생한다.

그렇다면 가입신청시 한DB의 10개 table transaction에 대해서는 어떻게 처리해야 하는가?

결국 한개의 conn, 즉 resource manager의 통제하에 들어가야 한다.

    public void testUserTransaction() throws Exception {
        UserTransaction ut = getUserTransaction();
        try{
            Connection conn = getConnection();
            ut.begin();
            testA(conn);
            testB(conn);
            ut.commit();
        }catch(SQLException e) {
            e.printStackTrace();
            try{
                ut.rollback();
            }catch(Exception x) { System.out.println(x);}
        }catch(SystemException e2) {
            e2.printStackTrace();
        }catch(NotSupportedException e3) {
            e3.printStackTrace();
        }catch(Exception ex) { System.out.println(ex); }
        System.out.println("TestEnded");
    }

    public void testA(Connection conn) throws SQLException {
        PreparedStatement pstmt = null;
        try{
            StringBuffer query = new StringBuffer();
            query.append("insert into acht002(table_no, table_name) values (2, 'AMT002') ");
            System.out.println("testAConnection : " + conn);

            pstmt = conn.prepareStatement(query.toString());
            pstmt.execute();
            System.out.println("TestA execute succeed");
        }catch(Exception e) {
            e.printStackTrace();
            throw new SQLException("testA exception!!");
        }finally{
            close(pstmt);
        }
    }
    public void testB(Connection conn) throws SQLException {
        PreparedStatement pstmt = null;
        try{
            StringBuffer query = new StringBuffer();
            query.append("insert into acht002(table_no, table_name) values (3, 'AMT0021') ");
            System.out.println("testBConnection : " + conn);
            pstmt = conn.prepareStatement(query.toString());
            pstmt.execute();
        }catch(Exception e) {
            e.printStackTrace();
            throw new SQLException("testB exception!!");
        }finally{
            close(pstmt);
        }
    }



위의 경우처럼 처리가 되어야 transaction을 보장받을 수 있다는 이야기이다.

간단하게 웹로직의 transaction처리에 대하여 살펴보았으며, 읽고 있는 여러분들도
쉽게 간과하고 넘어갈 성격의 문제가 아니다

 

 사이트 : http://www.javapattern.info/?doc=bbs/gnuboard.php&bo_table=sample_code&page=1&wr_id=3