XA의 사용

본 장에서는 분산 트랜잭션(Distributed Transaction)를 처리하는 데 사용되는 XA를 설명합니다.

분산 트랜잭션

하나의 데이터베이스 인스턴스 내에서는 한 트랜잭션으로 묶인 여러 개의 SQL 문장이 모두 커밋되거나 롤백됩니다. 네트워크로 연결된 여러 개의 데이터베이스 인스턴스가 참여하는 트랜잭션에서도 마찬가지로 각각 다른 데이터베이스 인스턴스에서 수행한 SQL 문장이 모두 동시에 커밋되거나 롤백될 수 있는 방법이 필요합니다.

이렇게 여러 개의 노드 또는 다른 종류의 데이터베이스가 참여하는 하나의 트랜잭션을 분산 트랜잭션(Distributed Transaction)이라고 합니다.

참고

분산 트랜잭션에 대한 자세한 내용은 "Tibero 관리자 안내서"를 참고합니다.

Two-phase commit

Tibero는 X/Open DTP(Distributed Transaction Processing) 규약의 XA를 지원합니다.

XA는 Two-phase commit를 이용하여 분산 트랜잭션을 처리합니다. 분산 환경에서 트랜잭션의 무결성을 보장하기 위해서 사용하는 커밋 방법은 Two-phase commit입니다.

보통 두 개 이상의 노드가 특정 트랜잭션을 함께 수행하고 있다면, 일반적으로 사용자의 요청을 받아 트랜 잭션을 시작한 노드가 코디네이터가 됩니다. TP-Monitor가 있는 시스템일 경우 TP-Monitor가 그 역할을 합니다.

Two-phase commit mechanism은 크게 두 단계로 작업이 이루어집니다.

위 예제를 기준으로 Two-phase commit를 설명하면 다음과 같습니다.

  1. First Phase(또는 Prepare Phase)

커밋을 준비하는 단계로 다음의 세부 과정으로 실행됩니다.

단계
설명

send "prepare"

각 노드는 코디네이터 노드로부터 커밋을 준비하라는 메시지를 받는다.

reply "prepared"

각 노드는 메시지를 받은 후 복구를 위해 로그 등에서 커밋이 가능한지를 검사한 다. 또는 필요에 따라 자체적으로 롤백을 한다. 만약 커밋이 가능하다면 최종으로 커밋한 로그를 제외한 모든 작업을 수행한 후 커밋이 준비되었다는 메시지를 코디 네이터 노드로 전달한다.

  1. Second Phase(또는 Commit Phase)

실제로 커밋한 기록을 저장하는 단계로 Second Phase은 다음의 세부 과정으로 실행됩니다.

단계
설명

send "commit"

코디네이터 노드는 모든 노드에서 커밋이 되었다는 메시지를 전달받는다. 이를 확인한 후 실제로 커밋을 실행하라는 메시지를 각 노드에 보낸다.

reply "committed"

각 노드는 커밋을 기록한 후 커밋이 완료되었다는 메시지를 코디네이터 노드로 전달한다.

다음은 Two-phase commit의 일반적인 예를 나타내는 그림입니다.

[그림 1] Two-phase commit의 일반적인 예

In-doubt 트랜잭션

Two-phase commit mechanism에 의해 첫 번째 prepare 메시지를 받으면 데이터베이스는 분산 트랜잭션에 해당하는 리소스를 잠금을 설정하거나 로그를 남김으로써 커밋할 준비를 합니다. 그런데 prepare까지 마친 상태에서 네트워크의 이상으로 다음 메시지(커밋 또는 롤백)를 받지 못하는 경우가 발생할 수 있습니다.

이 경우에 데이터베이스는 해당 트랜잭션을 커밋해야 할 지 롤백해야 할지 판단할 수 없습니다. 따라서 다음 메시지가 올 때까지 prepare된 리소스에 잠금 설정을 한 채로 기다리게 됩니다. 이렇게 prepare는 되었는데 그 다음 메시지를 받지 못한 채 리소스만 소유하고 기다리고 있는 트랜잭션을 In-doubt 트랜잭션이라 합니다.

예를 들어 First Phase에서 코디네이터 노드가 커밋을 준비하라는 메시지를 보냈음에도 불구하고 어떤 특정 노드의 서버가 다운되었거나 네트워크 상태가 불안정하여 그 메시지를 못 받았거나 또는 메시지는 받았지만 커밋이 준비되엇다는 답변을 받지 못했을 때 In-doubt 트랜잭션이 발생합니다.

이러한 경우 코디네이터 노드는 다른 모든 노드의 응답을 받았어도 한 노드의 응답을 받지 못했으므로, 이 트랜잭션을 In-doubt 트랜잭션으로 표시하고, 모든 노드에 Second Phase의 커밋 명령을 실행하라는 메시지를 보내는 대신에 롤백하라는 메시지를 보냅니다.

예를 들면 다음 그림과 같습니다.

[그림 2] In-doubt 트랜잭션이 발생하는 예

또한 모든 노드에서 커밋이 준비되었다는 메시지를 받아서 확인했더라도 그 이후에 코디네이터 노드가 보낸 Second Phase의 커밋 명령을 실행하라는 메시지를 특정 노드가 받지 못했거나, 그 노드가 커밋 명령은 잘 수행하였으나 커밋이 완료되었다는 응답을 코디네이터 노드에 전달하지 못했을 수도 있습니다. 이와 같은 경우도 마찬가지로 In-doubt 트랜잭션으로 분류됩니다.

트랜잭션은 커밋이나 롤백이 확정되지 않은 채로 데이터베이스 리소스에 대해 잠금(Lock)이 설정된 상태 즉 정체(Pending) 상태가 됩니다. 따라서 이를 해결하려면 첫 번째 경우처럼 코디네이터 노드가 자동으로 전체 트랜잭션을 롤백해 준다거나 DBA가 이러한 트랜잭션을 추출하여 자체적으로 판단하여 수동으로 처리 할 수 있습니다.

이때 DBA가 In-doubt 트랜잭션을 추출하기 위해 정보를 볼 수 있는 테이블과 뷰로는 VT_XA_BRANCHDBA_2PC_PENDING이 있습니다.

참고

VT_XA_BRANCH는 현재 데이터베이스 서버에서 활동 중인 모든 XA 트랜잭션 브랜치(XA transaction branch)의 정보를 실시간으로 볼 수 있는 테이블입니다.

DBA_2PC_PENDING은 현재 정체되 고 있는 XA 트랜잭션 브랜치의 정보를 보여주는 뷰입니다.

