물론 sqlj라는게 있긴 하지만..저는 sqlj는 얼마전에 설명을 한번 했습니다.
그러나..sqlj보다는 pro*C가 수행능력이 훨씬 뛰어나고 더 많이 사용하고
있습니다.
그래서 우리는 pro*C를 하는 것입니다. 그런데 자바 프로젝트를 할 때
어떻게 pro*C 를 사용할 수 있을까 하는 의문이 생깁니다.
즉, 대량의 데이타 처리를 위해 우리는 그냥 java + JDBC, sqlj, pro*C 중
어느 것을 택할것인가에 대한 선택의 문제에 봉착하게 되는데..
우리는 그중에 pro*C를 선택한다는 것입니다.
java + JDBC, sqlj는 당연히 별 문제 없이 그냥 사용할 수 있는 단순한
문제 이므로 우리는 가장 사용하기 힘들지만 가장 많이 사용하는
pro*C를 사용하자는 것인데...자바와 C를 어케 인터페이스 하느냐?
그것이 바로 JNI 입니다. 즉, 바로 아래 게시물에 있는 java + C 인 것입니다.
그것을 좀더 제가 발전시켜 java (JNI) + pro*C를 구현해 보았습니다.
1. 환경 설정은 아래와 같다.
- jdk 는 오라클 인스톨시 사용한 것을 바로 사용함.
- vc 는 Visual Studio 6.0을 이용함.
- Oracle은 9i 사용
set JAVA_HOME=c:\oracle\ora92\jdk
set JDK_CLASSES=%JAVA_HOME%\lib\tools.jar;%JAVA_HOME%\lib\dt.jar
set CLASSPATH=%JDK_CLASSES%;.;
SET PATH=%PATh%;%JAVA_HOME%\bin;%JAVA_HOME%;
2. 작업 디렉토리는 c:\tmp\test 인데 Visual Studio(vc++)를 띄운 후
File메뉴-> New->Projects->Win32 Dynamic-Link Library 선택
project name을 test27으로 입력함 --> OK -->창이 바뀌면서 What kind of dll would like create? 에
An empty Dll project 선택 --> 확인
: 탐색기로 확인해 보라. C"\TMP\TEST\TEST27 디렉토리에 몇 개의 작업 파일이 생성되 있을 것이다.
3. 자바 파일(BackupBatch.java)을 생성한다.
public class BackupBatch {
static
{
System.out.println("=== library was loaded");
System.load("C:\\tmp\\test\\test27\\Debug\\test27.dll");
}
public native void startBackup();
public static void main(String[] args) throws Exception
{
new BackupBatch().startBackup();
}
}
4. javac BackupBatch.java
--> BackupBatch.class 생성
5. javah BackupBatch
--> BackupBatch.h 생성
6. vc에서
Project메뉴->Add to Project --> Files 하여 BackupBatch.h를 추가한다.
7. Pro* C파일을 생성한다.
/* MyBatchProc.pc */
/* 아래와 같은 두개의 테이블이 있는데 sales에서 stat로 데이터를 batch로 복사하는 프로그램
으로 구현함. sales는 10개 이상의 Data를 입력한 상태에서 테스트
SQL> desc sales;
이름 널? 유형
----------------------------------------- -------- ----------------
NO NOT NULL NUMBER
SALE_CODE NOT NULL NUMBER
AMOUNT NUMBER
SALE_DATE DATE
SALE_MAN VARCHAR2(200)
SQL> desc stat;
이름 널? 유형
----------------------------------------- -------- ----------------
NO NOT NULL NUMBER
SALE_CODE NOT NULL NUMBER
SALE_DATE VARCHAR2(20)
AMOUNT NUMBER
*/
#include <stdio.h>
#include <stdlib.h>
#include <sqlca.h>
#define ARR_LENGTH 10
char *username = "SCOTT";
char *password = "TIGER";
/* Declare a host structure tag. */
EXEC SQL INCLUDE sqlca;
struct {
int NO[ARR_LENGTH];
int SALE_CODE[ARR_LENGTH];
int AMOUNT[ARR_LENGTH];
VARCHAR SALE_DATE[ARR_LENGTH][20];
VARCHAR SALE_MAN[ARR_LENGTH][200];
} sale_rec;
EXEC SQL BEGIN DECLARE SECTION;
EXEC SQL END DECLARE SECTION;
void print_rows(int n);
void sql_error(char *msg);
int main()
{
int num_ret; /* number of rows returned */
int i;
/* Connect to ORACLE. */
EXEC SQL WHENEVER SQLERROR DO sql_error("Connect error:");
EXEC SQL CONNECT :username IDENTIFIED BY :password;
printf("\nConnected to ORACLE as user: %s\n", username);
EXEC SQL WHENEVER SQLERROR DO sql_error("Oracle error:");
/* Declare a cursor for the FETCH. */
EXEC SQL DECLARE c1 CURSOR FOR
SELECT no, sale_code,amount,to_char(sale_date,'yyyy-mm-dd'),sale_man
FROM sales
where to_char(sale_date,'yyyymmdd') like '200303%' ;
EXEC SQL OPEN c1;
//위 쿼리에서 to_char(sale_date,'yyyymmdd') like '200303%'는 엄청
//문제있는 부분입니다. 실제 쿼리 튜닝시 반드시 수정해 주어야 합니다만...
//여기서의 문제에서는 제외하도록 합니다. (문제의 핵심은 튜닝을 얘기하는것이 아니라서)
//튜닝시 해결하는 방법은 임시테이블을 만들고 그 임시테이블에다
//insert 하는데 2003-03-01 ~ 2003-03-31까지를 insert한 후
//where sale_date in(select tmp_date from tmp_tbl)
//이런식으로 쿼리하도록 합니다. 음하하 쉽지요?
num_ret = 0;
/* Array fetch loop - ends when NOT FOUND becomes true. */
EXEC SQL WHENEVER NOT FOUND DO break;
for (;;)
{
printf("----------------------------------- 10개 단위로 fetch\n");
//만약 레코드가 100만건 정도 된다면 시스템에 따라 다르지만
// 보통 5000건 단위로 fetch 하는것이 대체로 유리함
//여기서는 10건 단위의 fetch
EXEC SQL FETCH c1 INTO :sale_rec;
/* Print however many rows were returned. */
print_rows(sqlca.sqlerrd[2] - num_ret);
num_ret = sqlca.sqlerrd[2]; /* Reset the number. */
for(i = 0; i < num_ret; i++)
sale_rec.SALE_DATE[i].arr[sale_rec.SALE_DATE[i].len] = 0x00;
EXEC SQL
INSERT INTO stat(no,sale_code,sale_date,amount)
VALUES ( :sale_rec.NO, :sale_rec.SALE_CODE, :sale_rec.SALE_DATE, :sale_rec.AMOUNT);
EXEC SQL COMMIT;
printf("================= insert ok");
}
/* Print remaining rows from last fetch, if any. */
if ((sqlca.sqlerrd[2] - num_ret) > 0)
print_rows(sqlca.sqlerrd[2] - num_ret);
EXEC SQL CLOSE c1;
printf("OK insert.\n\n\n");
/* Disconnect from the database. */
exit(0);
}
void sql_error(char *msg)
{
EXEC SQL WHENEVER SQLERROR CONTINUE;
printf("\n%s", msg);
printf("\n% .70s \n", sqlca.sqlerrm.sqlerrmc);
EXEC SQL ROLLBACK WORK RELEASE;
exit(1);
}
void print_rows(int n)
{
int i;
printf("\nNumber sale_code sale_date");
printf("\n------ -------- -------\n");
for (i = 0; i < n; i++)
{
printf("%d %d %s\n", sale_rec.NO[i], sale_rec.SALE_CODE[i], sale_rec.SALE_DATE[i].arr);
}
}
8. vc에서
위에서 생성한 MyBatchProc.pc를 Project메뉴->Add to Project --> Files 하여 추가한다.
화면왼쪽의 FileView Tab에서 pc파일을 선택 후 마우스 오른쪽 클릭--> setting 선택
Custom Build 탭의 Command 부분에
proc parse=full iname="C:\tmp\test\test27\MyBatchProc.pc" include="c:\Program Files\Microsoft Visual Studio\vc98\include"
Outputs 부분에
oname="c:\tmp\test\test27\MyBatchProc.c"
라고 입력한후 ok를 클릭하고
다시 위에서 생성한 MyBatchProc.pc를 화면왼쪽의 FileView Tab에서 선택 후 마우스 오른쪽 클릭--> compile한다.
9. MyBatchProc.c를 더블클릭하여 Open한다.
아래가 proc를 precompile하여 생성한 c 파일로서 아래와 같이 편집한다.
/* 전체적으로 변경 부분은 3 부분이다.
1. header 추가
2. main 함수를 jni 함수로 변경
3. 원래 main함수의 exit(0)를 return으로 변경
*/
//////////////////////////////////////////////////////////////////////////////////////////////////
/* 이 부분은 c파일에 추가된 부분으로 jni를 사용하기 위해
신규로 추가해야 한다 */
#include <jni.h>
#include "BackupBatch.h"
#include <stdio.h>
//////////////////////////////////////////////////////////////////////////////////////////////////
/* Result Sets Interface */
#ifndef SQL_CRSR
# define SQL_CRSR
struct sql_cursor
{
unsigned int curocn;
void *ptr1;
void *ptr2;
unsigned int magic;
};
typedef struct sql_cursor sql_cursor;
typedef struct sql_cursor SQL_CURSOR;
#endif /* SQL_CRSR */
/* Thread Safety */
typedef void * sql_context;
typedef void * SQL_CONTEXT;
/* Object support */
struct sqltvn
{
unsigned char *tvnvsn;
unsigned short tvnvsnl;
unsigned char *tvnnm;
unsigned short tvnnml;
unsigned char *tvnsnm;
unsigned short tvnsnml;
};
typedef struct sqltvn sqltvn;
struct sqladts
{
unsigned int adtvsn;
unsigned short adtmode;
unsigned short adtnum;
sqltvn adttvn[1];
};
typedef struct sqladts sqladts;
static struct sqladts sqladt = {
1,1,0,
};
/* Binding to PL/SQL Records */
struct sqltdss
{
unsigned int tdsvsn;
unsigned short tdsnum;
unsigned char *tdsval[1];
};
typedef struct sqltdss sqltdss;
static struct sqltdss sqltds =
{
1,
0,
};
/* File name & Package Name */
struct sqlcxp
{
unsigned short fillen;
char filnam[34];
};
static const struct sqlcxp sqlfpn =
{
33,
"c:\\tmp\\test\\test27\\MyBatchProc.pc"
};
static unsigned int sqlctx = 2133591581;
static struct sqlexd {
unsigned int sqlvsn;
unsigned int arrsiz;
unsigned int iters;
unsigned int offset;
unsigned short selerr;
unsigned short sqlety;
unsigned int occurs;
const short *cud;
unsigned char *sqlest;
const char *stmt;
sqladts *sqladtp;
sqltdss *sqltdsp;
void **sqphsv;
unsigned int *sqphsl;
int *sqphss;
void **sqpind;
int *sqpins;
unsigned int *sqparm;
unsigned int **sqparc;
unsigned short *sqpadto;
unsigned short *sqptdso;
unsigned int sqlcmax;
unsigned int sqlcmin;
unsigned int sqlcincr;
unsigned int sqlctimeout;
unsigned int sqlcnowait;
int sqfoff;
unsigned int sqcmod;
unsigned int sqfmod;
void *sqhstv[5];
unsigned int sqhstl[5];
int sqhsts[5];
void *sqindv[5];
int sqinds[5];
unsigned int sqharm[5];
unsigned int *sqharc[5];
unsigned short sqadto[5];
unsigned short sqtdso[5];
} sqlstm = {12,5};
/* SQLLIB Prototypes */
extern void sqlcxt (void **, unsigned int *,
struct sqlexd *, const struct sqlcxp *);
extern void sqlcx2t(void **, unsigned int *,
struct sqlexd *, const struct sqlcxp *);
extern void sqlbuft(void **, char *);
extern void sqlgs2t(void **, char *);
extern void sqlorat(void **, unsigned int *, void *);
/* Forms Interface */
static const int IAPSUCC = 0;
static const int IAPFAIL = 1403;
static const int IAPFTL = 535;
extern void sqliem(char *, int *);
static const char *sq0002 =
"select no ,sale_code ,amount ,to_char(sale_date,'yyyy-mm-dd') ,sale_man fr\
om sales where to_char(sale_date,'yyyymmdd') like '200303%' ";
typedef struct { unsigned short len; unsigned char arr[1]; } VARCHAR;
typedef struct { unsigned short len; unsigned char arr[1]; } varchar;
/* cud (compilation unit data) array */
static const short sqlcud0[] =
{12,4130,846,0,0,
5,0,0,1,0,0,27,44,0,0,4,4,0,1,0,1,97,0,0,1,97,0,0,1,10,0,0,1,10,0,0,
36,0,0,2,145,0,9,56,0,0,0,0,0,1,0,
51,0,0,2,0,0,13,78,0,0,5,0,0,1,0,2,3,0,0,2,3,0,0,2,3,0,0,2,9,0,0,2,9,0,0,
86,0,0,3,73,0,3,86,0,0,4,4,0,1,0,1,3,0,0,1,3,0,0,1,9,0,0,1,3,0,0,
117,0,0,4,0,0,29,90,0,0,0,0,0,1,0,
132,0,0,2,0,0,15,100,0,0,0,0,0,1,0,
147,0,0,5,0,0,32,111,0,0,0,0,0,1,0,
};
#include <stdio.h>
#include <stdlib.h>
#include <sqlca.h>
#define ARR_LENGTH 10
char *username = "SCOTT";
char *password = "TIGER";
/* Declare a host structure tag. */
/* EXEC SQL INCLUDE sqlca;
*/
/*
* $Header: sqlca.h,v 1.3 1994/12/12 19:27:27 jbasu Exp $ sqlca.h
*/
/* Copyright (c) 1985,1986, 1998 by Oracle Corporation. */
/*
NAME
SQLCA : SQL Communications Area.
FUNCTION
Contains no code. Oracle fills in the SQLCA with status info
during the execution of a SQL stmt.
NOTES
**************************************************************
*** ***
*** This file is SOSD. Porters must change the data types ***
*** appropriately on their platform. See notes/pcport.doc ***
*** for more information. ***
*** ***
**************************************************************
If the symbol SQLCA_STORAGE_CLASS is defined, then the SQLCA
will be defined to have this storage class. For example:
#define SQLCA_STORAGE_CLASS extern
will define the SQLCA as an extern.
If the symbol SQLCA_INIT is defined, then the SQLCA will be
statically initialized. Although this is not necessary in order
to use the SQLCA, it is a good pgming practice not to have
unitialized variables. However, some C compilers/OS's don't
allow automatic variables to be init'd in this manner. Therefore, if you are INCLUDE'ing the SQLCA in a place where it would be
an automatic AND your C compiler/OS doesn't allow this style
of initialization, then SQLCA_INIT should be left undefined --
all others can define SQLCA_INIT if they wish.
If the symbol SQLCA_NONE is defined, then the SQLCA variable will
not be defined at all. The symbol SQLCA_NONE should not be defined
in source modules that have embedded SQL. However, source modules
that have no embedded SQL, but need to manipulate a sqlca struct
passed in as a parameter, can set the SQLCA_NONE symbol to avoid
creation of an extraneous sqlca variable.
MODIFIED
lvbcheng 07/31/98 - long to int
jbasu 12/12/94 - Bug 217878: note this is an SOSD file
losborne 08/11/92 - No sqlca var if SQLCA_NONE macro set
Clare 12/06/84 - Ch SQLCA to not be an extern.
Clare 10/21/85 - Add initialization.
Bradbury 01/05/86 - Only initialize when SQLCA_INIT set
Clare 06/12/86 - Add SQLCA_STORAGE_CLASS option.
*/
#ifndef SQLCA
#define SQLCA 1
struct sqlca
{
/* ub1 */ char sqlcaid[8];
/* b4 */ int sqlabc;
/* b4 */ int sqlcode;
struct
{
/* ub2 */ unsigned short sqlerrml;
/* ub1 */ char sqlerrmc[70];
} sqlerrm;
/* ub1 */ char sqlerrp[8];
/* b4 */ int sqlerrd[6];
/* ub1 */ char sqlwarn[8];
/* ub1 */ char sqlext[8];
};
#ifndef SQLCA_NONE
#ifdef SQLCA_STORAGE_CLASS
SQLCA_STORAGE_CLASS struct sqlca sqlca
#else
struct sqlca sqlca
#endif
#ifdef SQLCA_INIT
= {
{'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' '},
sizeof(struct sqlca),
0,
{ 0, {0}},
{'N', 'O', 'T', ' ', 'S', 'E', 'T', ' '},
{0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0}
}
#endif
;
#endif
#endif
/* end SQLCA */
/* */
/*
* $Header: sqlca.h,v 1.3 1994/12/12 19:27:27 jbasu Exp $ sqlca.h
*/
/* Copyright (c) 1985,1986, 1998 by Oracle Corporation. */
/*
NAME
SQLCA : SQL Communications Area.
FUNCTION
Contains no code. Oracle fills in the SQLCA with status info
during the execution of a SQL stmt.
NOTES
**************************************************************
*** ***
*** This file is SOSD. Porters must change the data types ***
*** appropriately on their platform. See notes/pcport.doc ***
*** for more information. ***
*** ***
**************************************************************
If the symbol SQLCA_STORAGE_CLASS is defined, then the SQLCA
will be defined to have this storage class. For example:
#define SQLCA_STORAGE_CLASS extern
will define the SQLCA as an extern.
If the symbol SQLCA_INIT is defined, then the SQLCA will be
statically initialized. Although this is not necessary in order
to use the SQLCA, it is a good pgming practice not to have
unitialized variables. However, some C compilers/OS's don't
allow automatic variables to be init'd in this manner. Therefore,
if you are INCLUDE'ing the SQLCA in a place where it would be
an automatic AND your C compiler/OS doesn't allow this style
of initialization, then SQLCA_INIT should be left undefined --
all others can define SQLCA_INIT if they wish.
If the symbol SQLCA_NONE is defined, then the SQLCA variable will
not be defined at all. The symbol SQLCA_NONE should not be defined
in source modules that have embedded SQL. However, source modules
that have no embedded SQL, but need to manipulate a sqlca struct
passed in as a parameter, can set the SQLCA_NONE symbol to avoid
creation of an extraneous sqlca variable.
MODIFIED
lvbcheng 07/31/98 - long to int
jbasu 12/12/94 - Bug 217878: note this is an SOSD file
losborne 08/11/92 - No sqlca var if SQLCA_NONE macro set
Clare 12/06/84 - Ch SQLCA to not be an extern.
Clare 10/21/85 - Add initialization.
Bradbury 01/05/86 - Only initialize when SQLCA_INIT set
Clare 06/12/86 - Add SQLCA_STORAGE_CLASS option.
*/
#ifndef SQLCA
#define SQLCA 1
struct sqlca
{
/* ub1 */ char sqlcaid[8];
/* b4 */ int sqlabc;
/* b4 */ int sqlcode;
struct
{
/* ub2 */ unsigned short sqlerrml;
/* ub1 */ char sqlerrmc[70];
} sqlerrm;
/* ub1 */ char sqlerrp[8];
/* b4 */ int sqlerrd[6];
/* ub1 */ char sqlwarn[8];
/* ub1 */ char sqlext[8];
};
#ifndef SQLCA_NONE
#ifdef SQLCA_STORAGE_CLASS
SQLCA_STORAGE_CLASS struct sqlca sqlca
#else
struct sqlca sqlca
#endif
#ifdef SQLCA_INIT
= {
{'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' '},
sizeof(struct sqlca),
0,
{ 0, {0}},
{'N', 'O', 'T', ' ', 'S', 'E', 'T', ' '},
{0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0}
}
#endif
;
#endif
#endif
/* end SQLCA */
struct
{
int NO[ARR_LENGTH];
int SALE_CODE[ARR_LENGTH];
int AMOUNT[ARR_LENGTH];
/* VARCHAR SALE_DATE[ARR_LENGTH][20]; */
struct { unsigned short len; unsigned char arr[22]; } SALE_DATE[10];
/* VARCHAR SALE_MAN[ARR_LENGTH][200]; */
struct { unsigned short len; unsigned char arr[202]; } SALE_MAN[10];
} sale_rec;
/* EXEC SQL BEGIN DECLARE SECTION; */
/* EXEC SQL END DECLARE SECTION; */ void print_rows(int n);
void sql_error(char *msg);
//////////////////////////////////////////////////////////////////////////////////////////////////////////
/* 이 부분이 핵심이다.
원래 이 부분은 int main(){ 이렇게 되어 있는 c 파일의 메인 함수
부분이였다. 그러나, JNI를 사용하기 위한 메소드 부분으로 교체해 주어야
한다. 즉,int main(){ 대신에 아래의 한 라인으로만 대체하면 끝난다.
JNIEXPORT void JNICALL Java_BackupBatch_startBackup(JNIEnv *env, jobject obj) {
///////////////////////////////////////////////////////////////////////////////////////////////////////////
int num_ret; /* number of rows returned */
int i;
/* Connect to ORACLE. */
/* EXEC SQL WHENEVER SQLERROR DO sql_error("Connect error:"); */
/* EXEC SQL CONNECT :username IDENTIFIED BY :password; */
{
struct sqlexd sqlstm;
sqlstm.sqlvsn = 12;
sqlstm.arrsiz = 4;
sqlstm.sqladtp = &sqladt;
sqlstm.sqltdsp = &sqltds;
sqlstm.iters = (unsigned int )10;
sqlstm.offset = (unsigned int )5;
sqlstm.cud = sqlcud0;
sqlstm.sqlest = (unsigned char *)&sqlca;
sqlstm.sqlety = (unsigned short)256;
sqlstm.occurs = (unsigned int )0;
sqlstm.sqhstv[0] = ( void *)username;
sqlstm.sqhstl[0] = (unsigned int )0;
sqlstm.sqhsts[0] = ( int )0;
sqlstm.sqindv[0] = ( void *)0;
sqlstm.sqinds[0] = ( int )0;
sqlstm.sqharm[0] = (unsigned int )0;
sqlstm.sqadto[0] = (unsigned short )0;
sqlstm.sqtdso[0] = (unsigned short )0;
sqlstm.sqhstv[1] = ( void *)password;
sqlstm.sqhstl[1] = (unsigned int )0;
sqlstm.sqhsts[1] = ( int )0;
sqlstm.sqindv[1] = ( void *)0;
sqlstm.sqinds[1] = ( int )0;
sqlstm.sqharm[1] = (unsigned int )0;
sqlstm.sqadto[1] = (unsigned short )0;
sqlstm.sqtdso[1] = (unsigned short )0;
sqlstm.sqphsv = sqlstm.sqhstv;
sqlstm.sqphsl = sqlstm.sqhstl;
sqlstm.sqphss = sqlstm.sqhsts;
sqlstm.sqpind = sqlstm.sqindv;
sqlstm.sqpins = sqlstm.sqinds;
sqlstm.sqparm = sqlstm.sqharm;
sqlstm.sqparc = sqlstm.sqharc;
sqlstm.sqpadto = sqlstm.sqadto;
sqlstm.sqptdso = sqlstm.sqtdso;
sqlstm.sqlcmax = (unsigned int )100;
sqlstm.sqlcmin = (unsigned int )2;
sqlstm.sqlcincr = (unsigned int )1;
sqlstm.sqlctimeout = (unsigned int )0;
sqlstm.sqlcnowait = (unsigned int )0;
sqlcxt((void **)0, &sqlctx, &sqlstm, &sqlfpn);
if (sqlca.sqlcode < 0) sql_error("Connect error:");
}
printf("\nConnected to ORACLE as user: %s\n", username);
/* EXEC SQL WHENEVER SQLERROR DO sql_error("Oracle error:"); */ /* Declare a cursor for the FETCH. */
/* EXEC SQL DECLARE c1 CURSOR FOR
SELECT no, sale_code,amount,to_char(sale_date,'yyyy-mm-dd'),sale_man
FROM sales
where to_char(sale_date,'yyyymmdd') like '200303%' ; */
/* EXEC SQL OPEN c1; */
{
struct sqlexd sqlstm;
sqlstm.sqlvsn = 12;
sqlstm.arrsiz = 4;
sqlstm.sqladtp = &sqladt;
sqlstm.sqltdsp = &sqltds;
sqlstm.stmt = sq0002;
sqlstm.iters = (unsigned int )1;
sqlstm.offset = (unsigned int )36;
sqlstm.selerr = (unsigned short)1;
sqlstm.cud = sqlcud0;
sqlstm.sqlest = (unsigned char *)&sqlca;
sqlstm.sqlety = (unsigned short)256;
sqlstm.occurs = (unsigned int )0;
sqlstm.sqcmod = (unsigned int )0;
sqlcxt((void **)0, &sqlctx, &sqlstm, &sqlfpn);
if (sqlca.sqlcode < 0) sql_error("Oracle error:");
}
//위 쿼리에서 to_char(sale_date,'yyyymmdd') like '200303%'는 엄청
//문제있는 부분입니다. 실제 쿼리 튜닝시 반드시 수정해 주어야 합니다만...
//여기서의 문제에서는 제외하도록 합니다. (문제의 핵심은 튜닝을 얘기하는것이 아니라서)
//튜닝시 해결하는 방법은 임시테이블을 만들고 그 임시테이블에다
//insert 하는데 2003-03-01 ~ 2003-03-31까지를 insert한 후
//where sale_date in(select tmp_date from tmp_tbl)
//이런식으로 쿼리하도록 합니다. 음하하 쉽지요?
num_ret = 0;
/* Array fetch loop - ends when NOT FOUND becomes true. */
/* EXEC SQL WHENEVER NOT FOUND DO break; */
for (;;)
{
printf("----------------------------------- 10개 단위로 fetch\n");
//만약 레코드가 100만건 정도 된다면 시스템에 따라 다르지만
// 보통 5000건 단위로 fetch 하는것이 대체로 유리함
//여기서는 10건 단위의 fetch
/* EXEC SQL FETCH c1 INTO :sale_rec; */
{
struct sqlexd sqlstm;
sqlstm.sqlvsn = 12;
sqlstm.arrsiz = 5;
sqlstm.sqladtp = &sqladt;
sqlstm.sqltdsp = &sqltds;
sqlstm.iters = (unsigned int )10;
sqlstm.offset = (unsigned int )51;
sqlstm.selerr = (unsigned short)1;
sqlstm.cud = sqlcud0;
sqlstm.sqlest = (unsigned char *)&sqlca;
sqlstm.sqlety = (unsigned short)256;
sqlstm.occurs = (unsigned int )0;
sqlstm.sqfoff = ( int )0;
sqlstm.sqfmod = (unsigned int )2;
sqlstm.sqhstv[0] = ( void *)sale_rec.NO;
sqlstm.sqhstl[0] = (unsigned int )sizeof(int);
sqlstm.sqhsts[0] = ( int )sizeof(int);
sqlstm.sqindv[0] = ( void *)0;
sqlstm.sqinds[0] = ( int )0;
sqlstm.sqharm[0] = (unsigned int )0;
sqlstm.sqharc[0] = (unsigned int *)0;
sqlstm.sqadto[0] = (unsigned short )0;
sqlstm.sqtdso[0] = (unsigned short )0;
sqlstm.sqhstv[1] = ( void *)sale_rec.SALE_CODE;
sqlstm.sqhstl[1] = (unsigned int )sizeof(int);
sqlstm.sqhsts[1] = ( int )sizeof(int);
sqlstm.sqindv[1] = ( void *)0;
sqlstm.sqinds[1] = ( int )0;
sqlstm.sqharm[1] = (unsigned int )0;
sqlstm.sqharc[1] = (unsigned int *)0;
sqlstm.sqadto[1] = (unsigned short )0;
sqlstm.sqtdso[1] = (unsigned short )0;
sqlstm.sqhstv[2] = ( void *)sale_rec.AMOUNT;
sqlstm.sqhstl[2] = (unsigned int )sizeof(int);
sqlstm.sqhsts[2] = ( int )sizeof(int);
sqlstm.sqindv[2] = ( void *)0;
sqlstm.sqinds[2] = ( int )0;
sqlstm.sqharm[2] = (unsigned int )0;
sqlstm.sqharc[2] = (unsigned int *)0;
sqlstm.sqadto[2] = (unsigned short )0;
sqlstm.sqtdso[2] = (unsigned short )0;
sqlstm.sqhstv[3] = ( void *)sale_rec.SALE_DATE;
sqlstm.sqhstl[3] = (unsigned int )22;
sqlstm.sqhsts[3] = ( int )24;
sqlstm.sqindv[3] = ( void *)0;
sqlstm.sqinds[3] = ( int )0;
sqlstm.sqharm[3] = (unsigned int )0;
sqlstm.sqharc[3] = (unsigned int *)0;
sqlstm.sqadto[3] = (unsigned short )0;
sqlstm.sqtdso[3] = (unsigned short )0;
sqlstm.sqhstv[4] = ( void *)sale_rec.SALE_MAN;
sqlstm.sqhstl[4] = (unsigned int )202;
sqlstm.sqhsts[4] = ( int )204;
sqlstm.sqindv[4] = ( void *)0;
sqlstm.sqinds[4] = ( int )0;
sqlstm.sqharm[4] = (unsigned int )0;
sqlstm.sqharc[4] = (unsigned int *)0;
sqlstm.sqadto[4] = (unsigned short )0;
sqlstm.sqtdso[4] = (unsigned short )0;
sqlstm.sqphsv = sqlstm.sqhstv;
sqlstm.sqphsl = sqlstm.sqhstl;
sqlstm.sqphss = sqlstm.sqhsts;
sqlstm.sqpind = sqlstm.sqindv;
sqlstm.sqpins = sqlstm.sqinds;
sqlstm.sqparm = sqlstm.sqharm;
sqlstm.sqparc = sqlstm.sqharc;
sqlstm.sqpadto = sqlstm.sqadto;
sqlstm.sqptdso = sqlstm.sqtdso;
sqlcxt((void **)0, &sqlctx, &sqlstm, &sqlfpn);
if (sqlca.sqlcode == 1403) break;
if (sqlca.sqlcode < 0) sql_error("Oracle error:");
}
/* Print however many rows were returned. */
print_rows(sqlca.sqlerrd[2] - num_ret);
num_ret = sqlca.sqlerrd[2]; /* Reset the number. */
for(i = 0; i < num_ret; i++)
sale_rec.SALE_DATE[i].arr[sale_rec.SALE_DATE[i].len] = 0x00;
/* EXEC SQL
INSERT INTO stat(no,sale_code,sale_date,amount)
VALUES ( :sale_rec.NO, :sale_rec.SALE_CODE, :sale_rec.SALE_DATE, :sale_rec.AMOUNT); */
{
struct sqlexd sqlstm;
sqlstm.sqlvsn = 12;
sqlstm.arrsiz = 5;
sqlstm.sqladtp = &sqladt;
sqlstm.sqltdsp = &sqltds;
sqlstm.stmt = "insert into stat (no,sale_code,sale_date,amount) va\
lues (:b0,:b1,:b2,:b3)";
sqlstm.iters = (unsigned int )10;
sqlstm.offset = (unsigned int )86;
sqlstm.cud = sqlcud0;
sqlstm.sqlest = (unsigned char *)&sqlca;
sqlstm.sqlety = (unsigned short)256;
sqlstm.occurs = (unsigned int )0;
sqlstm.sqhstv[0] = ( void *)(sale_rec.NO);
sqlstm.sqhstl[0] = (unsigned int )sizeof(int);
sqlstm.sqhsts[0] = ( int )sizeof(int);
sqlstm.sqindv[0] = ( void *)0;
sqlstm.sqinds[0] = ( int )0;
sqlstm.sqharm[0] = (unsigned int )0;
sqlstm.sqharc[0] = (unsigned int *)0;
sqlstm.sqadto[0] = (unsigned short )0;
sqlstm.sqtdso[0] = (unsigned short )0;
sqlstm.sqhstv[1] = ( void *)(sale_rec.SALE_CODE);
sqlstm.sqhstl[1] = (unsigned int )sizeof(int);
sqlstm.sqhsts[1] = ( int )sizeof(int);
sqlstm.sqindv[1] = ( void *)0;
sqlstm.sqinds[1] = ( int )0;
sqlstm.sqharm[1] = (unsigned int )0;
sqlstm.sqharc[1] = (unsigned int *)0;
sqlstm.sqadto[1] = (unsigned short )0;
sqlstm.sqtdso[1] = (unsigned short )0;
sqlstm.sqhstv[2] = ( void *)(sale_rec.SALE_DATE);
sqlstm.sqhstl[2] = (unsigned int )22;
sqlstm.sqhsts[2] = ( int )24;
sqlstm.sqindv[2] = ( void *)0;
sqlstm.sqinds[2] = ( int )0;
sqlstm.sqharm[2] = (unsigned int )0;
sqlstm.sqharc[2] = (unsigned int *)0;
sqlstm.sqadto[2] = (unsigned short )0;
sqlstm.sqtdso[2] = (unsigned short )0;
sqlstm.sqhstv[3] = ( void *)(sale_rec.AMOUNT);
sqlstm.sqhstl[3] = (unsigned int )sizeof(int);
sqlstm.sqhsts[3] = ( int )sizeof(int);
sqlstm.sqindv[3] = ( void *)0;
sqlstm.sqinds[3] = ( int )0;
sqlstm.sqharm[3] = (unsigned int )0;
sqlstm.sqharc[3] = (unsigned int *)0;
sqlstm.sqadto[3] = (unsigned short )0;
sqlstm.sqtdso[3] = (unsigned short )0;
sqlstm.sqphsv = sqlstm.sqhstv;
sqlstm.sqphsl = sqlstm.sqhstl;
sqlstm.sqphss = sqlstm.sqhsts;
sqlstm.sqpind = sqlstm.sqindv;
sqlstm.sqpins = sqlstm.sqinds;
sqlstm.sqparm = sqlstm.sqharm;
sqlstm.sqparc = sqlstm.sqharc;
sqlstm.sqpadto = sqlstm.sqadto;
sqlstm.sqptdso = sqlstm.sqtdso;
sqlcxt((void **)0, &sqlctx, &sqlstm, &sqlfpn);
if (sqlca.sqlcode == 1403) break;
if (sqlca.sqlcode < 0) sql_error("Oracle error:");
}
/* EXEC SQL COMMIT; */
{
struct sqlexd sqlstm;
sqlstm.sqlvsn = 12;
sqlstm.arrsiz = 5;
sqlstm.sqladtp = &sqladt;
sqlstm.sqltdsp = &sqltds;
sqlstm.iters = (unsigned int )1;
sqlstm.offset = (unsigned int )117;
sqlstm.cud = sqlcud0;
sqlstm.sqlest = (unsigned char *)&sqlca;
sqlstm.sqlety = (unsigned short)256;
sqlstm.occurs = (unsigned int )0;
sqlcxt((void **)0, &sqlctx, &sqlstm, &sqlfpn);
if (sqlca.sqlcode < 0) sql_error("Oracle error:");
}
printf("================= insert ok");
}
/* Print remaining rows from last fetch, if any. */
if ((sqlca.sqlerrd[2] - num_ret) > 0)
print_rows(sqlca.sqlerrd[2] - num_ret);
/* EXEC SQL CLOSE c1; */
{
struct sqlexd sqlstm;
sqlstm.sqlvsn = 12;
sqlstm.arrsiz = 5;
sqlstm.sqladtp = &sqladt;
sqlstm.sqltdsp = &sqltds;
sqlstm.iters = (unsigned int )1;
sqlstm.offset = (unsigned int )132;
sqlstm.cud = sqlcud0;
sqlstm.sqlest = (unsigned char *)&sqlca;
sqlstm.sqlety = (unsigned short)256;
sqlstm.occurs = (unsigned int )0;
sqlcxt((void **)0, &sqlctx, &sqlstm, &sqlfpn);
if (sqlca.sqlcode < 0) sql_error("Oracle error:");
} printf("OK insert.\n\n\n");
/* Disconnect from the database. */
////////////////////////////////////////////////////////////////////////////////////
/* 원래 proc로부터 생성된 C파일에서는 exit(0)로 되어 있지만
여기서는 return으로 변경한다. */
return;
////////////////////////////////////////////////////////////////////////////////////
}
void sql_error(char *msg)
{
/* EXEC SQL WHENEVER SQLERROR CONTINUE; */
printf("\n%s", msg);
printf("\n% .70s \n", sqlca.sqlerrm.sqlerrmc);
/* EXEC SQL ROLLBACK WORK RELEASE; */
{
struct sqlexd sqlstm;
sqlstm.sqlvsn = 12;
sqlstm.arrsiz = 5;
sqlstm.sqladtp = &sqladt;
sqlstm.sqltdsp = &sqltds;
sqlstm.iters = (unsigned int )1;
sqlstm.offset = (unsigned int )147;
sqlstm.cud = sqlcud0;
sqlstm.sqlest = (unsigned char *)&sqlca;
sqlstm.sqlety = (unsigned short)256;
sqlstm.occurs = (unsigned int )0;
sqlcxt((void **)0, &sqlctx, &sqlstm, &sqlfpn);
} exit(1);
}
void print_rows(int n)
{
int i;
printf("\nNumber sale_code sale_date");
printf("\n------ -------- -------\n");
for (i = 0; i < n; i++)
{
printf("%d %d %s\n", sale_rec.NO[i],
sale_rec.SALE_CODE[i],
sale_rec.SALE_DATE[i].arr);
}
}
10. 이때 반드시 File View 탭에서 MyBatchProc.pc를 선택 --> 마우스 오른쪽 클릭 --> setting 클릭하여 output 설정 부분을 삭제하도록 한다. 즉, proc파일이 precompile되면서 c 파일이 Rebuild 되어 위에서 수정한 c 파일 날아가지 않도록 조치한다.
11. Tools메뉴 -> Option -> Directory탭에서 C:\ORACLE\ORA92\JDK\include 추가하고, C:\ORACLE\ORA92\JDK\include\win32 추가한다. 만약 jdk를 별도로 설치한 것을 사용한다면 해당 디렉토리를 추가하도록 한다.
12. File View 탭에서 Project를 선택(여기서는 test27 files라고 표시된 부분임) 한 후 --> 마우스 오른쪽 클릭 -->setting 클릭 --> 모달 창이 하나 나타나면 왼쪽 부분의 settings for 부분에 'all configurations'을 선택하고 오른쪽 탭은 Link를 선택하여 Object/Library modules 부분에 orasql9.lib(oracle 9i인 경우 임)를 추가적으로 입력후 ok 클릭
13. File View 탭에서 MyBatchProc.c를 선택 후 --> 마우스 오른쪽 클릭 --> compile
14. Build메뉴--> build test27.dll을 선택하여 test27.dll을 생성한다.
--> 탐색기로 c:\tmp\test\test27\Debug에 test27.dll이 생성되었는지 확인하고
--> 도스창에서 java HelloWorld를 실행한다.
C:\tmp\test>cd test27
C:\tmp\test\test27>dir
C 드라이브의 볼륨에는 이름이 없습니다.
볼륨 일련 번호: 2749-1605
C:\tmp\test\test27 디렉터리
2003.04.09 오후 07:31 <DIR> .
2003.04.09 오후 07:31 <DIR> ..
2003.04.09 오후 07:31 535 test27.dsw
2003.04.09 오후 09:49 50,176 test27.ncb
2003.04.09 오후 07:31 <DIR> Debug
2003.04.09 오후 07:32 402 BackupBatch.java
2003.04.09 오후 07:32 654 BackupBatch.class
2003.04.09 오후 07:32 406 BackupBatch.h
2003.04.09 오후 07:33 302 BackupBatch.c.BAK
2003.04.09 오후 07:34 302 BackupBatch.c
2003.04.09 오후 07:41 3,720 MyBatchProc.pc.BAK
2003.04.09 오후 07:46 3,663 MyBatchProc.pc
2003.04.09 오후 09:48 828 test27.plg
2003.04.09 오후 07:53 24,626 MyBatchProc.c.BAK
2003.04.09 오후 07:53 25,028 MyBatchProc.c
2003.04.09 오후 09:49 4,608 test27.dsp
2003.04.09 오후 09:49 48,640 test27.opt
14개 파일 163,890 바이트
3개 디렉터리 15,563,145,216 바이트 남음
C:\tmp\test\test27>cd Debug
C:\tmp\test\test27\Debug>dir
C 드라이브의 볼륨에는 이름이 없습니다.
볼륨 일련 번호: 2749-1605
C:\tmp\test\test27\Debug 디렉터리
2003.04.09 오후 07:31 <DIR> .
2003.04.09 오후 07:31 <DIR> ..
2003.04.09 오후 11:52 33,792 vc60.idb
2003.04.09 오후 07:53 61,440 vc60.pdb
2003.04.09 오후 07:53 14,205 MyBatchProc.obj
2003.04.09 오후 09:48 533,504 test27.pdb
2003.04.09 오후 07:53 218,964 test27.pch
2003.04.09 오후 09:48 2,026 test27.lib
2003.04.09 오후 09:48 619 test27.exp
2003.04.09 오후 09:48 263,364 test27.ilk
2003.04.09 오후 09:48 208,948 test27.dll
9개 파일 1,336,862 바이트
2개 디렉터리 15,563,128,832 바이트 남음
C:\tmp\test\test27\Debug>cd ..
C:\tmp\test\test27>java BackupBatch
=== library was loaded
Connected to ORACLE as user: SCOTT
----------------------------------- 10개 단위로 fetch
Number sale_code sale_date
------ -------- -------
1 1 2003-03-31
1 2 2003-03-31
1 3 2003-03-31
2 1 2003-03-31
2 2 2003-03-31
2 3 2003-03-31
2 4 2003-03-31
3 1 2003-03-31
3 2 2003-03-31
4 1 2003-03-31
================= insert ok----------------------------------- 10개 단위로 fetch
Number sale_code sale_date
------ -------- -------
5 1 2003-03-31
5 2 2003-03-31
OK insert.
RECENT COMMENT