이에 대한 자세한 내용은 각각 "Tibero 참조 안내서"와 "Tibero 관리자 안내서"를 참고합니다.


XA API

TP-Monitor를 이용하여 분산 트랜잭션을 수행하기 위해서는 XA API를 사용해야 합니다. Tibero는 X/Open DTP 규약의 XA를 지원하기 때문에 표준에 맞는 XA 애플리케이션 프로그램을 작성할 수 있습니다.

XA 함수

Tibero에서는 XA를 지원하기 위해 다음과 같은 XA 함수를 제공합니다. 단, 이 함수는 C 프로그래밍 언어용으로만 제공됩니다.

함수
설명

xa_open

리소스 매니저(Resource Manager)에 접속한다.

자세한 설명은 'xa_open 함수의 속성'을 참고한다.

xa_close

리소스 매니저에서 데이터베이스 접속을 해제한다.

xa_start

XID에 새로운 트랜잭션을 시작하거나, 이미 존재하는 트랜잭션에 현재 프로세스를 연결한다.

xa_end

XID에서 현재 프로세스를 분리한다.

xa_rollback

XID의 트랜잭션을 롤백한다.

xa_prepare

XID에 커밋을 준비한다. Two-phase commit의 First Phase이다.

xa_commit

XID에 커밋을 완료한다. Two-phase commit의 Second Phase이다.

xa_recover

prepare 상태인 트랜잭션의 목록을 검사하여 커밋이나 롤백을 수행한다.

xa_forget

XID의 트랜잭션이 이미 처리된 경우 로그 기록을 삭제한다.

xa_open 함수의 속성

다음은 xa_open 함수를 호출하는 데 필요한 속성이다.

속성

필수

설명

user

O

접속할 사용자의 이름이다. (예: user=tibero)

pwd

O

접속할 사용자의 패스워드이다. (예: pwd=1234)

db

X

접속할 데이터베이스의 DSN 이름(tbdsn.tbr 파일 내의 DSN 이름)이다. (예: db=sample)

conn_id

X

XA 연결에 이름을 부여한다. 이 이름을 ESQL의 AT 구문에서 사용할 수 있다. (예: conn_id=db1)

Loose_Coupling

O

다른 브랜치이지만 동일한 글로벌 트랜잭션끼리 같은 리소스를 사용하는 지 여부를 설정한다. (true/false)

loose coupling이면 (1,0)과 (1,2)는 서로 다른 내부 리소스를 사용한다. 예 를 들어 TX가 해당된다. 이와는 반대로 tight coupling이면 두 xtb는 하나의 TX를 서로 잠금 처리를 설정해가며 공유하여 사용한다.

(예: Loose_Coupling=false)

sestm

O

시스템에 의해 중단(abort)되기 전까지의 트랜잭션의 inactive time limit이 다. 클라이언트로부터 한 요청에 대한 답변을 전달한 후 그 다음 요청이 오 기 전까지 대기하는 시간이다. 그 이상의 시간이 지나면 클라이언트 측에서 문제가 있다고 판단하고 해당 xtb를 롤백한다. (예: sestm=10)

seswt

X

XA_RETRY가 반환되기 전까지 데이터베이스 서버가 트랜잭션을 기다리 는 대기 시간이다. flag 내에 TMNOWAIT이 설정되어 있지 않을 경우에는 클라이언트의 요청 을 데이터베이스가 곧바로 처리해줄 수 없을 때 해당 리소스를 사용할 수 있을 때까지 기다린 후에 클라이언트에 답변을 주게 된다. 이때 데이터베이스가 seswt로 설정한 시간까지 답변을 줄 수 없는 경우 XARETRY를 반환한다. (예: seswt=30)

oglogdir

X

XA의 로그를 저장할 디렉터리를 지정할 수 있다. (예: logdir=/home/test/log)

XA 애플리케이션 프로그래밍

일반적으로 C 프로그래밍 언어에서 XA 애플리케이션 프로그램을 작성할 때 ESQL을 이용하여 개발하는 예가 많습니다.

다음은 Tibero에서 제공하는 tbESQL를 이용하여 XA 애플리케이션 프로그램을 작성한 예입니다.

#define XA_CONN_STR_TIGHT "TIBERO_XA:user=tibero, pwd=tmax," \
                                "db=sample, sestm=60, logdir=/home/path/to/xa_log"

/* 동일한 글로벌 트랜잭션에서 다른 브랜치로 xa_start했을 경우
* Tight-Coupling
*/
void test_xa_2branch_2pc_tight()
{
     int rc;
     XID xid1;
     XID xid2;
     struct xa_switch_t *tbxa = &XA_SWITCH_NAME;
     char *conn_str = XA_CONN_STR_TIGHT; /* tightly coupled */
     long gtrid = _GTRID_BASE; 
     long bqual = 1;
     
     EXEC SQL BEGIN DECLARE SECTION;
          int cnt;
     EXEC SQL END DECLARE SECTION;

     /* xa_open */
     tbxa->xa_open_entry (conn_str, 0, TMNOFLAGS); 
     xid1.formatID = 1;
     xid1.gtrid_length = sizeof(gtrid); 
     xid1.bqual_length = sizeof(bqual); 
     memcpy(&xid1.data[0], &gtrid, sizeof(gtrid));
     memcpy(&xid1.data[sizeof(gtrid)], &bqual, sizeof(bqual));
     
     bqual = 2;
     xid2.formatID = 1; 
     xid2.gtrid_length = sizeof(gtrid); 
     xid2.bqual_length = sizeof(bqual);
     memcpy(&xid2.data[0], &gtrid, sizeof(gtrid)); 
     memcpy(&xid2.data[sizeof(gtrid)], &bqual, sizeof(bqual));

     EXEC SQL DELETE FROM PERSON; 
     EXEC SQL COMMIT WORK;

     /* (1, 1) 시작 */
     /* xa_start -- sql statements starts */ 
     tbxa->xa_start_entry (&xid1, 0, TMNOFLAGS);
     EXEC SQL INSERT INTO PERSON VALUES ('1', 'LEE'); 
     EXEC SQL INSERT INTO PERSON VALUES ('2', 'KIM');
     
     /* xa_end -- */
     tbxa->xa_end_entry (&xid1, 0, TMSUCCESS);

     /* (1, 2) 시작 */
     /* xa_start -- sql statements starts */ 
     tbxa->xa_start_entry (&xid2, 0, TMNOFLAGS);
     EXEC SQL INSERT INTO PERSON VALUES ('2', 'PARK'); 
     EXEC SQL INSERT INTO PERSON VALUES ('3', 'JAKE'); 
     EXEC SQL INSERT INTO PERSON VALUES ('4', 'KID'); 
     EXEC SQL INSERT INTO PERSON VALUES ('5', 'CHANHO');

     /* xa_end -- */
     tbxa->xa_end_entry (&xid2, 0, TMSUCCESS);

     /* xa_prepare */
     tbxa->xa_prepare_entry (&xid1, 0, TMNOFLAGS);

     /* Tightly-Coupled 가정 */
     /* xa_prepare */
     tbxa->xa_prepare_entry (&xid2, 0, TMNOFLAGS);

     /* xa_commit */
     tbxa->xa_commit_entry (&xid1, 0, TMNOFLAGS);

     /* xa_commit */
     tbxa->xa_commit_entry (&xid2, 0, TMNOFLAGS); 
     EXEC SQL SELECT COUNT(*) into :cnt FROM PERSON;
     CuAssertIntEq(tc, cnt, 6);

     /* xa_close */
     tbxa->xa_close_entry ("", 0, TMNOFLAGS); 
     return;
}

/* 같은 Global Trasaction의 다른 Branch로 xa_start했을 경우
* Loose-Coupling
*/
void test_xa_2branch_2pc_loose(CuTest *tc)
{
     int rc; 
     XID xid1; 
     XID xid2;
     struct xa_switch_t *tbxa = &XA_SWITCH_NAME; 
     char *conn_str = XA_CONN_STR_LOOSE;
     long gtrid = _GTRID_BASE; 
     long bqual = 1;

     EXEC SQL BEGIN DECLARE SECTION;
     int cnt;
     EXEC SQL END DECLARE SECTION;

     /* xa_open */
     tbxa->xa_open_entry (conn_str, 0, TMNOFLAGS); 
     xid1.formatID = 1;
     xid1.gtrid_length = sizeof(gtrid); 
     xid1.bqual_length = sizeof(bqual); 
     memcpy(&xid1.data[0], &gtrid, sizeof(gtrid));
     memcpy(&xid1.data[sizeof(gtrid)], &bqual, sizeof(bqual));

     bqual = 2;
     xid2.formatID = 1; 
     xid2.gtrid_length = sizeof(gtrid);
     xid2.bqual_length = sizeof(bqual); 
     memcpy(&xid2.data[0], &gtrid, sizeof(gtrid)); 
     memcpy(&xid2.data[sizeof(gtrid)], &bqual, 
     sizeof(bqual));

     EXEC SQL DELETE FROM PERSON; 
     EXEC SQL COMMIT WORK;

     /* (1, 1) 시작 */
     /* xa_start -- sql statements starts */ 
     tbxa->xa_start_entry (&xid1, 0, TMNOFLAGS);
     EXEC SQL INSERT INTO PERSON VALUES ('1', 'LEE'); 
     EXEC SQL INSERT INTO PERSON VALUES ('2', 'KIM');

     /* xa_end -- */
     tbxa->xa_end_entry (&xid1, 0, TMSUCCESS);

     /* (1, 2) 시작 */
     /* xa_start -- sql statements starts */ 
     tbxa->xa_start_entry (&xid2, 0, TMNOFLAGS);
     EXEC SQL INSERT INTO PERSON VALUES ('2', 'PARK'); 
     EXEC SQL INSERT INTO PERSON VALUES ('3', 'JAKE'); 
     EXEC SQL INSERT INTO PERSON VALUES ('4', 'KID'); 
     EXEC SQL INSERT INTO PERSON VALUES ('5', 'CHANHO');

     /* xa_end -- */
     tbxa->xa_end_entry (&xid2, 0, TMSUCCESS);

     /* xa_prepare */
     tbxa->xa_prepare_entry (&xid1, 0, TMNOFLAGS);

     /* Loosely-Coupled 가정 */
     /* xa_prepare */
     tbxa->xa_prepare_entry (&xid2, 0, TMNOFLAGS);

     /* xa_commit */
     tbxa->xa_commit_entry (&xid1, 0, TMNOFLAGS);

     /* xa_commit */
     tbxa->xa_commit_entry (&xid2, 0, TMNOFLAGS); 
     EXEC SQL SELECT COUNT(*) into :cnt FROM PERSON;
     CuAssertIntEq(tc, cnt, 6);

     /* xa_close */
     tbxa->xa_close_entry ("", 0, TMNOFLAGS); 
     return;
}


JDBC에서의 XA 지원

본 절에서는 XA(Extended Architecture)에서 지원하는 인터페이스와 이를 이용하여 작성한 프로그램에 대해 설명합니다.

XA 인터페이스

Tibero에서 지원하는 JDBC의 XA 인터페이스는 다음과 같습니다.

  • XA Datasource Interface

  • XA Connection Interface

  • XA Exception Interface

  • XA XID Interface

다음은 Tibero에서 구현된 XA 인터페이스의 목록입니다.

표준 XA 인터페이스(JDK 1.3)
Tibero의 XA 인터페이스

javax.sql.XADataSource

com.tmax.tibero.jdbc.ext.TbXADataSource

javax.sql.XAConnection

com.tmax.tibero.jdbc.ext.TbXAConnection

javax.transaction.xa.XAException

com.tmax.tibero.jdbc.ext.TbXAException

javax.transaction.xa.Xid

com.tmax.tibero.jdbc.ext.TbXid

XA 인터페이스 프로그래밍

다음은 tbJDBC 환경에서 XA 인터페이스를 이용하여 프로그래밍한 예입니다.

package test.com.tmax.tibero.cases; 
import com.tmax.tibero.jdbc.ext.*;
import test.com.tmax.tibero.AbstractBase; 
import javax.sql.XAConnection;
import javax.transaction.xa.XAResource; 
import java.sql.*;

public class TestXATwoBranch extends 
    AbstractBase{ int formatID = 1;
    int row_count = 0; 
    int pre1 , pre2 = 0;
    
    byte[] gtid1 = new byte[1]; 
    byte[] bq1 = new byte[1]; 
    byte[] gtid2 = new byte[1];
    byte[] bq2 = new byte[1];
    
    public TestXATwoBranch (String name) 
    { super(name);
    }

    /* Tightly Coupled 일 때 */
    public void test_xa_2branch_2pc_tight () throws Exception 
        { debug("test_xa_2branch_2pc_tight " + this._getClassName());

        create_table_for_xa();

        gtid1[0] = (byte)1; bq1[0] = (byte)1;
        gtid2[0] = (byte)1; bq2[0] = (byte)2;

        TbXADataSource xads1 = new TbXADataSource(); 
        xads1.setUrl(getXAurl()); 
        xads1.setUser(getXAuser()); 
        xads1.setPassword(getXApasswd());

        TbXADataSource xads2 = new TbXADataSource(); 
        xads2.setUrl(getXAurl()); 
        xads2.setUser(getXAuser()); 
        xads2.setPassword(getXApasswd());

        XAConnection xacon1 = xads1.getXAConnection(); 
        XAConnection xacon2 = xads2.getXAConnection();

        Connection conn1 = xacon1.getConnection(); 
        Connection conn2 = xacon2.getConnection();

        XAResource xars1 = xacon1.getXAResource(); 
        XAResource xars2 = xacon2.getXAResource();

        /* XID (1.1) 생성 */
        TbXid xid1 = new TbXid(formatID, gtid1,bq1 );

        /* XID (1.2) 생성 */
        TbXid xid2 = new TbXid(formatID, gtid2, bq2); 
        try {
            /* (1.1) 시작 */
            xars1.start(xid1, XAResource.TMNOFLAGS);

            PreparedStatement pstmt1;
            pstmt1 = conn1.prepareStatement("insert into author values (?,?)");
            
            pstmt1.setInt(1, 1);
                pstmt1.setString(2, "FOSCHIA"); 
                pstmt1.executeUpdate();

                pstmt1.setInt(1,2); 
                pstmt1.setString(2, "AGNOS"); 
                pstmt1.executeUpdate();

                /* (1.1) 종료 */
                xars1.end(xid1, XAResource.TMSUCCESS);

                /* (1.2) 시작 */
                xars2.start(xid2, XAResource.TMNOFLAGS); 
                PreparedStatement pstmt2;
                pstmt2 = conn2.prepareStatement("insert into author values (?,?)");

                pstmt2.setInt(1, 3); 
                pstmt2.setString(2, "JELLA"); 
                pstmt2.executeUpdate();

                pstmt2.setInt(1,4); 
                pstmt2.setString(2, "THIPHILO"); 
                pstmt2.executeUpdate();

                /* (1.2) 종료 */
                xars2.end(xid2, XAResource.TMSUCCESS);

                /* (1,1) prepare */
                pre1= xars1.prepare(xid1); 
                assertEquals(pre1, XAResource.XA_RDONLY);

                /* (1,2) prepare */
                pre2 = xars2.prepare(xid2); 
                assertEquals(pre2, XAResource.XA_OK);

                /* (1.1) commit */ 
                try {
                    xars1.commit(xid1, false);
                } catch(TbXAException e) {}

                /* (1.2) commit */ 
                try {
                    xars2.commit(xid2, false);
                } catch(TbXAException e) {}

                Statement stmt1 = conn1.createStatement();
                ResultSet rs1 = stmt1.executeQuery("select * from author"); 
                while (rs1.next())
                row_count ++; 
                
            assertEquals(4, row_count);
            
            rs1.close(); rs1 = null; 
            stmt1.close(); stmt1 = null;

            pstmt1.close(); conn1.close(); xacon1.close(); 
            pstmt2.close(); conn2.close(); xacon2.close();

            pstmt1= null; conn1= null; xacon1=null; 
            pstmt2= null; conn2= null; xacon2=null;
        } catch (TbXAException e) {}
    }

    /* Loosely Coupled 일 때 */
    public void test_xa_2branch_2pc_loose () throws Exception 
        { debug("test_xa_2branch_2pc_loose " + this._getClassName());

        create_table_for_xa();

        gtid1[0] = (byte)1; bq1[0] = (byte)1;
        gtid2[0] = (byte)1; bq2[0] = (byte)2;

        TbXADataSource xads1 = new TbXADataSource();

        xads1.setUrl(getXAurl()); 
        xads1.setUser(getXAuser()); 
        xads1.setPassword(getXApasswd());

        TbXADataSource xads2 = new TbXADataSource();

        xads2.setUrl(getXAurl()); 
        xads2.setUser(getXAuser()); 
        xads2.setPassword(getXApasswd());

        XAConnection xacon1 = xads1.getXAConnection(); 
        XAConnection xacon2 = xads2.getXAConnection();

        Connection conn1 = xacon1.getConnection(); 
        Connection conn2 = xacon2.getConnection();

        XAResource xars1 = xacon1.getXAResource(); 
        XAResource xars2 = xacon2.getXAResource();

        TbXid xid1 = new TbXid(formatID, gtid1,bq1 ); 
        TbXid xid2 = new TbXid(formatID, gtid2, bq2);
            try {
                xars1.start(xid1, TbXAResource.TBRTRANSLOOSE);

                PreparedStatement pstmt1;
                pstmt1 = conn1.prepareStatement("insert into author values (?,?)");

                pstmt1.setInt(1, 1); 
                pstmt1.setString(2, "FOSCHIA"); 
                pstmt1.executeUpdate();

                pstmt1.setInt(1,2); 
                pstmt1.setString(2, "AGNOS"); 
                pstmt1.executeUpdate();

                xars1.end(xid1, XAResource.TMSUCCESS); 
                xars2.start(xid2, TbXAResource.TBRTRANSLOOSE);

                PreparedStatement pstmt2;
                pstmt2 = conn2.prepareStatement("insert into author values (?,?)");

                pstmt2.setInt(1, 3); 
                pstmt2.setString(2, "JELLA"); 
                pstmt2.executeUpdate();

                pstmt2.setInt(1,4); 
                pstmt2.setString(2, "THIPHILO"); 
                pstmt2.executeUpdate();

                xars2.end(xid2, XAResource.TMSUCCESS);

                pre1= xars1.prepare(xid1); 
                assertEquals(pre1, XAResource.XA_OK);

                pre2 = xars2.prepare(xid2); 
                assertEquals(pre2, XAResource.XA_OK);

                xars1.commit(xid1, false); 
                xars2.commit(xid2, false);

                Statement stmt1 = conn1.createStatement();
                ResultSet rs1 = stmt1.executeQuery("select * from author");

                while (rs1.next()) 
                row_count ++;

                assertEquals(4, row_count);
            rs1.close(); rs1 = null; 
            stmt1.close(); stmt1 = null;

            pstmt1.close(); conn1.close(); xacon1.close(); 
            pstmt2.close(); conn2.close(); xacon2.close();

            pstmt1= null; conn1= null; xacon1=null; 
            pstmt2= null; conn2= null; xacon2=null;
        } catch (TbXAException e) {}
    }
}


TP-Monitor와 Tibero 연동

TP-Monitor(Transaction Processing Monitor)는 각종 프로토콜에서 동작하는 세션과 시스템 및 데이터베이스 사이의 최소 처리 단위인 트랜잭션을 감시하여 일관성 있게 보관 및 유지하는 역할을 하는 트랜잭션관리 미들웨어입니다. 본 절에서는 대표적인 상용 TP-Monitor인 Tmax와 Tuxedo를 Tibero와 연동하는 예제를 설명합니다.

Tmax와 Tibero 연동

Tmax는 Transaction Maximization의 약어로 트랜잭션 처리 극대화를 의미합니다. Tmax는 시스템의 분산 환경에서 이기종 컴퓨터 간의 트랜잭션 처리를 완벽히 보장하면서 부하를 분산시키고 에러가 발생하는 경우 적절한 조치를 담당하는 TP-Monitor입니다. 트랜잭션의 특성을 지원하면서 사용자에게는 최적의 개발환경을 제공하고, 클라이언트/서버 환경에서 최적의 솔루션을 제공하며, 성능 개선은 물론 모든 장애에 완벽하게 대처합니다. Tmax는 분산 트랜잭션 프로세싱의 국제 표준인 X/Open DTP(Distributed Transaction Processing) 모델을 준수하고 국제 표준기구인 OSI(Open Systems Interconnection group)의 DTP 서비스에 대한 기능적 분산과 기능 구성 요소 간 API 및 시스템 인터페이스 정의에 따라 개발되었습니다. 또한 분산 환경에서 이기종 간의 투명한 업무 처리 및 OLTP(On-Line Transaction Processing)를 지원하고 트랜잭션 처리의 ACID(Atomic, Consistent, Isolated, Durable: transaction properties) 특성을 만족하게 합니다. 아래에서 소개할 Tmax와 Tibero 연동 예제 프로그램을 테스트해보기 위해서는 Tmax와 Tibero가 정상적으로 설치되어 있어야 합니다.

참고

1. 연동할 Tmax와 Tibero가 다른 머신에 설치된 경우 Tmax가 설치된 머신에서 Tibero클라이언트를 따로 설치하여 Tibero 서버에 정상적으로 접속할 수 있는 환경이 구축되어야 합니다.

2. Tmax 설치 및 관리에 대한 자세한 내용은 "Tmax Installation Guide"나 "Tmax Administration Guide"를 참고합니다. Tibero로 설치 및 관리에 관한 자세한 내용은 "Tibero 설치 안내서"와 "Tibero 관리자 안내서"를 참고합니다.

여기서 소개하는 예제 프로그램은 Tmax를 설치할 때 인스톨러가 기본으로 제공하는 것입니다. 클라이언트가 Tmax 서버를 통하여 Tibero DB를 접속하여 특정 테이블의 데이터를 조회, 추가, 변경, 삭제하는 작업을 합니다.

테스트 환경과 프로그램에 사용된 각종 파일들은 다음과 같습니다.

테스트 환경

구분
설명

운영체제

Ubuntu Linux 2.6.32-24-server x86-64

bash

$TMAXDIR

Tmax 설치 디렉터리

프로그램 파일

파일
설명

sample.m

Tmax 환경설정 파일($TMAXDIR/config)

tms_tbr.mk

Tibero용 TMS makefile($TMAXDIR/sample/server)

tbrtest.tbc

서버 프로그램 tbESQL/C 파일($TMAXDIR/sample/server)

tbrtest.h

서버 프로그램 헤더 파일($TMAXDIR/sample/server)

Makefile.tbr

서버 프로그램 makefile($TMAXDIR/sample/server)

compile

서버 프로그램 빌드 스크립트($TMAXDIR/sample/server)

tbr_main.c

클라이언트 프로그램 파일($TMAXDIR/sample/client)

Makefile.c

클라이언트 프로그램 makefile($TMAXDIR/sample/client)

compile

클라이언트 프로그램 빌드 스크립트($TMAXDIR/sample/client)

다음에 제시된 순서대로 따르면 Tmax와 Tibero가 연동하는 것을 확인할 수 있습니다.

1. Tmax 기본 환경설정

2. TMS 컴파일

3. TMS 컴파일

4. 서버 프로그램 컴파일

5. 클라이언트 프로그램 컴파일

6. DB 테이블 생성

7. 예제 프로그램 실행

Tmax 기본 환경설정

다음은 Tmax 기본 환경을 설정하는 방법입니다.

  • Tmax 시스템 환경 파일 설정

$TMAXDIR/config 디렉터리에 있는 sample.m은 Tmax를 기동시킬 때 필요한 각종 정보들이 들어있는 Tmax 시스템 환경 파일입니다. ASCII 파일 형태로 작성하며, cfl 유틸리티로 컴파일하여 이진 파일을 생성합니다. 생성된 이진 파일은 Tmax 기동 및 종료할 때 참조됩니다. Tibero 서버와 연동하는 서비스를 활성화시키기 위해 sample.m 환경 파일의 SVRGROUP 절, SERVER 절, SERVICE 절 항목을 아래와 같이 수정합니다.

### tms for Tibero ###
svg4                NODENAME = "integrity", DBNAME = TIBERO,
                    OPENINFO = "TIBERO_XA:user=tibero,pwd=tmax,sestm=60,db=tibero", 
                    TMSNAME = tms_tbr
                    
### server for Tibero sample program ### 
tbrtest	            SVGNAME = svg4

### services for tbrtest ### 
                    TBRINS	SVRNAME = tbrtest
                    TBRSEL	SVRNAME = tbrtest
                    TBRUPT	SVRNAME = tbrtest
                    TBRDEL	SVRNAME = tbrtest

SVRGROUP 절의 NODENAME은 Tmax를 설치할 때에 hostname 값으로 자동 설정됩니다. DB 벤더를 구분하기 위한 DBNAME은 TIBERO로 설정되어 있습니다. OPENINFO는 XA 모드 설정을 위한 TIBERO_XA가 앞쪽에 쓰여 있고 그 뒤에 'xa_open 함수의 속성'이 나열되어 있습니다. TMSNAME에는 XA를 담당할 모듈 이름이 설정되어 있습니다.

SERVER 절에는 예제 서버 프로그램의 이름인 tbrtest가 설정되고, 서버 프로그램이 제공하는 서비스 4가지가 설정되어 있습니다.

참고

Tmax 시스템 환경 파일 설정에 대한 자세한 내용은 "Tmax Administration Guide"를 참고합니다.

  • Tmax 시스템 환경 파일 컴파일

수정한 sample.m 파일을 아래와 같은 명령으로 컴파일합니다.

cfl -i sample.m

성공적으로 컴파일 된 후에는 다음과 같은 메시지가 출력됩니다.

CFL is done successfully for node(<nodename>)

  • 서비스 테이블 생성

서비스 테이블은 각각의 Tmax 시스템 내 서버 프로세스가 생성될 때 필요한 파일로서 각각의 프로세스들이 어떤 서비스를 처리하는지에 대한 정보가 담겨 있습니다. 아래와 같은 명령으로 서비스 테이블을 생성합니다.

gst

성공적으로 처리되면 다음과 같은 메시지가 출력됩니다.

SVC tables are successfully generated GST is successfully done

  • 구조체 정의 이진 파일 및 필드 키 정의 이진 파일 생성

서버나 클라이언트 프로그램내에서 구조체나 필드 키를 쓰는 경우 이와 관련된 이진 파일을 생성해 주어야 합니다. 하지만 이 예제 프로그램에서는 구조체나 필드 키를 사용하지 않으므로 그 과정을 생략합니다.

TMS 컴파일

TMS(Transaction Management Server)는 Tmax 시스템의 구성요소로서 데이터베이스 관리 및 분산 트랜잭션 처리를 담당하는 프로세스입니다. Tibero용 TMS를 컴파일하기 전에 Tibero 관련 환경변수 TB_HOME, TB_SID, LD_LIBRARY_PATH, PATH 등이 제대로 설정되었는지 확인합니다.

확인한 다음 아래와 같이 $TMAXDIR/sample/server 디렉터리로 이동하여 Tibero용 TMS makefile을 이용하여 TMS를 컴파일합니다.

cd $TMAXDIR/sample/server
make –f tms_tbr.mk all

서버 프로그램 컴파일

$TMAXDIR/sample/server 디렉터리로 이동하여 실제적으로 서비스를 제공하는 서버 프로그램을 빌드 스크립트를 이용하여 컴파일합니다.

cd $TMAXDIR/sample/server
./compile tbc tbrtest

클라이언트 프로그램 컴파일

$TMAXDIR/sample/client 디렉터리로 이동하여 서비스를 요청하는 클라이언트 프로그램을 빌드 스크립트를 이용하여 컴파일합니다.

cd $TMAXDIR/sample/client
./compile c tbr_main

DB 테이블 생성

Tibero 서버에 tibero/tmax 계정으로 접속하여 아래와 같은 emp 테이블을 생성합니다.

tbsqltibero/tmax

create table emp 
    (    empno number,    
    ename char(16),    
    job char(16),    
    hiredate char(16),    
    sal number
);

예제 프로그램 실행

  • Tmax 시스템 기동 다음과 명령으로 Tmax를 기동합니다.

tmboot

성공적으로 기동되면 다음과 같은 메시지가 출력됩니다.

TMBOOT for node(<nodename>) is starting:
Welcome to Tmax demo system: it will expire 2012/3/11 
Today: 2012/1/13
    TMBOOT: TMM is starting: Fri Jan 13 14:18:31 2012
    TMBOOT: CLL is starting: Fri Jan 13 14:18:31 2012
    TMBOOT: CLH is starting: Fri Jan 13 14:18:31 2012
(I) CLH9991 Current Tmax Configuration: Number of client handler(MINCLH) = 1 
        Supported maximum user per node = 680
        Supported maximum user per handler = 680 [CLH0125] 
    TMBOOT: TLM(tlm) is starting: Fri Jan 13 14:18:31 2012
    TMBOOT: TMS(tms_tbr) is starting: Fri Jan 13 14:18:31 2012
(I) TMS0211 General Infomation : transaction recovery will be started [TMS0221]
(I) TMS0211 General Infomation : transaction recovery was completed [TMS0222] 
    TMBOOT: TMS(tms_tbr) is starting: Fri Jan 13 14:18:31 2012
    TMBOOT: SVR(tbrtest) is starting: Fri Jan 13 14:18:31 2012

  • 클라이언트 프로그램 실행 클라이언트 프로그램의 명령 옵션은 다음과 같습니다.

Usage: ./tbr_main empno loop_cnt ins_flag upt_flag del_flag 
flag : 1|0

원하는 옵션을 선택하여 수행시키면 아래와 같은 결과가 출력됩니다.

./tbr_main 12 3 1 0 0

LOOP COUNT = 1
>> INSERT : COMMIT TEST
[./tbr_main] [[TBRINS] emp Insert Success] 
[./tbr_main] [[TBRSEL] emp Select Success [1]]

LOOP COUNT = 2
>> INSERT : COMMIT TEST
[./tbr_main] [[TBRINS] emp Insert Success] 
[./tbr_main] [[TBRSEL] emp Select Success [2]]

LOOP COUNT = 3
>> INSERT : COMMIT TEST
[./tbr_main] [[TBRINS] emp Insert Success] 
[./tbr_main] [[TBRSEL] emp Select Success [3]]

TuxedoTibero 연동

Tuxedo는 분산 트랜잭션 처리를 위한 플랫폼으로서, C, C++ 및 COBOL로 작성된 소프트웨어를 위한 개방형의 분산 시스템을 토대로 Mainframe 확장성 및 성능을 제공합니다. 또한 메인스트림 하드웨어를 토대로 메인프레임 애플리케이션을 '리호스팅' 할 수 있는 플랫폼입니다.

Tuxedo는 비용 효과적인 신뢰성과 초당 수십만 건의 트랜잭션을 지원할 수 있는 탁월한 확장성을 제공하는 것은 물론, SOA와 같은 혁신적 아키텍처의 일부로서 기존 IT 자산의 수명을 연장함으로써 투자 보호의 이점을 제공합니다. Oracle Tuxedo는 Oracle Fusion Middleware의 전략적 트랜잭션 처리 제품입니다.

아래에서 소개할 Tuxedo와 Tibero 연동 예제 프로그램을 테스트하기 위해서는 Tuxedo와 Tibero가 정상적으로 설치되어 있어야 합니다.

참고

  1. 연동할 Tmax와 Tibero가 다른 머신에 설치된 경우 Tmax가 설치된 머신에서 Tibero 클라이언트를 따로 설치하여 Tibero 서버에 정상적으로 접속할 수 있는 환경이 구축되어야 합니다.

  2. Tuxedo 설치 및 관리에 대한 자세한 내용은 "Tuxedo Documenation"을 참고합니다. Tibero로 설치 및 관리에 관한 자세한 내용은 "Tibero 설치 안내서"와 "Tibero 관리자 안내서"를 참고합니다.

여기서 소개하는 예제 프로그램은 클라이언트가 Tuxedo 서버를 통하여 Tibero DB를 접속하여 특정 테이블의 데이터를 조회, 추가하는 작업을 합니다. 테스트 환경과 프로그램에 사용된 각종 파일들은 다음과 같습니다.

편의상 테스트 서버는 tux_machine이라는 호스트 네임을 가지고 있으며, Tibero와 Tuxedo는 각각 path/to/tibero와 /path/to/tuxedo에 설치되어 있다고 가정합니다. 그리고 예제 프로그램 파일들이 작업 디렉터리 /path/to/example에 있다고 가정합니다.

테스트 환경과 프로그램에 사용된 각종 파일들은 다음과 같습니다.

테스트 환경

구분
설명

운영체제

AIX

호스트 네임

tux_machine

bash

Tibero 설치 홈 디렉터리

/path/to/tibero

Tuxedo 설치 홈 디렉터리

/path/to/tuxedo

예제 프로그램 홈 디렉터리

/path/to/example

프로그램 파일

파일
설명

tb_tux.env

시스템 환경변수 설정 파일

tb_tux.conf.m

Tuxedo 환경설정 파일

tmax32.fld

필드 테이블 파일

trans_fml32.tbc

서버 프로그램 tbESQL/C 파일

builds.sh

서버 프로그램 빌드 스크립트

insert.c

INSERT 클라이언트 프로그램 파일

select.c

SELECT 클라이언트 프로그램 파일

buildc.sh

클라이언트 프로그램 빌드 스크립트

create_table.sql

테스트를 위한 DB 테이블 생성 파일

run.sh

Tuxedo 시스템 기동 스크립트

참고

본 절에서 설명하고 있는 기본 프로그램의 전체 소스 코드는 'Appendix B. Tibero와 Tuxedo 연동 예제'를 참고합니다.

다음에 제시된 순서대로 따르면 Tuxedo와 Tibero가 연동하는 것을 확인할 수 있습니다. 운영체제나 시스템 환경에 따라서 예제 파일을 수정하여 테스트해야 합니다.

  1. 시스템 환경변수 설정

  2. Tuxedo 기본 환경설정

  3. TMS 컴파일

  4. 서버 프로그램 컴파일

  5. 클라이언트 프로그램 컴파일

  6. DB 테이블 생성

  7. 예제 프로그램 실행

시스템 환경변수 설정

Tibero와 Tuxedo 연동 테스트를 하기 위해 필요한 시스템 환경변수 설정은 아래와 같습니다.

  • Tibero를 위한 기본 환경변수 설정

export TB_HOME=/path/to/tibero 
export TB_SID=tibero
export PATH=$TB_HOME/bin:$TB_HOME/client/bin:$TB_HOME/scripts:$PATH 
export LD_LIBRARY_PATH=$TB_HOME/client/lib:$TB_HOME/lib:$LD_LIBRARY_PATH 
export LIBPATH=$TB_HOME/client/lib:$TB_HOME/lib:$LIBPATH

  • Tuxedo를 위한 기본 환경변수 설정

export TUXDIR=/path/to/tuxedo 
export JAVA_HOME=$TUXDIR/jre
export JVMLIBS=$JAVA_HOME/lib/amd64/server:$JAVA_HOME/jre/bin 
export PATH=$TUXDIR/bin:$JAVA_HOME/bin:$PATH
export COBCPY=:$TUXDIR/cobinclude; export COBCPY
export COBOPT="-C ANS85 -C ALIGN=8 -C NOIBMCOMP -C TRUNC=ANSI -C OSEXT=cbl"
export SHLIB_PATH=$TUXDIR/lib:$JVMLIBS:$SHLIB_PATH 
export LIBPATH=$TUXDIR/lib:$JVMLIBS:$LIBPATH
export LD_LIBRARY_PATH=$TUXDIR/lib:$JVMLIBS:$LD_LIBRARY_PATH
export WEBJAVADIR=$TUXDIR/udataobj/webgui/java

  • Tuxedo를 위한 추가 환경변수 설정 연동 테스트를 위해서 추가 설정해주어야 할 환경변수들로서 상황에 맞게 설정합니다.

export TUXCONFIG=/path/to/tuxedo/tuxconf 
export FLDTBLDIR32=/path/to/tuxedo 
export FIELDTBLS32=tmax32.fld
export TLOGDEVICE=/path/to/tuxedo/TLOG 
export ULOGPFX=/path/to/tuxedo/ULOG

Tuxedo 기본 환경설정

Tuxedo 기본 환경설정은 아래와 같습니다.

  • Tuxedo 시스템 환경 파일 설정

tb_tux.conf.m은 Tuxedo를 기동시킬 때 필요한 각종 정보들이 들어있는 Tuxedo 시스템 환경파일 입니다. ASCII 파일 형태로 작성하며, tmloadcf 유틸리티로 컴파일하여 이진 파일을 생성합니다. 생성된 이진 파일은 Tuxedo 기동 및 종료할 때 참조됩니다.

Tibero 서버와 연동하는 서비스를 활성화시키기 위하여 tb_tux.conf.m 환경 파일의 RESOURCES 절, MACHINES 절, GROUPS 절, SERVERS 절, SERVICES 절 항목을 아래와 같이 설정합니다.

*RESOURCES 
IPCKEY             68300
DOMAINID           tbrdomain 
MASTER             tbrtest
MAXACCESSERS       10
MAXSERVERS         5
MAXSERVICES        10
MODEL              SHM 
LDBAL              N

*MACHINES
DEFAULT:
                   TUXDIR="path/to/tuxedo" 
                   APPDIR="path/to/example"
                   TUXCONFIG="path/to/example/tuxconf"
                   
tux_machine        LMID=tbrtest            

*GROUPS
TBXA               LMID=tbrtest GRPNO=1 
                       TMSNAME=tms_tibero
                       OPENINFO="TIBERO_XA:TIBERO_XA:user=sys,pwd=tibero,
                                 sestm=60,db=tibero"
                                 
*SERVERS
DEFAULT:
                   CLOPT="-A -r"
                   
trans_fml32        SRVGRP=TBXA SRVID=1

*SERVICES 
SELECT_FML32 
INSERT_FML32
~

RESOURCES 절과 MACHINE 절은 일반적인 Tuxedo 시스템 환경 파일 설정처럼 합니다. MACHINE 절의 tux_machine은 서버의 호스트 네임이므로 테스트 환경에 따라 다르게 설정해야 합니다. LMID와 MASTER는 임의로 tbrtest라고 정하였고, DOMAINID는 임의로 tbrdomain이라고 정하였으로 원하는 대로 설정합니다.

GROUPS 절도 일반적인 Tuxedo 시스템 환경 파일의 설정과 동일하게 합니다. TMSNAME에는 Tibero 서 버와 XA 통신을 담당할 모듈의 이름을 설정합니다. OPENINFO는 XA 모드 설정을 위한 'TIBERO_XA:TIBERO_XA:'를 앞쪽에 쓰고 그 뒤에 'xa_open 함수의 속성'이 나열되어야 합니다.

SERVERS 절에는 예제 서버 프로그램인 trans_fml32가 설정되어 있습니다. SERVICE 절에는 예제 서버 프로그램이 제공하는 서비스인 SELECT_FML32, INSERT_FML32가 설정되어 있습니다.

참고

Tuxedo 시스템 환경 파일 설정에 대한 자세한 설명은 "Tuxedo Documentation"를 참고합니다.

  • Tuxedo 시스템 환경 파일 컴파일 Tuxedo 시스템 환경 파일을 아래와 같은 명령으로 컴파일합니다.

tmloadcf -y tb_tux.conf.m

  • 필드 테이블 파일을 헤더 파일로 변환 서버나 클라언트 프로그램간에 데이터를 주고받을 때 공용 자료 구조체 정의해서 씁니다. 이번 연동 테스트에서는 FML(Field Manipulation Language)을 이용하여 아래 같은 필드 테이블 파일tmax32.fld을 새로 정의해서 씁니다.

#name	number	type	flag	comment
OUTPUT	302	string	0	-
EMPNO	901	long	0	-
ENAME	902	string	0	-
JOB	903	string	0	-
MGR	904	long	0	-
SAL	905	float	0	-
COMM	906	float	0	-
DEPTNO	907	long	0	-

이 필드 테이블 파일 tmax32.fld로부터 헤더 파일을 생성하는 방법은 다음과 같습니다.

mkfldhdr32 tmax32.fld

이 결과 tmax32.fld.h이라는 헤더 파일이 생성되며, 예제 클라이언트 프로그램과 서버 프로그램에서 가져다 씁니다.그 밖의 다른 구조체를 정의하여 파일을 만드는 방법은 "Tuxedo Documentation"을 참고합니다.

TMS 컴파일

다음 아래와 같은 명령어를 이용하여 Tibero용 TMS를 컴파일합니다.

buildtms -o $TUXDIR/bin/tms_tibero -v -r TIBERO_XA

서버 프로그램 컴파일

실제적으로 서비스를 제공하는 서버 프로그램을 아래와 같은 빌드 스크립트 builds.sh를 이용하여 컴파일합니다.

#### transaction server precompile #### 
PRECOMP=$TB_HOME/client/bin/tbpc 
PRECOMPFLAGS="UNSAFE_NULL=YES"
LIB=$TB_HOME/client/lib 
INC=$TB_HOME/client/include
CFLAGS="-ltbcli -ltbxa -lm -lrt -lpthread -ltbertl -g "
CC=gcc
rm -f trans_fml32.c
$PRECOMP INCLUDE=$TUXDIR/include UNSAFE_NULL=YES INCLUDE=$INC
$PRECOMPFLAGS ONAME=trans_fml32.c trans_fml32.tbc

#### transaction server build ####
buildserver -o trans_fml32 -v -f trans_fml32.c -s INSERT_FML32, 
SELECT_FML32 -r TIBERO_XA

참고

프리컴파일 옵션에 대한 자세한 설명은 "Tibero tbESQL/C 안내서"를 참고합니다.

클라이언트 프로그램 컴파일

insert와 select 서비스를 요청하는 클라이언트 프로그램을 아래와 같은 빌드 스크립트 buildc.sh를 이용하여 컴파일합니다.

buildclient -o insert -v -f insert.c 
buildclient -o select -v -f select.c

DB 테이블 생성

Tibero서버에 tibero/tmax 계정으로 접속하여 아래와 같은 emp 테이블을 생성합니다.

tbsqltibero/tmax

drop table emp;
CREATE TABLE emp (
    empno         NUMBER,
    ename         VARCHAR2(10),
    job           VARCHAR2(9),
    mgr           NUMBER(4), 
    hiredate      DATE, 
    sal           NUMBER(7,2),
    comm          NUMBER(7,2),
    deptno        NUMBER(2)
);

예제 프로그램 실행

  • Tuxedo 시스템 기동 다음 명령으로 기동합니다.

tmboot -y

성공적으로 기동되면 다음과 같은 메시지가 출력됩니다.

Booting all admin and server processes in /path/to/example/tuxconf 
INFO: Oracle Tuxedo, Version 10.3.0.0, 64-bit, Patch Level (none)

Booting admin processes ...

exec BBL -A :
        process id=3457104 ... Started.
        
Booting server processes ... 

exec tms_tibero -A :
        process id=4046910 ... Started. 
exec tms_tibero -A :
        process id=9265576 ... Started. 
exec tms_tibero -A :
        process id=1863802 ... Started. 
exec trans_fml32 -A -r :
        process id=3719284 ... Started.
5 processes started.

  • 클라이언트 프로그램 실행 emp 테이블에 Empyee 정보를 추가하고 조회하는 클라이언트 프로그램은 다음과 같이 실행합니다.

./insert
******************************************
| Employee Number : 1
| Employee Name : Kim
| Employee Job : Manager
******************************************

insert 프로그램을 실행시키고 Employee Number, Employ Name, Employ Job를 위와 같이 입력하면 Tuxedo 서버 프로그램을 통하여 Tibero 서버의 emp 테이블에 레코드가 추가됩니다.

./select
******************************************
| Employee Number : 1
******************************************

EMPNO: 1 
ENAME: Kim
JOB: Manager

select 프로그램을 실행시키고 Employee Number를 위와 같이 입력하면 Tuxedo서버 프로그램을 통하여 Tibero 서버의 emp 테이블로부터 해당 레코드 정보를 가져와서 출력합니다.

Last updated